자바] 파일과 인코딩

2022. 3. 22. 00:46프로그래밍

728x90

여전히 잘 모르겠다. 하지만 코딩은 할 수 있다 뭐 이런 얘기를 하고자 합니다.

나에게는 파일과 인코딩은 여전히 잘 모르는 분야 같아, 그냥 나만의 예제를 한 번 작성 해보고자 합니다.

 

이글을 쓰게 된 사건의 발단은

UNIX/LINUX 상에서 자바로 텍스트 파일, csv 파일에서 구분자 별로 글자들을 읽어서 데이터베이스에 저장하는 기능을
만들어 놓았는 데, 뭔가 한 번씩 오동작을 하고 있었으며, 정확하게는 인코딩 오류가 발생을 한다는 것이었습니다.

디버깅을 해 보았더니 다음 글자들을 읽어 들일 때마다 오류내고 있었죠.

쐑쐑버거
쉑쉑버거
쇅쇅버거
쑝쑝쑝
뚫훍뚤훍


글자들은 일반적으로 우리가 잘 쓰지 않는 것들이고 해당 글자의 범위도 일반적으로 쓰는 범위를 
넘어가서 버린 듯한 모습...

 

그래서 이런 실험을 해보고자 합니다.

우선 다음과 같이 해당 글자들을 메모장을 열고서 복사해 넣고 다음 세가지 인코딩 타입으로 저장 해 봅시다.

그런 다음 파일을 읽어 들이는 코드를 다음과 같이 작성을 하겠습니다. 일반적으로 파일을 읽어 들일 때, 나는 #BufferedReader 를 많이 쓰는 데, #readLine() 메서드가 해당 클래스에 존재 해서 입니다. 몇 줄 안되는 코드를 적어 보면 다음과 같습니다.

public static void main(String[] args)
{
	// ======UTF8========= //
	System.out.println("======UTF8=========");
	readFileFromUTF8Encoding();
	System.out.println("======UTF8BOM=========");
	readFileFromUTF8BOMEncoding();
	System.out.println("======Ansi=========");
	readFileFromAnsiEncoding();
}

private static void readFileFromAnsiEncoding() {
	BufferedReader rdr = null;
	
	try
	{
		rdr = new BufferedReader(
			new InputStreamReader(
				new FileInputStream(new File("C:\\DEV\\tmp\\encodingTest\\버거목록_ANSI.txt"))));
		String line = null;
		
		while((line = rdr.readLine()) != null)
		{
			System.out.println(line);
		}
	}
	catch(IOException ie)
	{
		
	}
	finally
	{
		if(rdr != null)
		{
			try
			{
				rdr.close();
			}catch(IOException ie) {}
		}
		
	}
}

private static void readFileFromUTF8BOMEncoding() {
	BufferedReader rdr = null;
	
	try
	{
		rdr = new BufferedReader(
			new InputStreamReader(
				new FileInputStream(new File("C:\\DEV\\tmp\\encodingTest\\버거목록_UTF-8(BOM).txt"))));
		String line = null;
		
		while((line = rdr.readLine()) != null)
		{
			System.out.println(line);
		}
	}
	catch(IOException ie)
	{
		
	}
	finally
	{
		if(rdr != null)
		{
			try
			{
				rdr.close();
			}catch(IOException ie) {}
		}
		
	}
}

private static void readFileFromUTF8Encoding() {
	BufferedReader rdr = null;
	
	try
	{
		rdr = new BufferedReader(
			new InputStreamReader(
				new FileInputStream(new File("C:\\DEV\\tmp\\encodingTest\\버거목록_UTF-8.txt"))));
		String line = null;
		
		while((line = rdr.readLine()) != null)
		{
			System.out.println(line);
		}
	}
	catch(IOException ie)
	{
		
	}
	finally
	{
		if(rdr != null)
		{
			try
			{
				rdr.close();
			}catch(IOException ie) {}
		}
		
	}
}

실행 코드도 그림이 놓인 순서대로 넣어 보았습니다. 여기서 BOM 은 Byte Order Mark 를 뜻합니다.

 

출력은 다음과 같이 된다.

======UTF8=========
?릲?릲踰꾧굅
?뎾?뎾踰꾧굅
?뇚?뇚踰꾧굅
======UTF8BOM=========
癤우릲?릲踰꾧굅
?뎾?뎾踰꾧굅
?뇚?뇚踰꾧굅
======Ansi=========
쐑쐑버거
쉑쉑버거
쇅쇅버거

자 그럼 위의 코드는 당연히 UTF-8 인코딩 인 경우 깨져서 나올 것을 예상 했고, Ansi 의 경우에는 찾아보니 code page 만 맞으면 해당 지역의 글자가 나오는 것으로 확인 했습니다.

 

문자열 인코딩 개념 정리(ASCII/ANSI/EUC-KR/CP949/UTF-8/UNICODE)

ASCII(American Standard Code for Information Interchange) ASCII는 최초의 문자열 인코...

blog.naver.com

 

일반적으로 파일을 인터페이스로 쓰는 경우에는 여러가지 복잡한 이유로 UTF-8 을 선호하는 경향이 있으니, 위의 UTF-8 인코딩은 바로 잡아 봅시다.

readFileFromUTF8Encoding2 라는 메서드를 다음과 같이 만들고서,

private static void readFileFromUTF8Encoding2() {
	DataInputStream din = null;
	
	try
	{
		din = new DataInputStream(
			new BufferedInputStream(
				new FileInputStream(
                new File("C:\\DEV\\tmp\\encodingTest\\버거목록_UTF-8.txt")
                )
			)	
		);
		
		byte[] buf = new byte[1024];
		StringBuffer stbuf = new StringBuffer();
		
		while(din.read(buf, 0, 1024) != -1)
		{
			stbuf.append(new String(buf, "UTF-8").trim()).append("\r\n");
		}
		
		System.out.println(stbuf.toString());
	}
	catch(IOException ie)
	{
		
	}
	finally
	{
		if(din != null)
		{
			try
			{
				din.close();
			}catch(IOException ie) {}
		}
		
	}
}

다음 코드를 메인에다 추가해 줍니다.

public static void main(String[] args)
{
	... 생략 ...
    System.out.println("======UTF82=========");
	readFileFromUTF8Encoding2();
}

결과는?

======UTF8=========
?릲?릲踰꾧굅
?뎾?뎾踰꾧굅
?뇚?뇚踰꾧굅
?몵?몵?몵
?슟?썚?슕?썚
======UTF8BOM=========
癤우릲?릲踰꾧굅
?뎾?뎾踰꾧굅
?뇚?뇚踰꾧굅
?몵?몵?몵
======Ansi=========
쐑쐑버거
쉑쉑버거
쇅쇅버거
쑝쑝쑝
뚫훍뚤훍
======UTF82=========
쐑쐑버거
쉑쉑버거
쇅쇅버거
쑝쑝쑝
뚫훍뚤훍

느낌 만으로 보자면 사실 다음 코드가 더 어울려 보이는 데...

while(din.read(buf, 0, 1024) != -1)
{
	stbuf.append(new String(buf, "EUC-KR").trim()).append("\r\n");
}

자 그런 느낌으로 보자면 Ansi 문자열은 다음과 같은 코드가 작성이 되겠죠.

private static void readFileFromAnsiEncoding2() {
	DataInputStream din = null;
	
	try
	{
		din = new DataInputStream(
			new BufferedInputStream(
				new FileInputStream(new File("C:\\DEV\\tmp\\encodingTest\\버거목록_ANSI.txt"))
			)
		);
		
		byte[] buf = new byte[1024];
		StringBuffer stbuf = new StringBuffer();
		
		while(din.read(buf, 0, 1024) != -1)
		{
			stbuf.append(new String(buf, "EUC-KR").trim()).append("\r\n");
		}
		
		System.out.println(stbuf.toString());
	}
	catch(IOException ie)
	{
		
	}
	finally
	{
		if(din != null)
		{
			try
			{
				din.close();
			}catch(IOException ie) {}
		}
		
	}
	
	
}

출력 결과를 확인해 보면...

======UTF8=========
?릲?릲踰꾧굅
?뎾?뎾踰꾧굅
?뇚?뇚踰꾧굅
?몵?몵?몵
?슟?썚?슕?썚
======UTF8BOM=========
癤우릲?릲踰꾧굅
?뎾?뎾踰꾧굅
?뇚?뇚踰꾧굅
?몵?몵?몵
======Ansi=========
쐑쐑버거
쉑쉑버거
쇅쇅버거
쑝쑝쑝
뚫훍뚤훍
======UTF82=========
쐑쐑버거
쉑쉑버거
쇅쇅버거
쑝쑝쑝
뚫훍뚤훍

======Ansi2=========
?q?q버거
쉑쉑버거
??많側?
????
뚫?a뚤?a

위의 출력 결과의 작은 결론은

내 운영체제인 윈도우 11 에서 메모장에 작성한 한글 파일을 Ansi 로 저장 한다면,
MS949 로 인코딩 되어 저장 된다.

라는 결론에 도달하게 됩니다.

 

위의 코드는 다음과 같이 수정하면 정상적인 출력이 나옵니다.

while(din.read(buf, 0, 1024) != -1)
{
	stbuf.append(new String(buf, "MS949").trim()).append("\r\n");
}

그럼, 이 글의 첫 머리에서 시작했던 오류에 대한 의문점에 대한 답이 무엇일까요?

 

아래 글자들은 EUC-KR(완성형) 범위를 벗어 나는 것들이어서 "MS949"로 다시 디코딩을 해 주어야 한다는 것이고,

읽어 들일때는 대상 시스템의 인터페이스가 파일을 저장 할 때 어떤 인코딩 방식으로 저장 했느냐에 따라서, UTF-8 혹은 EUC-KR, MS949 등으로 로 읽어 들여야 할 것이다. 라는 것이죠.

쐑쐑버거
쇅쇅버거
쑝쑝쑝
뚫훍뚤훍

 

이상.

728x90