2023.03.03 Java 복습

18. 데이터 I/O

18.1 I/O 스트림

데이터는 키보드나 파일 또는 프로그램에서 입력할 수 있습니다.


반대로 데이터를 모니터에 표시하거나 파일에 저장하거나 다른 프로그램으로 전송할 수 있습니다.


이들을 집합적으로 데이터 입력 및 출력이라고 합니다.

Java는 입력 스트림과 출력 스트림을 통해 데이터를 입력하고 출력합니다.


스트림은 데이터의 단방향 흐름입니다.

프로그램 기반에서 들어오는 데이터는 입력 스트림이 되고 나가는 데이터는 출력 스트림이 됩니다.


프로그램이 다른 프로그램과 데이터를 교환하기 위해서는 양쪽 모두 입력 스트림과 출력 스트림이 필요합니다.

스트림은 데이터가 입력되고 출력되는 방식에 따라 두 가지 유형으로 분류됩니다.


Byte Stream: 이미지, 멀티미디어, 텍스트 등 모든 형태의 데이터를 입출력하는데 사용합니다.


문자 스트림: 문자 입력 및 출력에만 사용됩니다.

Java는 java.io 패키지에서 데이터 입력 및 출력을 위한 라이브러리를 제공합니다.

분류 | 바이트 스트림 | 텍스트 스트림
| 입력 전류 | 출력 전류 | 입력 전류 | 출력 스트림
최상위 | 입력 스트림 | 출력 스트림 | 독자 | 작가
아래 | XXXInputStream | XXXOutputStream | XXX독자 | XXX작가
(파일 입력 스트림)

바이트 입력/출력 스트림의 최상위 클래스는 InputStream 및 OutputStream입니다.


이 클래스를 상속받은 하위 클래스는 위의 클래스를 접미사로 가집니다.


이미지와 같은 바이너리 파일에는 FileInputStream과 FileOutputStream이 있습니다.

18.2바이트 출력 스트림

OutputStream은 바이트 출력 스트림의 최상위 클래스이며 추상 클래스입니다.


모든 바이트 출력 스트림은 이 OutputStream 클래스를 상속하여 생성됩니다.


FileOutputStream/PrintStream/BufferedOutputStream/DataOutputStream이 있습니다.

OutputStream 클래스는 모든 바이트 출력 스트림이 기본적으로 가져야 하는 메서드를 정의합니다.


쓰기(int b) | 1바이트 출력
쓰기 (바이트() b) | 매개변수 b로 지정된 배열의 모든 바이트를 인쇄합니다.


write(byte() b, int 꺼짐, int len) | 매개변수로 주어진 배열 b(off)에서 len 바이트를 출력합니다.


플러시() | 출력 버퍼에 남아 있는 모든 바이트 출력
닫기() | 출력 스트림을 닫고 사용된 메모리를 해제합니다.

18.2.1 바이트 출력 스트림

public class WriteExample {
    public static void main(String() args) {
        try {
            OutputStream os = new FileOutputStream("C:Temp/test1.db");

            byte a = 10;
            byte b = 20;
            byte c = 30;

            os.write(a);
            os.write(b);
            os.write(c);

            os.flush();
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

OutputStream에는 작은 버퍼가 포함되어 있습니다.


write() 메서드를 호출하면 버퍼에 바이트를 먼저 저장하고 버퍼가 가득 차면 바이트를 순서대로 출력한다.


flush() 메소드는 내부 버퍼에 남아있는 모든 바이트를 덤프하고 버퍼를 플러시하는 역할을 합니다.


내부 버퍼를 사용하는 이유는 출력 성능을 향상시키기 위함입니다.


출력 스트림이 더 이상 사용되지 않으면 close() 메서드를 호출하여 출력 스트림에서 사용하는 메모리를 해제하는 것이 좋습니다.

18.2.2 바이트 배열 출력

일반적으로 바이트 출력은 드물다.

대부분의 경우 전체 바이트 배열이 반환됩니다.


write(byte() b) 메서드는 매개변수로 주어진 배열의 모든 바이트를 출력합니다.

public class WriteExample {
    public static void main(String() args) {
        try {
            OutputStream os = new FileOutputStream("C:/Temp/test2.db");

            byte() array = { 10, 20, 30 };

            os.write(array);
            os.flush();
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

배열의 일부만 인쇄하려면 write(byte() b, int off, int len) 메서드를 사용할 수 있습니다.


이 메서드는 b(off)의 len 바이트를 출력합니다.

public class WriteExample {
    public static void main(String() args) {
        try {
            OutputStream os = new FileOutputStream("C:/Temp/test3.db");

            byte() array = { 10, 20, 30, 40, 50 };

            os.write(array, 1, 3);
            os.flush();
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

18.3바이트 입력 스트림

InputStream은 바이트 입력 스트림의 슈퍼클래스이며 추상 클래스입니다.


모든 바이트 입력 스트림은 InputStream에서 상속받아 생성됩니다.


FileInputStream/BufferedInputStream/DataInputStream

읽기() | 1바이트를 읽은 후 읽은 바이트를 반환합니다.


읽기(바이트() b) | 매개변수로 지정된 배열에 읽은 바이트를 저장한 후 읽은 바이트 수를 반환합니다.


닫기() | 입력 스트림을 닫고 사용 중인 메모리를 해제합니다.

18.3.1 1바이트 읽기

read() 메서드는 입력 스트림에서 1바이트를 읽어 int(4byre) 형식으로 반환합니다.


따라서 반환된 4바이트 중 마지막 1바이트에만 데이터가 포함됩니다.


예를 들어, 5바이트가 수신되면 각 1바이트를 5번 읽어야 합니다.


더 이상 읽을 수 없는 경우 read() 메서드는 -1을 반환합니다.

public class ReadExample {
    public static void main(String() args) throws Exception {
        InputStream is = new FileInputStream("C:Temp/test1.db");

        while (true) {
            int data = is.read();
            if(data == -1) break;
            System.out.println(data);
        }

        is.close();
    }
}

18.3.2 바이트 배열로 읽기

read(byte() b) 메서드는 입력 스트림에서 지정된 배열의 길이만큼 바이트를 읽어 배열에 저장하고 읽은 바이트 수를 반환합니다.


예를 들어 입력 스트림이 5바이트인 경우 3의 배열로 두 번 읽을 수 있습니다.

더 이상 읽을 수 없으면 -1이 반환됩니다.

많은 양의 바이트를 읽을 때 이 방법을 사용하는 것이 좋습니다.


입력 스트림에서 100바이트가 들어오면 read() 메서드는 이를 100번 읽어야 하지만
read(byte() b) 메서드는 한 번의 읽기에서 어레이 길이만큼 읽어서 읽기 횟수를 줄인다.

public class ReadExample {
    public static void main(String() args) throws Exception {
        InputStream is = new FileInputStream("C:/Temp/test2.db");

        byte() data = new byte(100);

        while (true) {
            int num = is.read(data); //최대 100바이트르 읽고 읽은바이트는 data저장 읽은 수는 리턴
            if ( num == -1) {
                break;
            }
            for (int i = 0; i < num; i++) {
                System.out.println(data(i));
            }
        }
    }
}

읽은 바이트 수가 3이고 데이터 배열에 2개의 공백이 있는 경우
첫 번째 배열 읽기 1 2 -> 출력 1 2 -> 읽기 3 및 2는 여전히 2 -> 그러나 읽은 숫자가 1이므로 30,000이 출력됩니다.

파일 복사의 예는 읽기 -> 쓰기 및 반복입니다.

public class CopyeExmaple {
    public static void main(String() args) throws Exception {
        String orginalFileName = "C:/Temp/test.jpg";
        String targetFileName = "C:/Temp/test2.jpg";

        InputStream is = new FileInputStream(orginalFileName);
        OutputStream os = new FileOutputStream(targetFileName);

        byte() data = new byte(1024); //1024바이트 == 1키로바이튼

        while (true) {
            int num = is.read(data);
            if(num ==  -1) {
                break;
            }
            //마지막에 만약 2개만들어오면 2까지만읽기
            os.write(data, 0 , num); 
        }
        os.flush();
        is.close();
        os.close();

        System.out.println("복사 완료");
    }
}

Java 9부터 입력 스트림에서 출력 스트림으로 보다 편리하게 복사할 수 있도록 TransferTo() 메서드가 InputStream에 추가되었습니다.

public class CopyExmaple {
    public static void main(String() args) throws Exception {
        String orginalFileName = "C:/Temp/test.jpg";
        String targetFileName = "C:/Temp/test2.jpg";

        InputStream is = new FileInputStream(orginalFileName);
        OutputStream os = new FileOutputStream(targetFileName);

        is.transferTo(os);

        is.close();
        os.close();

        System.out.println("복사 완료");
    }
}

18.4 문자 I/O 스트림

바이트 입/출력 스트림인 InputStream 및 OutputStream에 해당하는 문자 입/출력 스트림으로.
독자와 작가가 있습니다.


입출력 단위가 문자인 것을 제외하고는 사용 방법은 바이트 입출력 스트림과 동일하다.

18.4.1 문자 출력

Writer는 문자 출력 스트림의 최상위 클래스이며 추상 클래스입니다.


모든 문자 출력 스트림 클래스는 Writer 클래스에서 상속되어 생성됩니다.


FileWriter BufferedWriter PrintWriter OutputStreamWriter

주요 방법은 다음과 같습니다.


쓰기(정수 c) | 매개변수로 지정된 문자를 반환합니다.


쓰기 (char() cbuf) | 매개변수로 주어진 배열의 모든 문자를 인쇄합니다.


write(char() cbuf, int off, int len) |매개 변수로 지정된 배열에서 cubf(off)부터 len까지의 문자를 출력합니다.


쓰기(문자열 str) | 매개변수로 주어진 문자열을 반환
write(String str, int off, int len) | 매개변수로 주어진 문자열에서 len을 통해 순서가 맞지 않는 문자를 반환합니다.


플러시() | 버퍼에 남아 있는 모든 문자를 인쇄합니다.


닫기() | 출력 스트림을 닫고 사용 중인 메모리를 해제합니다.

Write는 OutputStream과 같은 방식을 사용하지만 출력 단위는 char입니다.


또한 문자열을 반환하는 write(String str) 메서드가 제공됩니다.

public class WriteExample {
    public static void main(String() args) throws IOException {
        // 문자 기반 출력 스트림 생성
        Writer writer = new FileWriter("C:/Temp/test.txt");

        // 1문자씩 출력
        char a = 'A';
        writer.write(a);
        char b = 'B';
        writer.write(b);

        // char배열 출력
        char() arr = { 'C', 'D', 'E' };
        writer.write(arr);

        // 문자열 출력
        writer.write("FGH");

        // 버퍼에 잔류하고 있는 문자들을 출력하고 버퍼를 비움
        writer.flush();

        // 출력스트림 닫기
        writer.close();
    }
}

18.4.2 문자 읽기

Reader는 문자 입력 스트림의 최상위 클래스이며 추상 클래스입니다.


모든 문자 입력 스트림 클래스는 Reader 클래스를 상속받아 생성되었습니다.


FileReader BufferedReader 입력스트림리더

읽기() | 1자를 읽고 뒤로
read(char() cbut) | 파라미터로 지정한 문자 배열에 읽은 문자를 저장하고 읽은 문자 수를 반환
닫기() | 입력 스트림을 닫고 사용 중인 메모리를 해제합니다.

public class ReadExample {
    public static void main(String() args) throws Exception {
        // 입력스트림 생성
        Reader reader = null;

        // 1문자씩 읽기
        reader = new FileReader("C:/Temp/test.txt");
        while (true) {
            int data = reader.read();
            if (data == -1) {
                break;
            }
            System.out.print((char) data);
        }
        reader.close();
        System.out.println();

        // 문자배열로 읽기
        reader = new FileReader("C:/Temp/test.txt");
        char() data = new char(100);
        while (true) {
            int num = reader.read(data);
            if (num == -1) {
                break;
            }
            for (int i = 0; i < num; i++) {
                System.out.print(data(i));
            }
        }
        reader.close();
    }
}

스트리밍할 때마다 흐름을 열어야 합니다.

18.5 2차 전류

보조 스트림은 다른 스트림과 연결되어 다양한 유용한 기능을 제공하는 스트림입니다.


보조 스트림은 자체적으로 I/O를 수행할 수 없기 때문에 I/O 소스에서 직접 생성된 I/O 스트림과 함께 사용해야 합니다.

입력 스트림 -> 보조 스트림 -> 프로그램 -> 보조 스트림 -> 출력 스트림

보조 스트림을 입/출력 스트림에 연결하려면 보조 스트림을 생성할 때 생성자에 입/출력 스트림을 매개 변수로 제공하십시오.
하위 스트림 변수 = 새 하위 스트림(입력/출력 스트림);
보조 스트림은 다른 보조 스트림에 연결되어 전원 체인을 형성할 수 있습니다.

자주 사용되는 보조 전류는 다음과 같습니다.


입력스트림리더 | 바이트 스트림을 문자 스트림으로 변환
BufferedInputStream/Reader BufferedOutputStream/Writer | I/O 성능 향상
데이터입력스트림, 데이터출력스트림 | 기본형 데이터 입출력
PrintStream, PrinterWriter | 줄 바꿈 처리 및 형식이 지정된 문자열 출력
ObjectInputStream, ObjectOutputStream | 개체 입력 및 출력

18.6 캐릭터 변환 스트림

바이트 스트림에 입/출력할 데이터가 문자인 경우에는 문자 스트림 리더나 라이터로 변환하여 사용하는 것이 좋다.


편리하고 문자 집합 유형을 지정할 수 있기 때문입니다.

18.6.1 InputStream을 Reader로 변환

InputStream is = new FileInputStream(“test.txt”);
판독기 판독기 = new InputStreamReader(is);
InputStream을 판독기에 연결하기만 하면 됩니다.

FileReader는 이 연결 프로세스가 내부적으로 정의되어 있다고 가정할 수 있습니다.

18.6.2 OutputStream을 Writer로 변환
OutPutStream os = new FileOutPutStream(“test.txt”);
작성자 작성자 = new OutputStreamWriter(os);

FileWriter에도 이 연결 프로세스가 내장되어 있습니다.

->FileWirter/FileReader는 문자열을 지정할 수 없습니다.

다른 것들과 번갈아가며 사용해야 할 것 같습니다.

public class CharacterConvertStreamExample {
    public static void main(String() args) throws Exception {
        write("문자 변환 스트림을 사용합니다.

"); String data = read(); System.out.println(data); } public static void write(String str) throws Exception { OutputStream os = new FileOutputStream("C:/Temp/test.txt"); Writer writer = new OutputStreamWriter(os, "UTF-8"); writer.write(str); writer.flush(); writer.close(); } public static String read() throws Exception { InputStream is = new FileInputStream("C:/Temp/test.txt"); Reader reader = new InputStreamReader(is, "UTF-8"); char() data = new char(100); int num = reader.read(data); reader.close(); // char배열에서 읽은 문자 수만큼 문자열로 변환 String str = new String(data, 0, num); return str; } }

18.7 성능 개선 흐름

CPU와 메모리가 아무리 좋아도 하드 디스크 I/O가 느려지면 프로그램의 실행 성능이 하드 디스크의 처리 속도에 적응하게 됩니다.


네트워크에도 동일하게 적용됩니다.


이 문제에 대한 완벽한 해결책은 없지만 프로그램이 I/O 소스와 직접 작업하는 대신 메모리 버퍼를 중간에 두고 작업하도록 함으로써 실행 성능을 향상시킬 수 있습니다.


출력 스트림의 경우 데이터를 하드 디스크로 직접 보내는 대신 메모리 버퍼로 데이터를 보내면 출력 속도를 높일 수 있습니다.


버퍼는 데이터가 쌓일 때까지 기다렸다가 가득 차면 데이터를 한꺼번에 디스크로 보내 출력 수를 줄인다.


입력 전류에도 동일하게 적용됩니다.

public class BufferedExample {
    public static void main(String() args) throws Exception {
        // 입출력 스트림생성
        String originalFilePath1 = BufferedExample.class.getResource("originalFile1.jpg").getPath();
        String targetFilePath1 = "C:/Temp/targetFile1.jpg";

        FileInputStream fis = new FileInputStream(originalFilePath1);
        FileOutputStream fos = new FileOutputStream(targetFilePath1);

        // 입출력 스트림 + 버퍼 스트림생성
        String originalFilePath2 = BufferedExample.class.getResource("originalFile2.jpg").getPath();
        String targetFilePath2 = "C:/Temp/targetFile2.jpg";

        FileInputStream fis2 = new FileInputStream(originalFilePath2);
        FileOutputStream fos2 = new FileOutputStream(targetFilePath2);
        BufferedInputStream bis = new BufferedInputStream(fis2);
        BufferedOutputStream bos = new BufferedOutputStream(fos2);

        // 버퍼를 사용하지 않고복사
        long nunBufferTime = copy(fis, fos);
        System.out.println("버퍼 미사용: " + nunBufferTime + " ns");
        //버퍼 미사용: 7560354800 ns

        // 버퍼사용
        long BufferedTime = copy(bis, bos);
        System.out.println("버퍼 사용: " + BufferedTime + " ns");
        //버퍼 사용: 94382700 ns

        fis.close();
        fos.close();
        bis.close();
        bos.close();
    }

    public static long copy(InputStream is, OutputStream os) throws Exception {
        // 시작시간저장
        long start = System.nanoTime();
        // 1바이트 읽고 1바이트 출력
        while (true) {
            int num = is.read();
            if (num == -1) {
                break;
            }
            os.write(num);
        }
        os.flush();
        long end = System.nanoTime();
        return end - start;
    }
}

나는 시차를 보기 위해 의도적으로 한 글자씩 읽었다.


학습을 위한 배열로 읽는 법도 썼습니다.

byte() data = new byte(1024);
while (true) {
    int num = is.read(data);
    if (num == -1) {
        break;
    }
    os.write(data, 0, num);
}
os.flush();

18.7.2 성능 향상 스트림 -2

BufferedReader를 문자 입력 스트림 판독기에 연결하면 성능 향상 외에 또 다른 이점이 있습니다.


라인 단위를 문자열로 읽는 매우 편리한 readLine() 메서드를 제공합니다.

public class ReadLineExample {
    public static void main(String() args) throws Exception {
        Reader reader = new FileReader("src/Ch18/sec07/exam02/ReadLineExample.java");
        BufferedReader br = new BufferedReader(reader);    

        int lineNo = 1;
        while (true) {
            String str = br.readLine();
            if (str == null) {
                break;
            }
            System.out.println(lineNo + "\t" + str);
            lineNo++;
        }
        br.close();
    }
}

18.8 원시 스트림

보조 DataInputStream 및 DataOutputStream 스트림을 바이트 스트림에 연결하여 기본 유형을 입력 및 출력할 수 있습니다.


읽기 기본 유형(); 기본 쓰기();
문자열은 readUTF()입니다.

writeUTF() 메서드가 있습니다.


데이터 유형의 크기가 모두 다르다는 점에 유의해야 합니다.


다시 읽을 때 출력된 순서대로 입력을 받아야 합니다.

public class DataInputOutputStreamExample {
    public static void main(String() args) throws Exception {
        // Data출력 생성
        FileOutputStream fos = new FileOutputStream("C:/Temp/primitive.db");
        DataOutputStream dos = new DataOutputStream(fos);

        // 기본타입 출력
        dos.writeUTF("홍길동");
        dos.writeDouble(95.5);
        dos.writeInt(1);

        dos.writeUTF("김자바");
        dos.writeDouble(90.3);
        dos.writeInt(2);

        dos.flush();
        dos.close();
        fos.close();

        // Data입력스트림
        DataInputStream dis = new DataInputStream(new FileInputStream("C:/Temp/primitive.db"));

        // 기본타입입력
        for (int i = 0; i < 2; i++) {
            String name = dis.readUTF();
            double score = dis.readDouble();
            int order = dis.readInt();
            System.out.println(name + ": " + score + " :" + order);
        }
        dis.close();
    }
}

18.9 인쇄 스트림

PrintStream 및 PrintWriter는 프린터처럼 출력하는 print() println() printf() 메서드가 있는 스트림입니다.


out 필드가 PrintStream 유형이기 때문에 sysout을 사용하여 콘솔에 인쇄합니다.

public class PrintStreamExample {
    public static void main(String() args) throws Exception {
        FileOutputStream fos = new FileOutputStream("C:/Temp/printStream.txt");
        PrintStream ps = new PrintStream(fos);

        ps.print("마치 ");
        ps.println("프린터가 출력하는 것 처럼");
        ps.println("데이터를 출력합니다.

"); ps.printf("| %6d | %-10s | %10s | \n", 1 , "홍길동", "도적"); ps.printf("| %6d | %-10s | %10s | \n", 2 , "김자바", "학생"); ps.flush(); ps.close(); } }