본문 바로가기
공부하는 하스씨/안드로이드

[펌강좌] 문자셋, 인코딩 이야기 #5

by 박하스. 2010. 2. 23.
728x90
반응형

신비로 JAVA월드 의 Soony(fstars@shinbiro.com) 님의 강좌글 펌

http://club4.shinbiro.com/clb/bbs/sbrClbBbs_View.jsp?bbsid=50354&pg=2&dbsts=A&artno=428


[들어가며]

엇그제 한주의 시작을 알리는 월요일인거 같더니 벌써 금요일이 되었다. 주5일 근무하시는

분들이 부러울 따름이다. 필자는 요새 회사가 제품 개발의 마지막 단계에 있기 때문에 토요일

까지 근무하게되는 어처구니 없는 생활중이다. 아 부러버라~

 

[주의]

이 글은 정말 초보를 대상으로 작성된 글로써 필자는 이글에서 논의되는 내용의 좀더 심층적인

문제나 어투등을 문제 삼는 리플을 정중히 사양합니다. 단, 잘못된 점의 지적은 환영합니다.^^

표현의 원할함을 위해서 존칭은 생략했습니다.

 

[7. Big Endian? Little Endian? ]

 

이번에도 잠시 인코딩 역사에 대해서 한걸음 물러나 다른 이야기를 해볼까 한다. 위 제목의 단어

를 이해하시는 분이나 또는 "나는 평생 Intel 또는 AMD CPU 와 Windows OS 상에서만 놀꺼야"

 라는 분들은 이 부분은 Skip 하고 넘어가셔도 된다. -_-;

 

눈치 빠른 분들은 느끼셨을테지만 이번에 이야기할 부분은 Platform Independent

(쉽게 말해 내가 작성한 프로그램이 Solaris, Linux, Windows 등 어떤 하드웨어나 OS의 특성과 무관하게 일관된 기능이 동작 될 수 있도록 작성된 프로그램) 

한 프로그램을 작성할 때 주의해야 할 부분에 대한 이야기 이다.

 

자.. 가령 이러한 프로그램을 만들려고 한다고 치자.

"일본어 히라가나를 카타가나로 변환하는 프로그램"

즉, "あいうえお"(아이우에오)  라는 문장을 이 프로그램을 통과하면

"アイウエオ"(역시 아이우에오) 이렇게 바뀌는 거다.  

 

오호~ 흥미진진한걸~ 시작해 볼까?

 

(참고로 필자는 지금은 어떠어떠한 이유로 웹 프로그래머가 되었지만 원래는 C 프로그래머 였다.

따라서 강의 중에도 아마 C 문법을 이용한 예가 많이 나올듯 하다. 물론 C 문법을 모르시는 분들

을 위해서 가능하면 C 문법으로 설명하는 내용을 줄이려 노력하겠지만 역으로 아직도 C 문법을

모르시는 프로그래머가 있다면 기회가 된다면 꼭 C 는 한번쯤 공부해 보시길 권한다.)

 

쉽게쉽게 가기 위해서 입력되는 문장은 ASCII 가 섞이지 않은 절대적인 MBCS 문자열만 들어온다고 가정하자. 즉, 한번에 2바이트씩 읽고 그걸 1개 문자라고 생각해도 무방하다는 것이다.

 

여러분 같으면 어떻게 작성하겠는가?

가장 쉽게 떠오르는 것은 (물론 생각이 쉽다는 것이지 방법적으로 쉬운것이 아니다.)

"あ" -> "ア" 로 매칭시키는 코드 테이블을 미리 준비하여 읽은 데이터를 코드 테이블에

대조한 후 결과를 대치시키는 방법이다.  쉽다. 아주 좋다. 히라가나 50개 데이터의 변환표만

준비되어 있다면 이 방법을 사용해도 된다.

 

그러나 역시 또 우리의 눈치 빠른 독자분들 께서는 느끼셨겠지만 이 방법이 아닌 다른게 있으니까 필자가 이야기 하지 않을까?

 

EUC_JP 인코딩에서...

(전 강의에서도 잠깐 등장했지만 EUC_ 어쩌구 하는 인코딩은 Unix 계열에서 원래 사용되던 인코딩 방법으로 EUC_JP(일본어), EUC_KR(한국어), EUC_CN(중국어) 등등이 있고 이 인코딩들은 서유럽어의 ISO-8859-1 인코딩이 확정되기 전부터 사용되던 것이라서 서유럽어와 충돌을 피하기 위한 "예약 범위" 라는 것이 전 강의에 이야기한 범위와 다르다.

즉, 이 인코딩 들은 서유럽어와 충돌이 있다. 이 인코딩들에 대한 좀더 자세한 이야기는 아마 다음에 또 할 기회가 있으리라.-_-;)

 

암튼 EUC_JP에서 "あ" -> 0xA1A2   일 것이다. (윽, 기억이 가물가물하다. 정확한 값은 장담못한다. 첫 강의에서 이야기 했듯이 필자가 전 직장에서 배웠던 것을 이야기하는거라서 정확한 자료들은 이젠 없다. -_-)

 

"あ" -> 0xA1A2

"い" -> 0xA1A4

"う" -> 0xA1A6

"え" -> 0xA1A8

"お" -> 0xA1AA

 

한편,

 

"ア" -> 0xA3A2

"イ" -> 0xA3A4

"ウ" -> 0xA3A6

"エ" -> 0xA3A8

"オ" -> 0xA3AA

 

자.. 이렇다.. 이쯤되면 정확한 값은 논외로 하더라도 필자가 무엇을 말하려는지 알것이다.

그렇다. 문자셋은 아무렇게나 모인게 아니다. 각 언어의 고유 알파벳 순서 대로 모여있는 것이기

때문에 순서가 뒤죽박죽이라면 매칭 테이블이 필요하겠지만 히라가나와 카타가나의 순서가

같으므로 간단한 연산으로 변환이 가능하다.

게다가 인코딩을 정할 때도 아무렇게나 여유공간에 배치시킨게 아니다. 가능하면 구별이 확실히

될 수 있도록 상위바이트가 "0xA1" 인 곳은 히라가나 전용, "0xA3" 인곳은 카타가나 전용 구역으로 설정해놓은 것이다.

히라가나가 50개 정도 밖에 안되므로 충분히 "0xA105" 라는 구역도 비어있을 터.... 

그래도 그 구역은 더이상 다른 글자를 배치시키지 않는다.

생각해 보시라. 어차피 2바이트를 1글자로 쓰기로 정했기 때문에 공간이 많이 남는다.

기왕이면 구역별로 고유 공간을 만들어 주는게 좋지 않을까?

 

이러한 특성을 안다면 이제 우리가 해야할 일은 요렇게 정리된다.

1. 2바이트를 읽어(즉, 1글자를 읽어) 일단 상위 바이트가 "0xA1" 인지 비교한다.

     (맞으면 2단계로 진입, 틀리면 히라가나가 아니므로 무시하고 다음 글자 읽음)

2. 읽은 글자가 히라가나면 뒤쪽 바이트는 그대로 두고 앞쪽 상위 바이트 값에 2를 더한다.

     (즉, 0xA1A2    에서 0xA3A2로 )

3. 변환한 내용을 다시 저장하고 다음 글자를 읽는다.

 

어떤가? 말로 썼지만 이 방법대로 프로그래밍하는거야 간단하겠지? 특별히 테이블도 필요없다.

 

자.. 끝....ㅡ_ㅡ;

(아 .. 돌 날라오는 소리 여기저기서 들린다. 그렇다.. 지금까지 내용중에서 Endian에 대한 이야기는 한번도 없었지 않나.. 오늘 제목이 뭔데 필자가 여기서 강의를 마친단 말인가 -_-;)

 

자자 흥분 가라앉히시고...이제 본격적으로 Endian에 대한 이야기를 해보자

일단 위에 말한 내용을 실제로 짜본다고 생각해보자.

 

C 언어가 나올것이다. 이해해 주시라.

일단 한번에 2바이트씩 읽어들일 것이므로 char 변수가 아닌 2바이트짜리 unsigned short 를

사용한다. 한글자씩 글자수대로 처리하기 위함이다. short 변수는 2바이트의 저장 공간을 갖는다

 

읽어들인 short 변수에서 앞쪽 1바이트 부분에만 +2 를 시킬것이다.

 

그리고 변경된 short 변수를 다시 데이터 저장한다.

 

자 여기서 문제가 발생한다.

원래 의도라면 0xA1A2 가 읽어지면 나중에 저장되는 녀석은 0xA3A2 가 되어야 한다.

그런데 결과는?

어떤 컴퓨터에서는 우리의 의도대로 0xA3A2 라는 값으로 저장되지만

다른 곳에서는 0xA1A4 라고 저장된다.   헉... -_-;

 

무슨 소리인지 이해가 가는가?

우리가 상위 바이트라고 생각했던 부분이 실제로 상위 바이트가 아닐 수 있다는 것이다.

 

즉, 파일에 0xA1A2 라는 데이터를 메모리로 읽어들일때...

순서대로 메모리에 상위 바이트가 앞으로 가도록 배치되는 컴퓨터가 있는 반면(Big Endian),

거꾸로 상위 바이트가 뒤로 가게 배치되는 컴퓨터가 있다. (Little Endian)

 

따라서 무턱대고 2바이트중 앞의 것이 상위 바이트 이려니 생각하고 +2를 하면

Big Endian 의 경우

메모리 상 : 0xA1 0xA2        ---> 더하기 연산 ---> 0xA3 0xA2  ----> 파일 저장 ---> 0xA3A2

Little Endian 의 경우

메모리 상 : 0xA2 0xA1        ---> 더하기 연산 ---> 0xA4 0xA1  ----> 파일 저장 ---> 0xA1A4

 

이렇게 작업이 이루어진다.

 

보면 알다시피 순서의 바뀜은 오직 메모리상에서 이루어진다. 파일에 저장될 때는 원래의 순서

대로 컴퓨터 차원에서 알아서 순서를 돌려놓는다.

 

Little Endian 에서 멀티 바이트 크기를 가지는 변수들의 바이트 순서가 다라진다는 것이다.

(한가지 주의 할 점은 변수 내부의 바이트 순서가 달라질 뿐 개개의 변수 순서는 달라지지 않는다.

 

즉, 0xA1A2A1A4   이렇게 2글자를 Little Endian 에서 short 를 이용하여 순차적으로 2개 읽으면

메모리 상에는 이렇게 올라간다.

메모리 상 : 0xA2 0xA1 | 0xA4 0xA1

 

위와 같이 short 라는 변수 내부에서는 바이트 순서가 달라졌지만 short 라는 2바이트 묶음으로 봤을때의 순서는 달라진게 아니다. 이점은 상당히 중요하다.

즉, 한글자를 처리하고 다음 글자를 처리하기 위해서 short 크기만큼 진행하면 다음글자에 제대로 도착한다는 의미이다. )

 

그럼 왜 혼동되게 어디는 Big Endian 을 쓰고 어디는 Little Endian을 쓸까?

 

원래 컴퓨터는 모두 Big Endian 이었다. 사람이 글을 왼쪽에서 오른쪽으로 쓰듯이 당연히 처음

설계할 때는 모두 Big Endian으로 메모리 에서도 왼쪽부터 시작하게 설계된 것이다.

 

그런데 다음의 예를 보자.

메모리 공간 1바이트에 다음과 같은 값이 있었다.

00010000                        --> 0x10       즉, 십진수 16 이다.

그런데 컴퓨터는 하는일이 숫자 계산이 다라고 할 정도로 계산밖에 하는일 없는 녀석이다.

저기 16에 256을 더한다고 치자. 십진수로는 이제 272라는 값이 된다. 비트로는 ?

00000001 00010000        --> 값이 256이 넘기에 하나의 바이트로는 안되고 앞에 바이트가

                                           하나더 늘어나야 한다.

 

바로 이거다.. 컴퓨터가 다루는 무수한 계산 과정에서 자리수가 늘어나거나 줄어드는 일은 비일비재하다. 그러나 만약 자리수가 늘어날때 마다 앞쪽에 바이트를 배치해야 하는 Big Endian이라면 먼저 있던 바이트를 뒤에 복사하고 앞에 새로운 바이트를 복사하는 작업이 필요하게 된다.

 

그러나 Little Endian 이라면?

십진수 272를 나타낼때 이렇게 배치된다.

00010000 00000001        --> 요렇게 원래 있던 녀석은 가만히 있고 늘어난 자리수를 표시할 바이트를 뒤에 새로 붙이면 되는 것이다. 자리수가 늘어날때 마다 뒤에 붙이고 자리수가 줄면 다시 뒤에 있던걸 없애고 Big Endian이 자리수의 변동에 따라 전체 데이터를 왔다갔다 해야하는 것보다 훨씬 나은 방법이 아니겠는가?

 

그래서 비교적 최근에 설계된 컴퓨터들이 계산상의 성능 향상이라는 목적으로 Little Endian을

채택하고 있는 것이다.

 

("이봐요 강사, 그래서 우리가 조심해야하는 것은 무엇입니까?" 

특별히 없다. -_-; 이 바이트 순서 뒤집기는 메모리 상에서만 일어나는 현상이다. 기본적인 입출력시에는 컴퓨터가 알아서 다시 순서를 뒤집는다. 사용자는 메모리상에서 바이트 순서가 어떻든 전혀 신경쓸 필요가 없다. CPU와 메모리가 지들 계산 편하게 뒤집어 놓은걸 왜 사용자가 일일이 신경쓰겠는가? 알아서 돌려놓기만 하면 속에서 무엇을 하든 컴퓨터 맘이다. 우린 계산 결과만 잘나오면 장땡인것이다.

다만 예시로 작성해 보려했던 프로그램과 같이 변수와 변수의 관계가 아닌 하나의 변수속에서 바이트 순서를 직접 다루려고 하는 그러한 프로그램을 짜려면 Endian 문제를 지켜줘야 한다는 것이다. 당연하게도 컴퓨터 머리속에 있는 걸 직접 들어가서 조작하겠다 하면 순서 정도는 지켜줘야 할 것이다. )

 

마지막으로 누구는 Big Endian 이고 누구는 Little Endian 일까?

비교적 전통이 오래된 Sun Solaris 같은 컴퓨터는 Big Endian 을 사용한다.

일반적인 여러분 컴퓨터 즉, Intel, AMD CPU들은 Little Endian 이다.

즉, Windows 는 당연히 Little Endian 으로 처리한다.

Linux도 Intel CPU위에서 돌아가다면 Little Endian 이다.

 

[마침]

오늘의 내용은 인코딩과 별로 상관 없는 이야기 였지만 필자가 Endian 이라는 문제를 접한것이 바로 오늘의 예제와 같은 프로그램을 짜다가 실수한 것이라 생각나서 한번 기술해 보았다.

그런데 Little Endian 이라는 단어만 보면 필자는 자꾸 왜 Little Indian 이라는 동요가 생각나는지..-_-

        

728x90
반응형