본문 바로가기

프로그래밍/JAVA

JAVA 한글자르기(유니코드)


JAVA 한글자르기


6> 원리 찾아서

  1)  쉬프트 연산

  2)  signed Data ( Feat. 비트와 보수)

  3)  JAVA 에서의 한글표현

     -  1편의 예제 Source 중 핵심코드라고 되어 있던 2줄을 정확하게 이해하기 위해서 

         위의 3가지를 충분히 이해 할 수 있는 내공이 필요하다!!! 


7> 상세 원리 

1) 먼저 쉬프트 연산을 알아보자 ~

- Source에 나와 있는 0x80 값을 쉬프트 연산(&)을 위해 비트로 표현하면 10000000 과 같고 이값으로 쉬프트 연산을 한다면?

 ?

?

 <= 쉬프트 연산 대상의 비트 표현

 1

 <= 0x80 의 비트 표현

 ?

 <= 쉬프트 연산의 결과

위의 쉬프트 연산을 보면 0 과 시프트 연산(AND 연산을 하면)을 하게 되면 반드시 0이 나온다.  0*( '1' or '0' ) = 0 이되는건 당연!!!

-> if((by[i] & 0x80) == 0x80) count++; // 핵심 코드 "

    " if((by[len - 1] & 0x80) == 0x80 && (count % 2) == 1) len--; " // 핵심코드

==> 그럼 1편에서 본 위의 코드를 다시 해석해 보자!!!!! 아직 정확히는 잘 모르겠지만 로직적으로만 본다면

if절의 by[i], by[ len - 1] 의 값이 다른 순번에 있는 값과는 상관없이 8번째 비트의 값기준으로 1 일때 

true 그외에는 false 가 떨어진다. 라는 결론에 도달할 수 있다.

3번에서 count % 2 에 대한 부분은 아래쪽에서 다시 설명 하겠다.


2) signed Data

- JAVA의 경우 Unsigned Data를 지원하지 않는다 왜?? 지원하지 않는것인가에 대해서는

 자바 리포트(Java Report)*라는 잡지, 2000 7월에 실린 인터뷰 기사에서 제임스 고슬링(James Gosling) JAVA 의 아버지라 불리는 사람이 간결한 언어와 관련된 질문에 대한 대답중 다음과 같이 이야기를 한다unsigned 연산이 어떻게 이루어지는지 제대로 이해하고 있는 개발자가 거의 없다” 그러면서 그는자바는 다른 언어들이 봉착하게 되는 다양한 경계조건들(edge cases)과 제대로 이해하는 사람이 거의 없는 것들은 포함시키지 않았다고 말했다.

위와 같은 이유로 Unsigned 를 지원하지 않는다고 했지만....  ( JDK 8 버전부터는 지원한다고 한다.......)

- unsigned 데이터가 뭔지에 대해서와 2번째 이해를 위한 내공증진을 위해 비트와보수 <= 꼭 정독을 하도록 한다!!


내용중 JAVA Byte 표현에서 127 이상의 값은 음수의 형태로 표현된다~ 라는 부분이 중요한데 이는 signed Data를 표현하는


JAVA에서 127 이상의 값을 표현할 때 8째 자리인 부호비트가 음수를 나타내는 1이 된다는 사실이다.


    엇~!! 그럼 대충 감이 오는 분들이 있으시겠죠~ 1번의 참(True)이 되는 기준과 묘하게 엮이기 시작합니다.



3) 한글표현 In JAVA


이제 마지막 원리이다~ 여기까지 오기 힘들었지만 마지막이 젤 빡세다 ㅠ 


JAVA도 결국은 컴퓨터에서 돌아가고 컴퓨터라는 녀석이 안탑깝게도 어디에서? 영어 문화권에서 시작을 하다보니 컴퓨터가 알아들을 수 있도록 한글을 표현하기 위한 방법이 필요하게 되었다. 


어떻게 컴퓨터에서 한글을 표현하게 되었는지 역사에 대한 내공을 얻으러 한글문자 집합 및 인코딩 <= 요기를 정독하자!!!

또한 JAVA 에서 한글을 표현하는 것에 대한 내공을 얻으로 유니코드와 JAVA를 이용한 한글처리 <= 요기도 정독하자!!!


내공을 얻기가 그리 쉽지 않죠~ 그게 쉬우면 아무나 다하게!!!! 완전히 이해를 할 수 없더라도 기본적인 인코딩과 유니코드에 대한 개념은

가지고 있어야 전산으로 밥먹고 사는데 살림살이가 조금(?)이라도 좋아질 수 있으니 꼭 한번정도는 정독 해보도록 합시다. 물론 저도~ ㅋ


다시 본론으로~ 




위의 표는 EUC-KR 인코딩에서 한글을 표현하기 위해 컴퓨터가 알아먹을 수 있도록 맵핑되는 값이다. 

즉 EUC-KR 인코딩을 사용하여 한글 "가" 를 표현하는 값은 B0A1 이며 저렇게 쓰게 되면 

컴퓨터가 EUC-KR의 인코딩의 "가"를  알아먹고 표현하게 된다. 귀찮고 비싼 놈 같으니라구  ㅇㅅㅇ  

16진수인 B0A1을 우리가 잘 아는 정수형으로 값을 표현하면 B0 = 176 , A1= 161 이다. 


이해를 위해 코드로 설명을 해보자


String Test = "가"

int B0 = 176;

int A1 = 161;


byte[] TestString = Test.getByte("EUC-KR"); // EUC-KR에서는 한글 한글자에 2 byte를 사용한다. 

System.out.println(TestString.length)          // 확인 해보면 당연히 2다~!!! 만약 UTF-8 이라면 Length는 3이 되겠지요~


System.out.println( TestString[0] );

System.out.println( TestString[1] );

System.out.println( Integer.toBinaryString(TestString[0]) );

System.out.println( Integer.toBinaryString(TestString[1]) );

System.out.println( Integer.toBinaryString(B0) );

System.out.println( Integer.toBinaryString(A1) );           


Print

TestString length : 2

TestString[0] : -80

TestString[1] : -95

TestString[0] BinaryString : 11111111111111111111111110110000

TestString[1] BinaryString : 11111111111111111111111110100001

B0 : 10110000

A1 : 10100001


한글 "가"를 나타내는 값이 바이너리값으로 표현될때 위와 같이 표현됨을 확인 할 수 있다.


그럼 위의 표를 확인할 때 EUC-KR 에서 한글에 맵핑되는 코드의 값이 B0A1 ~ C8FE 사이에 속하게 되고 이를 정수형 값으로

본다면 모두 127 이상이며 그렇기 때문에 8번째 비트값은 반드시 1을 가진다 라고 이해 할 수 있다.


유래카!!!!!!!!!!!!!!!!!!! 수수께기는 모두 풀렸다.



8> 최종 코드 정리

 - 다시 코드를 확인 해보자


public static String cutString(String str, int len) { 

  byte[] by = str.getBytes();
  int count = 0;
  try  {
       for(int i = 0; i < len; i++) {

            if((by[i] & 0x80) == 0x80) count++; // 핵심 코드

       }

       if((by[len - 1] & 0x80) == 0x80 && (count % 2) == 1) len--; // 핵심코드

       return new String(by, 0, len);   

  }
  catch(java.lang.ArrayIndexOutOfBoundsException e)
  {
       System.out.println(e);
       return "";
  }


최종적으로 위의 코드를 말로 풀어본다면


1 번째 핵심 코드

EUC-KR 인코딩에서 by[i] 가 한글이라면 0x80 과 비트연산을 하게 되면 0x80 과 같은 값이 나오게 되며(왜냐면 EUC-KR 표현에서 한글은 127이상의 수로 표현되어지기 때문에 바이너리표현상 8번째 자리가 반드시 1이다 ) by[] 의 Length 만큼 루프를 돌면 한글이 포함된 개수를 알 수가 있고 



2 번째 핵심 코드

by[] 마지막 값이 한글이면서 count가 홀수(EUC-KR은 한글을 2자리로 표현하기 때문에)라면 한글이 깨진형태이니 최종 Length를 한자리 줄여서 글자를 표현하라~ 그러면 깨지지 않는 한글을 보게 될 것이다~




이제 원리를 알았으니 코드를 좀 더 아름답게 수정도 가능할 것이며 응용도 가능하리라고 생각합니다 그것이 내공을 쌓는 이유죠~


이를테면 저기 함수에 인코딩 인자를 받아서 처리해야 정확한 처리라던가 만약 인코딩이 UTF-8 이면 처리하는 방법이라던가 ~ 


한글의 범위를 정확하고 정교하게 나누는 방법도코딩할 수 있을거라 생각하고 그것은 이글을 읽는 독자분께~ 맡기도록 하겠습니다.




9> 마무리

 한글자르기에 대한 블로그를 작성하면서 아마 틀린부분도 있을것이고 저 역시도 내공이 부족해 좀 더 깊고 상세한 

명을 못했다고 생각하는 부분도 있습니다 그렇기에 혹시나 잘못된 표현이 또는 좀 더 이해하기 좋게 수정 할 수 

있도록 좋은 피드백을 기다립니다. 

또한, 해당 글은 어디든 퍼가셔도 되는데 꼭 출처만은 지켜주셨으면 합니다~ 그럼 담에 또~ 뵙도록 하겠습니다.







'프로그래밍 > JAVA' 카테고리의 다른 글

JAVA 한글자르기(Feat 한글깨짐)  (0) 2019.01.04