brunch

You can make anything
by writing

C.S.Lewis

JAVA I.O. - 파일 읽기

* 이 내용은 철저히 초심자를 위해 알기 쉽게 설명하는 것을 목적으로 하고 있습니다.

* 더 정확하고 자세한 개념은 다른 고수님들의 글들을 참고하시길 바랍니다.

* 그리고 이 글에서는 코드 최소한으로 다루고 있습니다.


우리가 그동안 배웠던 방식은 모두 JAVA 안에서 일어나는 어떤 계산일 뿐이지 이것을 JAVA 외부로 내보낸 적은 없습니다. 여기서 외부로 내보낸다는 것은 무엇을 의미할까요? 


모니터를 통해 글자나 이미지를 보여주거나, 파일 등으로 만들어 저장하는 것을 의미합니다. 


반대로 키보드를 통해 문자 또는 숫자를 JAVA 안으로 입력하거나, 파일이나 이미지를 JAVA 안으로 전송할 수 있습니다.


이러한 행위들은 JAVA를 기준으로  무언가 들어오고(In) 나간다(Out) 고 하여, JAVA I.O.라고 부릅니다.


그리고 이렇게 파일이나 문자 등이 자바 안으로 들어오고 나가게 해 주는 흐름(stream)을 

JAVA 내부로 들어오는 흐름인 Input Stream, 그리고 JAVA 밖으로 내보내는 흐름인 Output Stream으로 부릅니다.


당연히 이 Stream을 우리가 직접 개발하는 것은 상당히 어려운 일일 겁니다. 그래서 자바에서 Stream이라는 이름의 클래스로 제공하고 있습니다. 그리고 이 Stream 클래스는 기본적으로 바이트단위로 데이터를 주고받습니다.


프로그래밍 세계에서 바이트는 기본 단위이기 때문에 문자도 이동이 가능합니다. 하지만 안타깝게도 한글이나 기타 문자 들은 2바이트나 3바이트이기에 전송 시 깨짐이 발생하기도 합니다. 하지만 걱정하지 마세요, 자바에서는 문자를 전문으로 하는 클래스도 제공해 주니까요. 바로 읽어 들이는 Reader와 외부로 써내는 Writer 가 그것입니다. 


그리고 이 부모를 상속한 자식클래스들도 존재합니다.

읽어 들이고 내보내는 기능은 같으나 특정 환경이나 상황에 더 적합한 클래스들이라고 이해하시면 될 것 같습니다. 그래서 이 친구들의 이름을 가만히 살펴보면 OOOInputStream, XXXOutputStream 등으로 되어있습니다. 누구의 자식인지 알아보기 쉽게 하기 위한 작명인 것이지요.


File

우리가 JAVA I.O. 에서 무언가를 읽고 내보낼 때 가장 많이 사용하는 방식은 바로 File 일 것입니다.

우리가 어떤 프로그램에서 무언가를 하고 나서 저장하면 파일로 저장이 되고, 지난 작업을 불러올 때도 파일을 통해 읽어 들이는 것만 생각해 봐도 얼마나 파일을 자주, 그리고 유용하게 쓰는지 알 수 있습니다.

이런 파일을 다루기 위한 클래스도 자바에서는 물론 제공해 주고 있으니, 바로 File 클래스입니다.

특정한 경로를 넣어 파일 객체를 생성하면 해당 경로의 폴더나 파일을 생성하거나 삭제도 가능합니다.


파일 읽기

지금까지 알아본 내용으로 파일을 읽어 시스템에 출력하는 과정을 살펴보겠습니다. 우선 C:/ 에 file.txt라는 파일 안에 오늘의 기사가 있다고 가정해 봅시다. 그리고 파일 안의 기사를 읽어서 자바 안으로 가져 올의 절차는 이렇습니다.

1. 읽어올 파일의 위치를 확인시켜 줍니다.

2. 자바 안으로 읽어올 스트림을 준비합니다.

3. 스트림을 통해 흘러 들어오는 데이터를 자바가 읽어옵니다.

4. 읽어온 내용을 모니터로 출력해 줍니다.

이 순서를 잘 기억하세요!


그리고 이것을 코드로 표현해 보겠습니다. 물론 우리는 코드를 아는 게 목적이 아니므로 대충 ‘이런 거 구나 ‘하는 감만 가져가는 겁니다.

1번에서 파일의 위치를 지정하여 File 객체를 생성했습니다. 이제 file 변수 안에는 해당 경로의 파일의 정보를 담고 있게 됩니다. 

그리고 2번에서 읽어 들일 스트림을 준비합니다. 그런데 우리가 읽을 파일은 텍스트 파일 이므로 읽기에 특화된 Reader를 사용할 것입니다. 그런데 파일을 읽을 것이므로 파일 읽기에 특화된 FileReader를 준비합니다.


이 부분은 다소 복잡해 보일 수 있습니다.

우선 3-1부터 보겠습니다. 앞에서 선언한 FileReader를 담은 reader로부터 read() 메서드를 사용하여 한 글자씩 읽어 옵니다. 이때 읽어오는 것은 10진수 아스키코드가 나오게 됩니다. 그리고 더 이상 읽을 내용이 없다면 -1이 나타나게 됩니다. 그래서 data 가 -1 이 아니라면 while 문에 의해서 아래 내용(3-1, 3-2)이 계속 실행됩니다.

3-2는 아스키코드가 담긴 data를 문자인 char 형태로  형변환 하여 담습니다.

그리고 담은 문자는 말 그대로 한 글자 이므로 3-3에서 StringBuffer에 하나씩 넣어서 합해 줍니다.

왜 StringBuffer를 사용했는지는, 문자와 문자열 부분을 다시 보고 오시면 이해가 될 겁니다.


마지막으로 buffer에 담긴 내용을 문자열로 만들어서 화면으로 내보냅니다.

이때 사용한 System.out.println(); 은 매개변수에 넣은 값을 시스템(Syetem)의 밖으로(out) 출력(print) 한다는 뜻입니다. 그런데 print()가 아닌 println(); 를 사용한 이유는 ln 이 줄 바꿈의 의미가 있어, 줄 바꿈을 하면서 출력해 주기 위해서입니다.


이렇게 특정 파일을 읽어와 시스템에 출력까지 해 봤는데요, 아직 끝나지 않았습니다. 사용한 스트림(파이프)은 반납을 해줘야 하기 때문이죠. 이 스트림은 시스템의 소중한 자원이기에 내가 반납하지 않는다면 계속해서 만들어야 하므로 자원이 낭비될 수밖에 없기 때문입니다.


그래서 사용한 스트림은 reader는 close() 메서드를 통해 자원을 반납해 줘야 합니다.


마지막으로 전체 코드를 다시 한번 보겠습니다.

앞에서 언급했듯이 코드 하나하나를 해석하기 위해 너무 애쓰지 마세요.

가장 중요한 것은 우리가 파일을 읽어서 시스템에 출력하기까지의 순서와 절차입니다.

각 번호에 맞춰서 무엇을 하는지를 떠올려 봅시다.

그리고 다음 시간에는 반대로 시스템 내부의 문자를 파일로 저장하는 방법에 대해서 알아보도록 하겠습니다.

매거진의 이전글 컬렉션프레임워크 - FILO, FIFO
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari