language/java

15. 입출력

wooweee 2023. 3. 26. 09:41
728x90

1.입출력(I/O)과 스트림(stream)

  • 원리
    • 외부와 자바프로그램간의 data를 주고 받는 과정 필요
    • data를 주고 받기 위해 외부와 자바프로그램을 연결시켜주는 다리가 필요 : stream
      • stream은 14장의 Stream과 다르다.
    • 연결시켜주는 다리(일방통행) : Input, Output
      1. 외부에서 자바로 들어가는 것 하나 : Input
      2. 자바에서 외부로 보내는 것 하나 : Output

  • 목적
    • 외부에서 데이터를 받아서 읽기
    • 내부에서 데이터 작성해서 외부로 보내기
    • input과 output은 따로따로 이다. 연결해서 작동하는 경우가 많은 거지 서로 연결할 필요가 없다.

  • read(), write()
    input과 output stream의 다른 method들이 분명 더 있겠지만 읽고 쓰기가 기본 원리이다.
    1. 외부에서 온 데이터 읽고 싶을 때 : read()
      1. Inputstream이 외부데이터를 먼저 받는다. -> 객체
      2. 해당 객체로부터 read()해서 java가 읽을 수 있도록 해준다.
    2. 내부에서 작성한 데이터 외부로 보낼 때 : write()
      1. Outputstream을 먼저 생성한다. -> 객체 
      2. 해당 객체에 write()를 이용해서 외부로 보내고 싶은 데이터를 넣는다.
        • 자바에서 작성하는 것자체가 write 아닌가?
          • 내부에서 작성한 데이터는 자바프로그램이 읽을 수 있는거지 외부의 형태(동영상, 노래 파일 등등)로 읽힌다는 보장이 없다.
          • 그래서 내부에서 작성한 데이터를 outputstream에 맞게 다시 write()해줘야한다.
더보기

 

inputStream(inputData 받기) - 데이터 형태

read로 읽음 - inputstream의 데이터 존재 여부 체크 -1, 0

- read로 한번 데이터 정리 - 필요데이터만 뽑아온다던지 -> 이방식으로 실제 input 데이터를 볼수 있음

write으로 output stream에 보낼 데이터 넣어줌 - 이상하게 생김

 

output으로

 

 

2. 흐름

  • Byte기반stream : FileStream, Byte[]Stream, PipedSteam, AudioStream, StringBufferStream ... - Input,Output  생략
    • method: read(), write()가 핵심
    • 보조 stream : Filter, Buffer, LineNumber, Print, pushBack, sequence ...
  • Char 기반stream : FileReader,Writer  , Char[]  ,  String  ,  Piped  ...
    reader = Input
    writer  = Output
    Byte[] -> Char[]
    • method: read(), write()가 핵심
    • 보조 stream : Filter, Buffer, LineNumber, Print, pushBack ...

  • 기타 Stream
    1. InputStreamReader, OutputStreamReader : byteStream에서 charStream으로 변경

    2. 표준 Stream : System.out, System.in ... -> 객체 생성없이 사용 가능

    3. 직렬화 Stream : object를 stream에 넣을 수 있도록 해줌

  • File
    • stream에 들어가는 data 형태 중 하나
    • 매우 자주 사용되므로 알아둘 필요 존재

 

3. Byte 기반 stream

  • byte로 data를 해결
    • InputStream에 data를 저장할 때 byte 형태로 저장
    • OutputStream에 data write()할 때 params로 byte를 받아서 외부로 데이터 보내고  해당 데이터를 변해야되는 data type으로 변경(음악, 파일, 영상 등등) - 알아서 해줌

3.1. 메서드

// InputStream
int available() // 스트림으로부터 읽어 올 수 있는 데이터 크기를 반환
long skip(long n) // 스트림에서 주어진 길이만큼 건너뜀

boolean markSupported() //mark와 reset을 지원하는지 확인해줌
void mark(int readlimit) // 현재위치를 표시, reset()으로 해당 위치로 돌아갈 수 잇다. params는 되돌아 갈 수 있는 수
void reset() // 마지막 mark가 호출된 곳으로 되돌아감

abstract int read() // 1byte를 읽어온다. 없으면 -1 반환
int read(byte[] b) // 배열 b 길이 먼저 읽고 b배열 길이만큼 data 반환 
int read(byte[] b, int off, int len) // 배열 b의 지정된 위치 off부터 len개수만큼 저장

void close() // 스트림을 닫음으로써 사용하고 있던 자원을 반환

// OutputStream
void flush()// 스트림의 버퍼에 있는 모든 내용을 출력소스에 사용

abstract void write(int b) // 주어진 값을 출력소스에 사용
boid write (byte[] b) // 주어진 배열 b에 저장된 모든 내용을 출력소스에 사용
void write(byte[] b, inf off, int len) // 주어진 배열 b에 저장된 내용중 off번째부터 len개수만큼 읽어서 출력소스에 사용

void close() // 입력소스를 닫음으로써 사용하고 있던 자원을 반환

 

3.2. 사용 예

byteArrayStream으로 예시

  • 요약
    1. 외부 src를 byteInput에 담는다. = input
    2. input을 read()를 통해서 하나하나를 읽는다
      • read()는 byte 한개씩 읽고 한번 read()할 때 마다 사라진다.
      • read()를 통해서 하나씩 읽은 data를 어떻게 사용할지가 관건이다.
        • write에 사용할수있고
        • 그냥 print()할 때 쓸수 있고
        • 사용 목적에 따라 달라진다.
    3. read()를 통해 나온 낱개 낱개를 write에 넣어준다. - output
    4. output에 저장된 data를 outSrc에 담아준다.
      • output이나 input 같은 경우는 컴퓨터가 읽을 수 있는 0/1의 형태에 맞게 변경되어서 read(), write()하는 것이다.
      • 따라서 read(), write()만 하면 컴퓨터가 전송할 수 있는 형태가 된 것이고 == 다른 컴퓨터나 파일로 이동가능한 상태
      • 사람이 읽을려면 그에 맞는 변경이 필요하다.
  • stream 인스턴스에 담긴 data는 stream에 맞게 저장되어있는 것이다.
  • 따라서 예제라서 한정이 된것이지만 write해서 쓴 것을 사람이 볼수 있게 볼 때, 그리고 보는 곳이 ide이기 때문에 byte[]로 변경해서 보는 것이다.

 

class Try1 {
    public static void main(String[] args) {
        byte[] inSrc = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
        byte[] outSrc = null; // 출력한 데이터를 받을 공간이 필요

        // 외부로 부터 input을 java 프로그램이 받음 -> byte로 저장
        ByteArrayInputStream input = new ByteArrayInputStream(inSrc); 
        ByteArrayOutputStream output = new ByteArrayOutputStream();

        int data = 0;

        while ((data = input.read()) != -1) {
            output.write(data); // write로 read한 데이터 사용 -> input에서는 데이터가 하나씩 사라짐
        }
        System.out.println("input.read() = " + input.read()); // -1 나옴

        outSrc = output.toByteArray(); // 현재 outputStream에 저장한 data를 읽을 수 있게 byte[]에 넣는 과정

        System.out.println("Arrays.toString(inSrc) = " + Arrays.toString(inSrc)); 
        // Arrays.toString(inSrc) = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        System.out.println("Arrays.toString(outSrc) = " + Arrays.toString(outSrc)); 
        // Arrays.toString(outSrc) = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        
        System.out.println("input = " + input); 
        // input = java.io.ByteArrayInputStream@372f7a8d
        System.out.println("output = " + output); 
        //output = nullKVOWKGBO // input에서 객체 나온거나 output에서 이상한거 나온거나 동일하다고 보면됨
    }
}

 

class Practice {
    public static void main(String[] args) {
        byte[] inSrc ={0,1,2,3,4,5,6, 'a'};
        byte[] outSrc;
        // 출력stream의 값을 넣을 공간을 직접 만들어 준것
        // file 같은 경우에는 new FileOutPutStream("출력 stream 값을 넣을 수 있게 file 명 작성하면 file 생성됨")
        byte[] temp = new byte[10];

        ByteArrayInputStream input = new ByteArrayInputStream(inSrc);
        ByteArrayOutputStream output = new ByteArrayOutputStream();

        int read = input.read(); // 이미 하나 써버림
        input.read(temp, 0, temp.length);
        // temp로 값 넣어주면서 input의 배열은 준거만큼 삭제됨
        // temp 배열에 0부터 temp.length 개까지 data를 넣는다.
        // 만약 temp에서 필요로하는 index 개수가 input의 개수 이상이면 0을 저장

        output.write(temp, 5, 5);
        // 출력하려고 출력스트림에 값을 넣어줌 - temp는 byte[]배열이므로 삭제는 안됨
        // byteStream이라서 그런지 byte형식이 직접 존재하면 write()의 params에 넣어도 상관 없는 거 같다.

        outSrc = output.toByteArray();
        // 출력stream의 값을 넣을 공간인 outSrc에 넣어줌
        // file같은 경우는 만든 파일 속에 출력stream값이 byte형태에서 string이든 뭐든 알아서 변경되서 작성됨
        // bytestream같은 경우 직접 변경해줘야함

        System.out.println("Arrays.toString(in) = " + Arrays.toString(inSrc));
        System.out.println("Arrays.toString(temp) = " + Arrays.toString(temp));
        System.out.println("Arrays.toString(outSrc) = " + Arrays.toString(outSrc));
    }
}

 

class Try1 {
    public static void main(String[] args) {
        byte inSrc[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
        byte[] outSrc;
        byte[] temp = new byte[4];

        ByteArrayInputStream input = new ByteArrayInputStream(inSrc);
        ByteArrayOutputStream output = new ByteArrayOutputStream();

        try {
            while (input.available() > 0) {
                System.out.println("input.available() = " + input.available());
                // available은 blocking없이 읽을수 있는 stream 개수 반환
                
                input.read(temp); // 4개만 읽기 가능 - temp길이가 4개 이기 때문
                output.write(temp);
                // 4개를 보내주는데 마지막에 2개만 보내줘야하지만 
                // byte[]는 값을 덮어쓰기 때문에 마지막에 8,9,6,7을 보내게 됨.

                outSrc = output.toByteArray();
                System.out.println("Arrays.toString(temp) = " + Arrays.toString(temp));
                System.out.println("Arrays.toString(outSrc) = " + Arrays.toString(outSrc));
            }
        } catch (IOException e) {
                // read, write는 필수로 IOException 예외처리를 해야함
        }

    }
}

 

3.3. FileStream - 자주 사용

  • 생성자
// input
FileInputStream(String name) // 지정된 파일이름(name)을 가진 실제 파일과 연결된 FileInputStream 생성
FileInputStream(File file) // 파일이 file 인스턴스인 것을 제외하고 위에 생성자와 동일
FileInputStream(FileDescriptor fdObj) // 파일 디스크립터로 fileInputStream을 생성

// output
FileOutputStream(String name) 
// 지정된 파일이름을 가진 실제 파일과 연결된 fileoutputstream 생성
// 지정된 파일없을시 생성, 존재시 덮어쓰기 default
FileOutputStream(String name, boolean append) // append true 일때 덮어쓰지 않고 파일 마지막에 내용 덧붙임

FileOutputStream(File file) // 파일의 이름을 String이 아닌 File 인스턴스로 지정한것 말고 동일
FileOutputStream(File file, boolean append) // 동일

FileOutputStream(FileDescriptor fdObj) // 파일 디스크립터로 fileOutputStream을 생성

 

  • FileStream - read() 예시
    • 만약 readfile에 한글 존재시 글자 깨짐 -> byte의 unicode가 다 처리 못함
    • read 일때만 그렇고 실제 write한 파일에는 잘 처리됨
public class Try3 {
    public static void main(String[] args) throws IOException {

        FileInputStream fis = new FileInputStream("절대경로+Try3.java");

        int data = 0;
        while ((data = fis.read()) != -1) {
//            int read = fis.read(); // byte로 나옴
//            System.out.println("read = " + read);
            char c = (char) data;
            System.out.print(c);
        }
    }
}

 

  • 중요
    • new FileOutputStream("생성하고 싶은 txt 파일명") : 해당 stream에 write하면 byte코드로 해당 instance에 저장해서 실제 txt 파일에 사람이 읽을 수 있는 원래 언어로 변경되는 일련의 과정을 자기가 알아서 다 처리해줌
    • 이전 Byte 같은 경우는 write가 다 해주지 않아서 직접 toArray 써가면서 사람이 읽을 수 있게 수동으로 변경한 것
public class Try3 {
    public static void main(String[] args){
        try {
            FileInputStream fis = new FileInputStream("절대경로 + Try3.java"); 
            // Try3.java와 연결된 FileStream 생성 - fileStream 객체 내부에 해당 파일을 byte로 저장한 배열이 존재
            System.out.println("fis = " + fis); // fis = java.io.FileInputStream@372f7a8d
            FileOutputStream fos = new FileOutputStream("hi.txt");
            // fileStream을 통해서 file생성
            // write를 통해서 들어온 byte type의 data를 fileStream에 저장 
            // 실제 출력값을 저장할 hi.txt(아까 위의 byte[] outSrc 같은 역할)에는 fileStream이 자동으로 byte->string으로 변환후 저장해줌
            // 위 예제에서는 toByteArray로 직접 작성해줬었다.
            System.out.println("fos = " + fos); // fos = java.io.FileOutputStream@2f92e0f4

            // byteStream 예외 동일
            int data = 0;
            while ((data = fis.read()) != -1) {
                fos.write(data);
                System.out.print((char) data); //read 된 것보고 싶어서 넣음
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

4. 보조 스트림

4.1. FilterStream

  • Input Output Stream의 자손
  • 모든 보조 스트림의 조상
  • 역할: 기반 스트림의 메서드를 그대로 호출해준다.

 

4.2. BufferedStream

  • filter stream의 자손
  • 기반 스트림의 입출력 효율을 높이기 위해 버퍼를 사용하는 보조스트림
  • 1 byte씩 입출력하는 것보다 버퍼를 이용해서 여러 바이트를 입출력하는 것이 빠르기 때문에 사용

 

  • BufferedInputStream
//생성자
BufferedInputStream(InputStream in, int size) 
// in 객체를 입력소스로 하고 지정된 크기만큼의 내부 buffer인스턴스 생성
// 내부 buffer 인스턴스로 read하고 하는 것이 외부데이터와 연결해서 read()하는 것보다 효율적이다.
// read()를 통해서 data를 다 읽은 후 다시 read()하면 자동으로 내부 buffer에 size만큼 다시 data를 저장한다.

BufferedInputStream(InputStream in) // 크기를 default로 8192byte로 가진다.

 

  • BufferedOutputStream
// 생성자
BufferedOutputStream(OutputStream out, int size) 
// write로부터 오는 출력이 bufferstream에 먼저 저장되고 가득 차면 모든 내용을 실제 가야되는 outputStream으로 모두 간다.
// outputStream은 이전에 공부한 방식으로 출력값을 저장해야되는 공간으로 이동시킨다.(abc.txt, byte[] abc)
// 마지막 부분은 buffer가 가득차지 않으므로 close()나 flush()로 마지막 버퍼를 다 출력시켜줘야한다.

BufferedOutputStream(OutputStream out) // 크기를 default로 8192byte 가짐

// method
flush() // buffer의 모든 내용을 출력소스(outputStream)으로 출력(이동) 후 버퍼를 비운다.
close() // flush를 호출 한 후, bufferoutputStream 객체서 사용하던 모든 자원을 반환한다.

 

  • 사용 예
    • char를 넣어야 하는 이유
      1. char 타입은 2바이트 크기의 데이터 타입이며, 0 ~ 65535 범위의 유니코드 문자를 나타낸다.
        • char 타입으로 1과 9를 사용하면 2바이트 크기의 유니코드 문자가 출력
          바이트 스트림으로 변환할 때 인코딩 방식을 지정해줄 수 있으며, 정상 결과 나타난다.
      2. int 타입은 4바이트 크기의 데이터 타입이며, -2147483648 ~ 2147483647 범위의 정수를 나타낸다.
        • for문에서 int 타입으로 1과 9를 사용하면 1부터 8까지의 4바이트 크기의 정수값이 출력
          문자로 변환하면 현재 사용되는 인코딩에 따라 예상치 못한 결과가 나타난다.
public class Try3 {
    public static void main(String[] args){
        try {
            FileOutputStream fos = new FileOutputStream("hi.txt");
            BufferedOutputStream bos = new BufferedOutputStream(fos, 5);

            for (int i = '1'; i < '9'; i++) { // 해당 char의 int값을 사용해야 올바른 문자가 출력됨
                bos.write(i);
            }
            fos.close(); // 사용안하면 1,2,3,4,5만 저장됨
            bos.close(); // 사용안하면 1,2,3,4,5만 저장됨 - buffer에 담은 것을 다 꺼내야 함
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

 

4.3. SequenceStream

  • SequenceInputStream
    • 여러 개의 입력스트림을 연속적으로 연결해서 하나의 스트림으로부터 데이터를 읽는 것과 같이 처리
    • 큰 파일을 여러 개의 작은 파일로 나눠서 작업했다가 마지막에 파일을 합칠 때 좋다.
// 생성자
SequenceInputStream(Enumeration e) // Enumeration에 저장된 순서대로 입력스트림을 하나의 스트림으로 연결
SequenceInputStream(InputStream s1, InputStream s2) // 2개의 입력스트림을 하나의 스트림으로 연결

// 사용법
// 1. Vector에 해당 stream 객체들을 넣은 후 sequenceStream 이용하기 - 합할 stream이 3개 이상일 때 사용
Vector files = new Vector();
files.add(new FileInputStream("file.001"));
files.add(new FileInputStream("file.002"));

System.out.println("v = " + v);
SequenceInputStream in = new SequenceInputStream(files.elements());

// 2. 파일을 보조스트림에 다이렉트로 박아넣음
FileInputStream file1 = new FileInputStream("file.001");
FileInputStream file2 = new FileInputStream("file.002");
SequenceInputStream in = new SequenceInputStream(file1, file2);

 

  • SequenceOutputStream
    • stream이 3개 이상일 때는 vector와 sequenceStream을 잘 조합해서 사용 필요
public class Try3 {
    public static void main(String[] args){
        
        byte[] arr1 = {1,2,3};
        byte[] arr2 = {4,5,6};
        byte[] arr3 = {7,8,9};
        byte[] outSrc = null;

        Vector v = new Vector<>();
        v.add(new ByteArrayInputStream(arr1));
        v.add(new ByteArrayInputStream(arr2));
        v.add(new ByteArrayInputStream(arr3));

        SequenceInputStream input = new SequenceInputStream(v.elements());
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        
        int data = 0;
        try {
            while ((data = input.read()) != -1) {
                output.write(data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        outSrc = output.toByteArray();
        System.out.println("Arrays.toString(outSrc) = " + Arrays.toString(outSrc));
    }
}

 

4.4. PrintStream

  • System.out.xxx  <- 여기 들어가던 println(), printf(), print() 요녀석들 얘기하는 것

 

5. Char 기반 stream

 

5.1. Reader

  • InputStream의 메서드와 다를게 없다.
  • byte배열대신 char배열 사용한다는 것만 차이점
    • fileInputStream은 한글이 깨져서 출력되지만 Reader는 인코딩과 자바에서 사용하는 유니코드 간의 변환을 자동적으로 처리
// Reader
boolean ready() // 입력소스로부터 데이터를 읽을 준비가 됬는지 알림
long skip(long n) // 스트림에서 주어진 길이만큼 건너뜀

boolean markSupported() //mark와 reset을 지원하는지 확인해줌
void mark(int readlimit) // 현재위치를 표시, reset()으로 해당 위치로 돌아갈 수 잇다. params는 되돌아 갈 수 있는 수
void reset() // 마지막 mark가 호출된 곳으로 되돌아감

int read() // 하나의 문자를 읽어온다. 마지막 데이터에 도달시 -1 반환
int read(char[] c) // 배열 c 길이 먼저 읽고 c배열 길이만큼 data 반환 
int read(charBuffer target) // 입력소스로를 읽어서 문자버퍼에 저장
abstract int read(char[] c, int off, int len) // 입력소스로부터 최대 len개 문자읽어서, 배열 c에 지정된 off부터 len 길이만큼 저장

abstract void close() // 스트림을 닫음으로써 사용하고 있던 자원을 반환

 

5.2. Writer

  • OutputStream의 메서드와 다를게 없다.
  • byte배열대신 char배열 사용한다는 것만 차이점
  • 문자열 인코딩과 자바에서 사용하는 유니코드간의 변환을 자동적으로 처리
// Writer
Writer append(char c) // 지정된 문자를 출력소스에 출력
Writer append(CharSequence c) // 지정된 문자열을 출력소스에 출력
Writer append(CharSequence c, int start, inst end) // 지정된 문자열 일부를 출력소스에 출력


void write(int b) // 주어진 값을 출력소스에 사용
void write(String str) // 주어진 문자열을 출력소스에 사용
abstract void write(String str, inf off, int len) // 주어진 str에 저장된 내용중 off번째부터 len개수만큼 읽어서 출력소스에 사용
void write (char[] c) // 주어진 배열 c에 저장된 모든 내용을 출력소스에 사용
abstract void write(char[] c, inf off, int len) // 주어진 배열 c에 저장된 내용중 off번째부터 len개수만큼 읽어서 출력소스에 사용


abstract void flush()// 스트림의 버퍼에 있는 모든 내용을 출력소스에 사용(buffer가 있는 스트림에 한정)
abstract void close() // 입력소스를 닫음으로써 사용하고 있던 자원을 반환

 

5.3. 사용 예

  • 인코딩 변환 확인 예제
public class Try3 {
    public static void main(String[] args){
        try {
            String fileName = "hi.txt";
            FileInputStream fis = new FileInputStream(fileName);
            FileReader fr = new FileReader(fileName);

            int data = 0;
            while ((data = fis.read()) != -1) {
                System.out.print((char) data);
            }
            System.out.println();
            fis.close();

            while ((data = fr.read()) != -1) {
                System.out.print((char) data);
            }
            System.out.println();
            fr.close();
        } catch (IOException e) {

        }

    }
}

 

  • 띄어쓰기 공백 tab 제거
public class Try3 {
    public static void main(String[] args){
        try {
            FileReader fr = new FileReader("hi.txt");
            FileWriter fw = new FileWriter("bye.txt");

            int data = 0;
            while ((data = fr.read()) != -1) {
                if (data!='\t' && data!='\n' && data!=' ' && data !='\r')
                    fw.write(data);
            }
            fr.close();
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

 

5.4. StringReader, StringWriter

  • StringWriter에 출력되는 data는 StringWriter 내부의 StringBuffer에 저장된다. - StringBuffer 객체 꺼내고싶으면 getBuffer() 사용하면 된다.
public class Try3 {
    public static void main(String[] args){

        String inputData = "abcd";
        StringReader sr = new StringReader(inputData);
        StringWriter sw = new StringWriter();

        int data = 0;
        try {
            while ((data = sr.read()) != -1) {
                sw.write(data);
            }
            StringBuffer buffer = sw.getBuffer();
            System.out.println("buffer = " + buffer);
            sr.close();
            sw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("inputData = " + inputData);
        System.out.println("sw = " + sw);
    }
}

 

 

 

5. 보조 스트림

5.1. BufferedReader, BufferedWriter

  • 이전 보조 스트림과 동일
  • readLine() : 파일을 라인단위로 읽는다. -> String line에 read한 값을 넣음
public class Try3 {
    public static void main(String[] args){
        try {
            FileReader fr = new FileReader("hi.txt");
            BufferedReader br = new BufferedReader(fr);

            String line = "";
            for (int i = 1; (line = br.readLine()) != null; i++) {
                // ;를 포함한 라인을 출력
                if (line.indexOf(";") != -1) {
                    System.out.println(i + ":" + line);
                }
            }
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

 

 

6. 기타 스트림

6.1. 연결 Stream - InputStreamReader, OutputStreamWriter

  • 바이트기반 스트림을 문자기반 스트림으로 연결시켜주는 역할 
  • 바이트기반 스트림의 데이터를 지정된 인코딩의 문자데이터로 변환하는 작업 수행
  • 생성자와 메서드
// Reader 생성자, 메서드
InputStreamReader(InputStream in) // os에서 사용하는 기본 인코딩 문자로 변환후  Reader 생성
InputStreamReader(InputStream in, String encoding) // 지정한 인코딩을 사용한 Reader 생성
String getEncoding() // InputStreamReader의 인코딩을 알려준다.


// Writer 생성자, 메서드
OutputStreamWriter(OutputStream in) // os에서 사용하는 기본 인코딩 문자로 변환후  Writer 생성
OutputStreamWriter(OutputStream in, String encoding) // 지정한 인코딩을 사용한 Writer 생성
String getEncoding() // OutputStreamWriter의 인코딩을 알려준다.

 

6.2. 표준 입출력

  • 표준 입출력: 콘솔을 통한 데이터 입력콘솔로의 데이터 출력
    1. System.in
      • InputStream으로부터 input값 받음
      • 콘솔로부터 데이터를 입력받는데 사용
    2. System.out
      • PrintStream으로 보조stream이용해서 출력값 도스창에 출력
      • 콘솔로 데이터를 출력하는데 사용
    3. System.err
      • PrintStream으로 보조stream이용해서 출력값 도스창에 출력
      • 콘솔로 데이터를 출력하는데 사용
  • in, out, err: System class에 선언된 클래스변수(static 변수)

 

  • 표준 입출력 대상변경
    • 콘솔 화면에서부터 다른 입출력대상으로 변경 가능
// 입출력 대상변경 method
static void setOut(PrintStream out) // System.out 출력을 지정된 PrintStream으로 변경
static void setErr(PrintStream err) // System.err 출력을 지정된 PrintStream으로 변경
static void setIn(PrintStream in) // System.in 출력을 지정된 InputStream으로 변경

입출력 대상변경 사용예

public class Try3 {
    public static void main(String[] args){
        PrintStream ps = null;
        FileOutputStream fos = null;

        try {
            fos = new FileOutputStream("test.txt");
            ps = new PrintStream(fos);
            System.setOut(ps);
        } catch (IOException e) {
            System.err.println("File not found.");
        }
        System.out.println("hi");
        System.err.println("hi");
    }
}

 

6.3. 직렬화, ObjectnputStream, ObjectOutPutStream

  • 직렬화
    • 객체를 데이터 스트림으로 만드는 것
    • 객체에 저장된 데이터를 스트림에 write() 하기 위해 연속적인 데이터로 변환 하는 것
  • 역직렬화
    • stream으로부터 data를 읽어서 객체를 만드는 것

 

  • ObjectInputStream, ObjectOutputStream
    • 보조스트림 중 하나
    • 객체 생성할 때 입출력할 스트림을 먼저 지정해줘야한다.

    • ObjectInputStream
      • 역직렬화
      • ObjectInputStream.readObject();
    • ObjectOutputStream
      • 직렬화
      • ObjectOutputStream.writeObject();
    • readXXX(), writeXXX() : Object 타입 이외에도 여러 타입을 가진 메서드를 제공, 어지간한 타입 다 존재함
  • 사용
// 직렬화
FileOutputStream fos = new FileOutputStream("파일명.txt"); // 3. 실제 파일에 객체 저장됨
ObjectOutputStream out = new ObjectOutputStream(fos);
out.writeObject(new 객체()); // 1. 2.
// 1. 객체를 파일명.txt에 저장하는 과정
// 2.보조stream에 객체 직렬화

// 역직렬화
FileInputStream fos = new FileInputStream("파일명.txt"); // 1. 객체가 저장된 실제 파일 inputstream 생성
ObjectInputStream in = new ObjectInputStream(fos);
객체타입 info = (객체타입)in.readObject(); // 2. 3.
// 2. 보조stream에 객체 역직렬화 -> Object type으로 나옴
// 3. 형변환 필요

 

  • 직렬화 가능한 클래스 생성
    • 직렬화 하고 싶은 필드를 Serializable interface 구현만 하면 끝
    • 조건
      1. class에 Serializable 구현시 : 모든 필드 직렬화 대상에 포함
      2. 조상 class에서 Serializable 구현시 : 조상 필드부터 자손 필드까지 직렬화 대상에 포함
      3. 자손 class에서만 Serializable 구현시 : 조상 필드는 직렬화에 제외
public class Info{
    String name;
    String password;
    int age;
}

// 직렬화 class
public class Info implements Serializable{
    String name;
    String password;
    int age;
}

 

  • 직렬화 제외
    • Serializable을 구현해도 필드내에 직렬화가 불가한 필드가 존재시 NotSerializableException 발생한다.
    • 직렬화가 제외되는 필드
      1. 참조변수가 아니라 참조변수가 가리키는 객체가 기본형이 아닌 객체타입일 때 불가
      2. password같이 보안상 넘기면 안되는 필드
// 직렬화 제외 class 
public class Info implements Serializable{
    String name;
    int age;
    
    transient String password; // 중요한 보안 정보
    transient Object obj = new Object(); // 참조변수의 실제타입이 객체인 경우
    
    Object strObj = new String("abc"); // 현재는 예외가 발생하지만 Object class에 Serializable 구현하면 정상 작동
}

 

  • 직렬화
public static void main(String[] args) throws IOException {
    try {
        String fileName = "test.txt";
        FileOutputStream fos = new FileOutputStream(fileName);
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        ObjectOutputStream out = new ObjectOutputStream(bos);

        Try3 t1 = new Try3("1", "234", 12);
        Try3 t2 = new Try3("2", "23334", 121);

        ArrayList<Try3> list = new ArrayList<>();
        list.add(t1);
        list.add(t2);

        out.writeObject(t1);
        out.writeObject(t2);
        out.writeObject(list);
        out.close();
        System.out.println("직렬화 잘 끝");
    } catch (IOException e) {
        e.printStackTrace();
    }
}

 

  • 역직렬화
    • 객체를 읽을 때 저장한 순서를 지켜야한다.
    • 그래서 객체에 직렬화할 때 collection framework를 이용해서 안에 한번에 넣어주면 순서를 고려할 필요가 없으므로 객체 각각 저장하는 것보다 Collection type으로 한번에 직렬화하는 것을 추천
public static void main(String[] args) throws IOException {
    try {
        String fileName = "abc.txt";
        FileInputStream fis = new FileInputStream(fileName);
        BufferedInputStream bis = new BufferedInputStream(fis);

        ObjectInputStream in = new ObjectInputStream(bis);

        // 객체 읽을때는 출력한 순서와 동일한 순서로 읽어야 한다.
        Try3 t1 = (Try3) in.readObject();
        Try3 t2 = (Try3) in.readObject();
        ArrayList list = (ArrayList) in.readObject();

        System.out.println(t1);
        System.out.println(t2);
        System.out.println(list);

        in.close();
        System.out.println("역직렬화 잘 끝");
    } catch (Exception e) {
        e.printStackTrace();
    }
}

 

7. File

  • 기본적이면서도 가장 많이 사용되는 입출력 대상
  • File class를 통해서 파일과 디렉토리를 다룰 수 있도록 한다.
  • File 인스턴스는 file일수도 있고 directory가 될 수도 있다.

 

  • 생성자, 메서드
// 생성자
// fileName을 이름으로 갖는 file을 위한 File instance 생성
// directory도 동일한 방법으로 생성
// fileName은 주로 path를 포함해서 지정해준다.
// path 지정 안할 시, default 위치에 생성
File(String fileName) // 위에 작성
File(String pathName, String fileName) // path와 파일명 분리해서 지정
File(File pathName, String fileName) // File 인스턴스로 된 path와 파일명 분리해서 지정
File(URI uri) // 지정된 uri로 파일 생성

// method
String getName() // 파일이름 String으로 반환
String getPath() // 파일경로 String으로 반환

String getAbsolutePath() // 파일절대경로 String으로 반환
File getAbsoluteFile()   // 파일절대경로 File으로 반환

String getParent()   // 파일 조상 directory String으로 반환
File getParentFile() // 파일 조상 directory File로 반환

String getCanonicalPath() // 파일의 정규경로를 String으로 반환
File getCanonicalFile()   // 파일의 정규경로를 File로 반환


// 멤버변수
static String pathSeparator // os에서 사용하는 경로 구분자
static char pathSeparator   // os에서 사용하는 경로 구분자

static String separator // os에서 사용하는 이름 구분자
static char separator   // os에서 사용하는 이름 구분자

 

  • 예제에 나온 추가 method
    1. System.getProperty("user.dir"));  // 현재 프로그램이 실행중인 directory
    2. createNewFile(); // 새 파일 생성
    3. mkdir(); // 새 디렉토리 생성
    4. listFiles(); // 해당 dir 내부 dir, 파일들 File[]로 추출하기
    5. delete(File, 삭제조건); // 삭제
    6. renameTo(File f, 변경할 파일명); // 파일 이름 변경
// file 생성자 사용방법
// 기존에 존재하는 파일을 참조만 하는 것
// 파일 생성하지 않음
File f = new File("/Users/유저명/Documents/javaSpring/untitled/src/practice","Try3.java");

File dir = new File("/Users/유저명/Documents/javaSpring/untitled/src/practice");
File f = new File(dir, "Try3.java");

 

// 새로운 파일 생성
File f = new File("/Users/유저명/Documents/javaSpring/untitled/src/practice", "NewFile.java");
f.createNewFile(); // 새 file 생성

// 새로운 directory 생성
File dir = new File("/Users/유저명/Documents/javaSpring/untitled/src","newDirectory");
dir.mkdir(); // 새 directory 생성

 

  • 예제1
public class Try3 {
    public static void main(String[] args) throws IOException {
        File f = new File("/Users/유저명/Documents/javaSpring/untitled/src/practice/Try3.java");
        String fileName = f.getName();
        int pos = fileName.lastIndexOf(".");

        System.out.println("f.getName() = " + f.getName());
        System.out.println("fileName.substring(0,pos) = " + fileName.substring(0, pos));
        System.out.println("fileName.substring(pos+1) = " + fileName.substring(pos + 1));

        System.out.println("f.getPath() = " + f.getPath());
        System.out.println("f.getAbsolutePath() = " + f.getAbsolutePath());
        System.out.println("f.getCanonicalPath() = " + f.getCanonicalPath());
        System.out.println("f.getParent() = " + f.getParent());

        System.out.println("System.getProperty(\"user.dir\") = " + System.getProperty("user.dir")); // 현재 프로그램이 실행중인 directory

        System.out.println("File.pathSeparator = " + File.pathSeparator);
        System.out.println("File.pathSeparatorChar = " + File.pathSeparatorChar);
        System.out.println("File.separator = " + File.separator);
        System.out.println("File.separatorChar = " + File.separatorChar);
        
    }
}

 

  • 예제2 - 파일과 디렉토리 목록 보여주기
public class Try3 {
    public static void main(String[] args) throws IOException {
        if (args.length != 1) {
            System.out.println("usage : my package");
            System.exit(0);
        }

        File f = new File(args[0]);

        if (!f.exists() || !f.isDirectory()) {
            System.out.println("유효하지 않은 디렉토리");
            System.exit(0);
        }

        File[] files = f.listFiles();

        for (int i = 0; i < files.length; i++) {
            String fileName = files[i].getName();
            System.out.println(files[i].isDirectory() ? "[" + fileName + "]" : fileName);
        }
    }
}

 

 

 

이전 발행글 : 2023.03.24 - [java/java 기초] - 14. 람다와 스트림