저번 시간에 InputStream으로 파일을 읽는 것에 대해서 배웠습니다.

 


 다시 1.txt를 보겠습니다. 이번에는 한글이 포함된 무언가가 있습니다. 이것은 UTF-8로 인코딩 되어 있습니다.

 

 

 제가 저번에 작성했던 이 프로그램은, 의도대로 잘 동작할까요? 인풋의 크기에 비해서 버퍼의 크기가 상대적으로 크니 제대로 동작하는 것처럼 보입니다. b의 배열 크기를 5로 줄여보도록 하겠습니다.

 

 

 이 프로그램을 실행시켜 보겠습니다.

 

 

 그러면 글자가 깨져서 나옵니다. 이는, 2byte나 3byte 길이의 데이터를 읽다가 끊었기 때문입니다. 어떻게 읽어들였는지, 헥사로 출력해 보도록 하겠습니다.

 

 

 요렇게 바꿔 보겠습니다. Integer.toHexString은, int 값을 Hexa로 출력해 주는 겁니다. 우리는 byte 값을 출력할 것이므로 0xff를 and 연산을 시켜 주겠습니다.

 

 

 그러면 이런 식으로 찍히는데요. 여기서, ea b0 80은 '가'입니다. 여기서 문제. 80 ed 9d ac d 이 부분이 UTF8로 인코딩 되었다면 어떻게 읽어들여야 할까요? byte 단위로는 제대로 읽어왔는데, 그게 끝입니다. 이 상태에서 디코딩을 한다고 한들, 제대로 된 값이 들어오지 않으니 깨질 수 밖에 없습니다.

 

 


 그러면 어떻게 해야 할까요? 우리가 일일히 디코드 해야 할까요? 물론 해도 됩니다. 단지 귀찮을 뿐입니다. 이 귀찮은 작업을 대신 처리해 주는 친구가 있습니다.

 

 바로 InputStreamReader입니다. byte stream에서, character stream으로 바꾸기 위한 다리 역할을 한다고 되어 있습니다. 더 읽어보면, byte를 읽어서, decode를 한다고 되어 있는데, 명시된 charset을 이용한다. 정도로 보시면 됩니다. 이것을 간단하게 그림으로 정리해 보면 아래와 같습니다.

 

 ec a1 b0이 들어왔습니다. 이것이 UTF-8로 인코딩 되었다면, UTF-8로 디코딩 하면 '조'가 됩니다. 즉, 요래 들어온 바이트 스트림을 UTF-8로 디코딩해서 char 스트림으로 변환하면 '조'가 됩니다.

 

 

 위 그림이 그것을 보여줍니다.

 

 

 이제 바뀐 코드를 보겠습니다. FileInputStream 객체 is를 한 번 더 wrap 해서 InputStreamReader ir을 생성하였습니다. 나머지는 크게 변한 건 없습니다. 단지, b 배열이 byte에서 char로 바뀐 것 빼고는 없습니다. 12번째 줄에서, Arrays.fill을 했는데요. 이는 b배열전체를 널 문자로 초기화 하기 위해서입니다.

 

 

 잘 동작하네요. 그러면 UTF-16LE로 인코딩 되어 있는 것을 읽어내려면 어떻게 해야 할까요? 일단 byte 단위로 읽어오는 건 별 문제가 없을 듯 합니다. InputStreamReader에는 디코더가 있다고 했는데, 그러면, charset을 받는 생성자도 있을 겁니다.

 

 

 찾아보니 있습니다. 그러면, UTF-16LE 같은 인코딩을 2번째 인자로 넣어주시면 됩니다.

 

 

  즉, 8번째 줄 처럼 charsetName을 2번째 인자로 넣어주면 디폴트 인코딩이 아닌 다른 인코딩으로, (예를 들자면 디폴트가 UTF-8이였는데 UTF-16LE로 저장된) 인코딩이 된 텍스트 파일도 읽어올 수 있습니다.

 

 

 1r.txt를 읽으니 잘 읽어지네요. 어떻게 byte로 들어온 것을 character 단위로 변환하는지는 코드 뜯는 카데고리에서 nio와 함께 언급할 날이 있을 듯 싶습니다.