I/O

  • Java
  • 2021년 2월 9일

컴퓨터의 5대 기능인 입력/출력/연산/저장/제어 중 입력(Input)과 출력(Ouput)을 줄여 I/O라고 말한다.


1. 스트림 / 버퍼 / 채널 기반의 I/O

1) 스트림

입출력을 도와주는 모듈로써 Stream이라는 단어 그대로 흐름을 의미하며, 한 방향으로만 진행하는 단방향통신이다.

2) 버퍼

일종의 데이터 공간으로 메모리간, 컴퓨터와 사용자간의 속도차이로 생기는 병목현상을 줄이기 위한 공간

데이터를 쌓아두고 한번에 찾을 수 있는 데이터 공간으로 이를 이용하여 데이터를 입출력하는 방식으로 속도향상을 꾀할 수 있다.

3) 채널

스트림과 다르게 양방향 통신이 가능한 방법으로 non-blocking/비동기 방식 모두 지원한다.

버퍼를 통해 데이터를 읽고 쓴다.

Selector

socket과 채널이 연결되어 클라이언트 연결시 스레드를 생성하는 대신 하나의 selector에 여러개의 채널을 생성하여 다수의 클라이언트를 대응할 수 있다.



2. InputStream과 OutputStream

Stream기반의 클래스의 최상위 클래스(추상 클래스) 이다.

바이트 스트림들이 갖는 메서드가 다음과 같이 존재한다.

1) InputStream

메서드

메서드기능
read()입력 스트림으로부터 1바이트를 읽고 읽은 바이트를 리턴한다.
read(byte[] b)입력 스트림으로부터 읽은 바이트들을 매개값으로 주어진 바이트 배열b에 저장하고 실제로 읽은 바이트 수를 리턴한다.
read(byte[] b, int off, int len)입력 스트림으로부터 len개의 바이트만큼 읽고 매개값으로 주어진 바이트 배열 b[off]부터 len개까지 저장한다. 그리고 실제로 읽은 바이트 수인 len개를 리턴한다. 만약 len개를 모두 읽지 못하면 실제로 읽은 바이트 수를 리턴한다.
close()사용한 시스템 자원을 반납하고 입력스트림을 닫는다.

2) OutputStream

메서드

메서드기능
write(int b)출력 스트림으로 부터 1바이트를 보낸다.
write(byte[ ] b)출력 스트림으로부터 주어진 바이트 배열 b의 모든 바이트를 보낸다.
write(byte[ ] b, int off, int len)출력 스트림으로 주어진 바이트 배열 b[off]부터 len개까지의 바이트를 보낸다.
flush()버퍼에 잔류하는 모든 바이트를 출력한다.
close()사용한 시스템 자원을 반납하고 출력 스트림을 닫는다.



3. Byte와 Character 스트림

1) Byte Stream

데이터를 Byte단위로 전송하며 바이트로 구성된 파일인 오디오,이미지,동영상 등을 읽고 쓰는데 적합한 스트림이다.

Input 종류

클래스기능
AudioInputStream오디오 포맷에 특화된 프레임 단위 스트림 입력
ByteArrayInputStream바이트 배열을 바이트 스트림으로 변환 입력
BufferedInputStream버퍼를 이용한 바이트 스트림 입력
FileInputStream파일을 바이트 단위로 읽어들여 바이트 스트림 입력
FilterInputStream버퍼와 같은 필터에 의한 바이트 스트림 입력
InputStream바이트 스트림의 입력을 위한 추상 클래스
ObjectInputStream자바 객체를 직렬화 시켜 읽어들여 스트림으로 변환
PipedInputStream바이트 스트림을 읽어들여 연결된 PipedOutputStream으로 동시에 전달
SequenceInputStream서로 다른 InpustStream을 순차적으로 입력하기 위한 클래스
StringBufferInputStream문자열 스트림 입력을 위한 클래스, JDK 1.1 이후 StreamReader 클래스로 대체되었다.

Output 종류

클래스기능
ByteArrayOutputStream바이트 스트림을 바이트 배열로 출력
FileOutputStream바이트 스트림을 바이트 파일로 출력
FilterOutputStream버퍼와 같은 필터가 추가된 바이트 스트림 출력을 위한 추상 클래스
ObjectOutputStream바이트 스트림을 직렬화된 객체 형식으로 출력
OutputStream바이트 출력 스트림을 위한 추상 클래스
PipedOutputStreamPipedInputStream의 입력 스트림을 출력
void byteArrayTest(){
        byte[] input = {0,1,2,3,4,5,6,7,8,9};
        byte[] output = null;

        byte[] temp = new byte[4];

        ByteArrayInputStream inputStream = new ByteArrayInputStream(input);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        int len;
        try {
            while ((len = inputStream.read(temp)) != -1){
                outputStream.write(temp,0,len); //read함수를 통해 읽은 데이터를 outputStream에 write
            }

            output = outputStream.toByteArray();  //OutputStream의 데이터를 output배열에 저장
            System.out.println("output : "+ Arrays.toString(output));

            inputStream.close();
            outputStream.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

inputStream의 read함수가 더이상 데이터를 읽지 못하면 -1을 리턴하는 것을 이용해서 while문을 통해 inputStream을 끝까지 읽을 수 있으며, ByteArray~ 스트림을 이용하여 4바이트 단위로 데이터를 읽고 쓸 수도 있다.


2) Char Stream

자바는 char형도 2바이트이기 때문에 바이트단위 스트림은 문자열(text)를 전송하는데 적합하지 않을 수 있기 때문에 문자 기반의 스트림을 제공한다.

클래스명도 뒤에 Stream이 아닌 Reader를 붙여 사용한다.

Input 종류 (Reader)

클래스기능
BufferedReader버퍼를 이용한 문자 스트림 입력
CharArrayReader문자 배열의 입력
FileReader파일을 문자 스트림으로 변환해 입력
FilterReader버퍼와 같은 필터에 의한 문자 스트림 입력
InputStreamReader바이트 스트림을 문자 스트림으로 변환
LineNumberReader버퍼를 이용한 문자 스트림 입력, 라인번호 저장
PipedReader문자 스트림을 읽어들여 연결된 PipedWriter로 동시에 전달
Reader바이트 입력 스트림을 문자 스트림으로 변환하기 위한 추상 클래스
StringReader문자열 데이터를 문자 스트림으로 입력

Output 종류 (Writer)

클래스기능
BufferedWriter문자 스트림을 버퍼를 이용해 문자열 단위로 출력
CharArrayWriter문자 스트림을 문자 배열 단위로 출력
FilterWriter버퍼와 같은 필터가 추가된 문자 스트림 출력을 위한 추상 클래스
OutputStreamWriter문자 스트림을 바이트 스트림으로 변환 출력
PipedWriterPipedReader에서 전달받은 문자 스트림을 바로 출력
PrintWriter형식이 있는 Writer 객체를 문자 스트림으로 출력
StringWriter문자 스트림을 문자열 데이터로 출력



4. 표준 스트림

표준 입출력 장치(콘솔)에 입출력을 위해 자바에서 제공하는 스트림이다.

java.lang패키지에 System 클래스를 통해 제공한다.

변수입출력기능
System.inInput콘솔의 데이터를 입력받음
System.outOutput콘솔에 데이터를 출력
System.errOutput콘솔에 에러를 출력

1) 키보드문자 입력 방법

Scanner 이용

Scanner scanner = new Scanner(System.in);

Scanner 클래스를 통해 표준입출력장치로 부터 데이터를 받아올 수 있으며 next(), nextLine(), nextByte(), nextDouble…과 같은 메서드를 통해 읽어 올 수 있다.


System.in.read() 이용

System클래스에서 제공하는 read함수를 이용하여 입력을 받을 수 있으며, 1byte씩만 데이터를 읽어 ascii코드에 해당하는 int형을 리턴하는 함수이다.


BufferedReader 이용

한문자씩 스트림에서 읽어오는 InputStreamReader를 버퍼를 사용해서 문자열 처리를 편하게 해주는 방법이 있다.

BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));

String str = bufferedReader.readLine();

bufferedReader.close();

Scanner처럼 데이터 타입에 맞게 파싱해주는 메소드를 따로 제공하지 않기 때문에, Intger.parseInt()와 같은 메서드를 이용해서 형변환을 해주어야 하고 Scanner보다는 빠른 입력처리가 가능하다.


2) 데이터 출력 방법

모두가 잘 아는 System.out.println(), System.out.print() 등과 같이 System의 메서드를 이용하는 방법이 있는데, 이를 남발하면 시스템 성능 저하의 원인이 될 수 있다.

다른 방법으로는 BufferReader와 같은 방법으로 BufferedWriter를 이용한 방법이 있다.

BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));

bufferedWriter.write("Hello\n");

bufferedWriter.close();

println() 처럼 자동으로 줄바꿈을 해주는 메서드는 따로 존재하지 않기 때문에 개행문자 (\n)를 직접 입력해주어야 하고, IOException의 예외처리를 해주어야 한다.



5. 파일 읽고 쓰기

1) 바이트 기반 스트림 (FileStream)

FileInputStream, FileOutputStream을 이용한 방법

void FileOutputStreamTest(){
        FileOutputStream fileOutputStream = null;
        FileInputStream fileInputStream = null;
        try{
            fileOutputStream = new FileOutputStream("src/test/IoTest/outputFile");
            for(char ch='a'; ch <='z'; ch++){
                fileOutputStream.write(ch);
            }

            File file = new File("src/test/IoTest/outputFile");
            fileInputStream = new FileInputStream(file);

            int c;
            while((c = fileInputStream.read()) != -1){
                System.out.print((char) c);
            }

            fileInputStream.close();
            fileOutputStream.close();
        }catch (FileNotFoundException e){
            System.out.println("Not Found file");
        }catch (IOException e){
            System.out.println("IO Exception");
        }
    }

2) 문자기반 스트림

FileReader와 FileWriter를 이용하여 사용하는 방법

    void fileWriterTest(){
        FileWriter fileWriter =null;
        FileReader fileReader = null;
        try{
            fileWriter = new FileWriter("src/test/IoTest/fileWriter.txt");

            for(int i=0; i < 10; i++){
                fileWriter.write(i);
            }
            fileWriter.close();

            fileReader = new FileReader("src/test/IoTest/fileWriter.txt");
            int c;
            while((c = fileReader.read()) != -1){
                System.out.print(c);
            }
            fileReader.close();

        }catch (IOException e){
            e.printStackTrace();
        }
    }

3) 차이점

FileInputStream/FileOutputStream은 InputStream/OutputStream을 상속받고 있고 FileReader/FileWriter은 Reader/Writer를 상속받고 있다.

때문에, FileReader/Writer은 바이트를 문자로 변환하여 입출력을 처리하고 FileInput/OutputStream은 1바이트 이상인 한글등을 처리하기 위해서 버퍼를 사용해서 처리해야 한다.

파일 포인터의 시작 포인트가 다른데 byte방식은 outputStream을 close안해도 input에서 읽을 수 있고 char방식은 close를 안하면 reader로 읽을 수가 없다.

void FileOutputStreamTest(){
        FileOutputStream fileOutputStream = null;
        FileInputStream fileInputStream = null;
        try{
            fileOutputStream = new FileOutputStream("src/test/IoTest/outputFile.txt");
            for(int i=0; i< 10; i++){
                fileOutputStream.write(i);
            }

            fileInputStream = new FileInputStream("src/test/IoTest/outputFile.txt");
            int c;
            while((c = fileInputStream.read()) != -1){
                System.out.print(c);
            }

            fileInputStream.close();
            fileOutputStream.close();
        }catch (FileNotFoundException e){
            System.out.println("Not Found file");
        }catch (IOException e){
            System.out.println("IO Exception");
        }
    }

byte방식은 FileOutputStream으로 쓰고나서 close하지 않은 파일을 FileInputStream으로 처음부터 읽는게 가능하다.


void fileWriterTest(){
        FileWriter fileWriter =null;
        FileReader fileReader = null;
        try{
            fileWriter = new FileWriter("src/test/IoTest/fileWriter.txt");
            for(int i=0; i < 10; i++){
                fileWriter.write(i);
            }

            fileReader = new FileReader("src/test/IoTest/fileWriter.txt");
            int c;
            while((c = fileReader.read()) != -1){
                System.out.print(c);
            }
            fileWriter.close();
            fileReader.close();

        }catch (IOException e){
            e.printStackTrace();
        }
    }

char방식인 FileReader은 FileWriter로 작성후에 close하지 않은 파일을 읽으려고 하면 파일포인터가 끝을 가리키기 때문에 읽히지 않는다.



6. 인코딩

byte단위로 입출력하는 FileInputStream/FileOutputStream에서 아스키코드가 아닌 그 외 문자들을 제대로 출력하기 위한 인코딩을 설정해주어야한다.

Reader의 클래스중 하나인 Input/OutputStreamReader를 이용할 수 있다.

1) InputStreamReader / OutputStreamReader

파일의 인코딩 방식을 지정할 수 있고, 바이트입력 스트림에 연결되어 문자입력스트림인 Reader로 변환시킨다.

void encodingTest(){
        try {
            FileOutputStream fileOutputStream = new FileOutputStream("src/test/IoTest/encoding.txt");
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream,"utf-8");
            outputStreamWriter.write("안녕하세요");

            outputStreamWriter.close();
            fileOutputStream.close();

            FileInputStream fileInputStream = new FileInputStream("src/test/IoTest/encoding.txt");
            InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream,"utf-8");
            int c;
            while((c = inputStreamReader.read()) != -1){
                System.out.print((char) c);
            }

            inputStreamReader.close();
            fileInputStream.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }





Reference

https://velog.io/@godkimchichi/Java-7-IO-File

https://hyeonstorage.tistory.com/248

Tags :

Related Posts

블로그 첫 시작 👋

블로그 첫 시작 👋

우선, 나는 말수가 적은 편이다. 말을 적게한다고 생각을 안하는 것이 아니라 머리속에서 생각은 정말 많으나, 그것을 입밖으로 꺼낼때 정리가 되지 않아 버벅거리기도 하고, 조사를 잘못 선택하여 말을 하거나 말을 하는 도중에 머리속이 꼬여 중간에 말을 멈추기도 한다. 말은 생각을 언어로 바꾼 것이기 때문에 많은 생각들을 정리하는 연습과 맞춤법, 올바른 조사사용 등 올바른 언어 습관을 글쓰기를 통해 얻고자 글쓰기를 시작하기로 했다....

Read More
[WSL2] Mysql 자동실행 설정하기

[WSL2] Mysql 자동실행 설정하기

[wsl2 설치가 안된 분은 여기를 참고하자 ] 나는 이번년도에 wsl2를 알게 되어 wsl2을 통해 편하게 개발을 진행하였다. 그런데, wsl2가 아직 여럿 불편한 점이 있었고 그 중 하나가 wsl2의 자동 실행 미지원이다. wsl2는 컴퓨터 시작시 자동 실행을 지원하지 않아 수동으로 ssh나 mysql, nginx와 같은 프로그램들을 실행시켜줘야하는 불편함이 있었다....

Read More
Spring boot와 EK스택 연결하기

Spring boot와 EK스택 연결하기

현재 진행하고있는 spring boot 프로젝트에 쿼리 통계를 위해 ElasticSearch와 Kibana를 도입하기로 했고 이에 맞게 로깅방법이나 세팅 방법을 기록하기 위해 글을 작성하게 됬다. diagram 구성은 다음과 같은데 서버로 EC2 프리티어를 사용하고 있어 사양이 안좋기 때문에 서버 내부에 logstash없이 filebeat를 이용하여 로그를 전송하기로 했다. ELK스택으로는 Elastic Cloud 프리티어를 이용해 빠르게 구축하는 것을 테스트해볼 생각이다....

Read More