일반 UTF-8을 위한 문자열 인코딩처리 관련

황제낙엽 2006.10.06 18:14 조회 수 : 881 추천:188

sitelink1 http://webapp2.net/tt/19?TSSESSION=5dd5e...7b6e7dbcb8 
sitelink2  
sitelink3 http://1 
sitelink4 http://ko 
sitelink5  
sitelink6 http://sitelink1 
낮에 파일에서 읽은 인코딩과 데이터베이스로 보내는 스트림의 인코딩이 달라서 고생을 좀 했습니다. 편법을 쓰면 금방 해결하는 일이었지만, 쩝... 오기가 생긴다거나 제대로 해야겠다는 것도 아니지만...^^; (어차피 모든 것을 다 알수는 없으니까요)
아무튼, 그동안 뒷전으로 미루었던 것들에 대해 처음부터 다시 한다는 기분으로 살펴보는 것도 좋겠다 싶더군요. 그래서 일단 코드를 짜봅니다.
먼저 파일 네 개를 인코딩이 전부 다르게 해서 만들었습니다. 데이터는 별 뜻 없이 넣었구요.
one, two, three and to the four
1234%^&*(||+)
거추장스러운 허식을 벗고 나면 무엇이 남을까?
난로는 그냥 쓰시고, 동아일보는 봄부터
영어, 숫자, 특수문자와 한글을 혼용해서 넣었는데, 생각해보니 ISO8859-1에 한글을 넣은 것은 좀 어처구니 없는 것 같기도 하지만, 일단 똑같이 데이터를 넣고 아래 코드를 실행해 봤습니다.
// 1. ms949, ISO-8859-1, UTF-8, UTF-16 포맷의 파일에서 읽은 문자열의 길이 비교

// 1.1 각 파일 데이터를 String으로 읽기
String files[] = {"ms949.txt", "iso8859-1.txt", "utf-8.txt", "utf-16.txt"};
String data[] = new String[4];

for(int i = 0; i < files.length; i++){
  BufferedReader reader = new BufferedReader(
    new FileReader(files[i]));
 
  data[i] = ""; // 초기화 하지 않으면, 반복문에서 null 이라는 글자가 추가됨
  String line;
  while((line = reader.readLine()) != null)
   data[i] += line + "/n";
 
  reader.close();
}

// 1.2 String 데이터 길이 비교
for(int i = 0; i < data.length; i++){
  System.out.println(files[i] + " " + data[i].length());
  System.out.println(data[i]);
}
 
일단 콘솔 출력은 다음과 같이 나옵니다. 유니코드는 엽기로 출력됩니다. 브라우저에서 많이들 보셨겠지만 ^^;
유심히 살펴볼 것은 String으로 읽은 값의 길이가 다르다는 점입니다.
ms949.txt 98
one, two, three and to the four/n1234%^&*(||+)/n거추장스러운 허식을 벗고 나면 무엇이 남을까?/n난로는 그냥 쓰시고, 동아일보는 봄부터/n
iso8859-1.txt 98
one, two, three and to the four/n1234%^&*(||+)/n?????? ??? ?? ?? ??? ????/n??? ?? ???, ????? ???/n
utf-8.txt 127
one, two, three and to the four/n1234%^&*(||+)/n嫄곗텛?옣?뒪?윭?슫 ?뿀?떇?쓣 踰쀪퀬 ?굹硫? 臾댁뾿?씠 ?궓?쓣源??/n?궃濡쒕뒗 洹몃깷 ?벐?떆怨?, ?룞?븘?씪蹂대뒗 遊꾨???꽣/n
utf-16.txt 178
??
인코딩 String 길이 파일 크기(bytes)
MS949 98 131
ISO-8859-1 98 96
UTF-8 127 166
UTF-16 178 194
 
글쎄요.. 인코딩에 대해서 본격적으로 다뤄보기 전에 일단 문제의 발단부터 해결까지의 과정을 먼저 말씀드려야겠네요. 우선, 4메가(4351960) 정도 되는 우편번호 데이터를 넣는 sql 파일이 있습니다. MS949로 인코딩된 sql 파일을 그대로 실행시켜서, 데이터베이스에 넣었더니, 자바 클래스에서 비교를 하니까 다 깨지더군요. 그래서 아예 파일 자체를 UTF-8로 갖고 있으면 편하겠다 싶었죠. ^^;
그래서, MS949 파일의 데이터(insert 문장을 길게 나열한 것이죠.)를 BufferedReader.readLine()올 한 줄씩 읽어서 DataOutputStream.writeUTF() 로 쐈더니만 모든 행의 첫 줄에 입력하지 않는 글자들이 하나씩 붙더군요. ㅡㅡ;  API를 다시 보니, 일반, UTF-8이 아니라 modified UTF-8를 썼습니다. 그냥 간단히 살펴보면
'u0001'과 'u007F' 사이의 문자는 다음과 같이 한 바이트로 나타내고,
Bit Values
Byte 1
0
bits 6-0
널 문자인 'u0000'와 'u0080'과 'u07FF' 사이의 문자는 두 바이트로
Bit Values
Byte 1
1
1
0
bits 10-6
Byte 2
1
0
bits 5-0
'u0800'과 'uFFFF' 사이의 문자는 세 바이트로 나타낸다고 합니다.
Bit Values
Byte 1
1
1
1
0
bits 15-12
Byte 2
1
0
bits 11-6
Byte 3
1
0
bits 5-0
여하튼, UTF-8의 결함을 보완하기 위해서 수정한 것이라지만, 앞에 붙는 문자들로 인해서 ANT의 sql 태스크가 실행되지 않았습니다. DBMS에서 SQL 실행이 안되는 것이죠. insert 앞에 이상한 것들이 붙어 있으니까.. 문법 오류가 나죠. ^^;
문제를 어떻게 해결하느냐?
먼저 떠오르는 것은 이클립스를 이용한 꽁수였습니다. MS949 파일을 열어서 CTRL+A 하고 나서 CTRL+C 하고, 다시 UTF-8 포맷의 파일을 열어서 CTRL+V 하는 방법이죠. ^^;
두번째는 일일이 바이트를 인코딩해주는 것입니다. 호기심 좀 채우자고, RFC 같은 것을 읽을 수는 없으니까(너무 길더군요) URLEncoder의 encode() 메소드 소스를 보면 할 수 있을 것 같더군요. 뭐, 시간이 되면... 톰캣에 있는 encode() 메소드 구현이나 java.net.URLEncoder의 구현을 보고, 글을 올리겠습니다. 장담은 못하구요. ^^;
마무리는 꽁수가 아니라, JDK 5.0으로 해결한 이야기를 해드리죠.
꽁수를 써서 처음엔 해결하려고 해도 자꾸 이클립스에서 Heap이 넘쳐나는 에러가 나더군요. ^^;
그래서 다른 방법을 찾았는데 나중에 이 문제는 다시 부딪히게 되었습니다. 그건 나중에 이야기하구요. JDK5.0에서는 PrintWriter 클래스의 생성자 중에서 File과 인코딩 방식을 인자로 넣어주는 것이 있더군요. 그것을 사용하니까 잘 되었습니다. 너무 쉽게...^^; (테스트 코드를 첨부합니다.)
public void testReadByUTF8() throws Exception{
      
       BufferedReader reader = new BufferedReader(
               new FileReader("ref/zipcode_mysql6.sql"));
      
       PrintWriter writer = new PrintWriter(
               new File("setup/zipcode-mysql-6-data.sql"), "UTF-8");

      
       String line;
       while ((line = reader.readLine()) != null) {
           System.out.println(line);
           writer.println(line);
       }      
      
       reader.close();
       writer.close();
  }
프로그램이 잘 실행되었습니다. sysout(System.out.print) 한 내용도 문제 없이 출력되었구요. 파일에 데이터도 잘 복사되었습니다. Ant에서 sql 태스크를 수행하는데 잘 되다가 돌연 에러가 났습니다. 에러가 난 지점을 확인해보니.. 출력이 되다가 말았더군요. ㅡㅡ;
하필, 47,706건 중에서 100개도 안남긴 충북 청원군 부용면 부강8리의 데이터 삽입 구문이 출력되다 말았습니다. 정확히, 4,874,058  bytes 에서 문제가 발생한거죠. TaskMgr(작업 관리자)의 메모리 확인해보고, VM 옵션으로 힙(Heap) 크기를 크게 줘봐도 변화가 없었습니다. ㅡㅡ;
에러가 나는데, .log는 도대체 어디 있는건지. 탐색기에서 검색으로 찾아봤더니 이클립스의 로그 파일인 .log는 프로젝트 디렉토리 아래의 .metadata 디렉토리에 있었습니다.
java.lang.OutOfMemoryError: Java heap space
이걸로 어쩌란 것인지..ㅡㅡ;
결국은 이클립스 뉴스리스트를 보고 알았습니다. 이클립스에서 프로그램을 실행할 때 VM 옵션을 수정해줘봐야 소용이 없고, eclipse를 실행할 때 VM 옵션을 수정해야 합니다. 이클립스가 실행될 때의 디폴트 힙 사이즈는 128MB인 모양입니다.(정확하지는 않습니다. ^^;)
그런데, 플러긴까지 설치하면 금방 넘어가겠죠. 그랬던 것입니다. 고작 5MB도 안되는 파일을 읽다가 멈췄다기 보다, 딱 4MB 정도 남았는데 제가 파일을 로딩하면서 힙 사이즈를 초과한 것이라고 추측이 됩니다. 처음에 말했던 꽁수를 부릴 때도 에러가 난 것도 마찬가지 이유죠.
eclipse.exe -vm <자바 설치 디렉토리>jrebinjavaw.exe -vmargs -Xmx512M
위와 같이 실행했더니 무사히 앞서 했던 작업을 마칠 수 있었습니다. 이클립스 역시 VM 상에서 돌아간다는 것을 깜빡했던거죠. 이클립스가 수행되는 VM의 힙 사이즈를 늘려서 문제는 해결되었습니다.
저처럼 평소에 더블클릭으로만 이클립스를 띄웠던 분들은 참조하세요.
eclipse [platform options] [-vmargs [Java VM arguments]]
-vmargs args VM에 옵션을 전달하고자 할 때

닫기
퍼온 글

 
출처: http://blog.naver.com/inking007/120001550360
 
문자 코드 변환 과정
 
1. 컴파일
컴파일시 원시파일의 문자열을 현재 환경(한글환경 KSC5601)의 locale로 인코딩하여 읽어 들인후
유니코드에서의 대응하는 코드값으로 변환한다.
원시파일에서  '한글' 이라는 문자열은 다음과 같은 hex code를 가지고 있다. (KSC5601)
c7 d1 b1 db
2.실제 .class 파일로 저장시 UTF-8로 변환하여 저장한다.
예를 들어 다음과 같은 코드 작성후
컴파일하면
public class Test {

   public static void main(String args[]) {
        String str = "한글";
       System.out.println(str);
  }

}
클래스파일 Test.class에 '한글' 이라는 문자열은 다음과 같이 인코딩되어 저장된다.
ED 95 9C EA B8 80
디컴파일 하면 다음과 같이 변환된다. UTF-8 --> UTF-16
        String s = "uD55CuAE00";
uD55C  '한'
uAE00  '글'
아래 URL을 클릭하면 해당 Unicode가 나타내는 문자및 UTF-8코드를 볼수 있다.
 
3. 실행시 문자열을 UTF-16으로 변환하여 로드하고 화면에 출력시 디폴트 인코딩으로 출력한다.



 
 
닫기
인코딩에 따라 글자수 인식이 달라진다.
인코딩이 되지 않고 한글이 입력될 때는(ISO8859-1로 추정)
'이호'라고 입력하면 6자로 인식된다.
한글 한 글자를 3자로 인식하는 것이다.
'육봉달'이라고 입력하면 9.. 받침하고 상관없다.
'튕뽃뾳'이라고 해도.. 9
이번엔 인코딩을 해본다.
먼저 UTF-8
위의 세 개의 문자를 대상으로 테스트하면
2, 3, 3 글자.. 역시 받침에 상관없고
한글 한 글자를 영문 알파벳 하나와 동일하게 인식한다.
퉧뾳뾳, 111, aaa, %%%
위의 입력 모두 동일하게 3글자로 인식한다.
EUC-KR은 다를까?
위의 네개 문자 조합을 입력하면 나머지 셋은 3 글자로 인식하지만
한글인 '퉧뾳뾳'은 2글자로 인식하여 모두 6글자가 된다.
이것만 보고 EUC-KR은 한글을 두 자로 인식하구나 일반화하는 것은 위험한 일이었다.
다음과 같은 기이한 현상을 보인다.
퉧뾳뾳 : 제대로 인코딩을 못하며 24글자로 인식한다.(퉧뾳뾳)
이쁋: 역시 두 번째 글자는 인식을 제대로 못해 9자가된다.(이쁋)
제대로 인식하지 못하는 글자에 대해서는 8글자가 배당됨을 알 수 있다.
EUC-KR을 관행처럼 쓰고 있는 가운데
UTF-8을 쓰자할 때 뭐가 장점이냐 물으면.. 사실 떠오르는 것이 별로 없었는데
한가지 예로 들 수 있는 현상이다.
UTF-8에선.. 뾳뛕쁋쿅푝 같은 외계어같은 문자마저 올바로 5글자로 인식한다는 면에서 EUC-KR보다 실용성이 높다고 할 수는 없지만 신뢰성이 높은 것은 분명한 것이다.


Hatori  2006/04/21 13:52
글자수를 센다기보다는 바이트를 센다는게 정확한 말이 아닐까요.
전에 어디선가 해결방법을 찾아서 잘썼던거 같은데 기억이 안나네요 -_-ㅋ

손님  2006/04/24 10:52 
어떤 환경에서 테스트하느냐에 따라서 달라질 것 같은데요..
클라이언트에서 eclipse를 통해 테스트해본 바로는 위에 쓰신 내용과는 다르게 나오네요...



Java, 한글이 포함된 문자열을 byte 크기로 잘라내기
번호 제목 글쓴이 날짜 조회 수
23 Polymorphism과 Method Overriding에 대한 이야기 황제낙엽 2003.05.15 330
22 자바의 특징 황제낙엽 2003.05.06 450
21 JVM (Java Virtual Machine)에 대한 몇가지 설명 황제낙엽 2003.05.06 210
20 자바설화 황제낙엽 2003.05.03 395
19 XML기반 정보 보호 기술의 대두 황제낙엽 2003.04.22 265
18 인터넷을 통한 자바 기술의 변화 황제낙엽 2003.04.07 493
17 JAVA관련 용어와 기술 황제낙엽 2003.04.05 458
» UTF-8을 위한 문자열 인코딩처리 관련 황제낙엽 2006.10.06 881
15 Code Conventions for JavaTM Programing Language file 황제낙엽 2006.10.23 518
14 자바에서 UTF-8 개발에 관한 정리 (2) 황제낙엽 2006.10.06 543
13 자바에서 UTF-8 개발에 관한 정리 (1) 황제낙엽 2006.10.06 539
12 일본어 전각 반각 변환 예제 소스 .두번째 [1] file 황제낙엽 2007.01.11 569
11 일본어 전각 반각 변환 예제 소스 .첫번째 file 황제낙엽 2007.01.10 3371
10 환경파일 로드 클래스 구성 (XML파싱) 황제낙엽 2007.01.05 437
9 HttpURLConnection을 사용하여 웹 페이지 액세스하기 (두번째) 황제낙엽 2007.01.05 593
8 자바의 I/O 예제 [1] file 황제낙엽 2006.12.28 347
7 HttpURLConnection 클래스를 이용한 Search 황제낙엽 2006.12.28 670
6 HttpURLConnection을 사용하여 웹 페이지 액세스하기 (첫번째) 황제낙엽 2006.12.28 522
5 Servlet으로 구현한 파일 다운로드(download) 페이지 황제낙엽 2006.12.27 455
4 JSP로 구현한 파일 다운로드(download) 페이지 황제낙엽 2006.12.27 2583