# $str 의 인코딩을 $from 에서 $to 로 컨버트 sub convert_encode { my ($str, $from, $to) = @_; eval { require Encode; }; unless($@) { $str = Encode::encode($to, Encode::decode($from, $str)); } else { eval { require Text::Iconv; }; unless($@) { my $converter = Text::Iconv->new($from, $to); $str = $converter->convert($str); } } return $str; }Encode(4) 모듈을 사용하는 경우에 한해서는 위의 decode()(5)와 encode()(6)를 결합하는 대신에 from_to()(7)를 사용해서 아래와 같이 쓸 수도 있음
Encode::from_to($str, $from, $to); # 이 경우는 $str의 값 자체가 바뀐다.
일단, split_string 에서 중간에 정규표현식을 사용하여 쪼개는 과정을 생략하고, 단지 utf-8 -> 어떤 인코딩 -> utf-8 로 돌리는 것은 웬만하면 다 됩니다. "어떤 인코딩"이 UCS-2(16), UTF-16(17), UTF-32(18), 여기에 각각 BE, LE 붙여가며 해 봤는데, Encode(19) 모듈과 Text::Iconv(20) 모듈 다 스트링이 원래의 것으로 잘 나오더군요. 그런데 정규 표현식을 써서 쪼개기를 하려면, 제대로 쪼개지질 않더군요. 어떤 인코딩을 뭘로 지정해도... 이런 인코딩들의 경우는 정규표현식의 "."이 여전히 "1바이트"를 의미한다고 생각하고, $length 값을 2배로 곱해서 처리하게 했더니 그제서야 제대로 쪼개집니다. 아래의 표는 각각의 인코딩 이름을 두 개의 모듈을 써서, 한글 스트링, 일본어 스트링, 그리고 위키페디아에서 적당히 복사해 온 정체모를 언어 스트링을 split_string($str, 2) 를 거치게 했을 때의 결과입니다. (딱히 언어에 따라 다른 결과가 나오지는 않았습니다)즉 endian 을 지정하지 않은 경우에, BOM이 붙느냐 마느냐, BOM이 붙은 스트링을 쪼갤 때 다시 양쪽에 BOM을 붙여 주느냐 마느냐 등의 문제로 서로 다르게 동작했다.Encode모듈 Text::Iconv모듈 UCS-2 ok ok UCS-2BE ok ok UCS-2LE ok ok UTF-16 error(주1) error(주5) UTF-16BE ok ok UTF-16LE ok ok UTF-32(주2) error(주3) error(주6) UTF-32BE ok ok UTF-32LE ok ok UTF-8 error(주4) error(주7) * 주1) UTF-16:Unrecognised BOM b098 at /usr/lib/perl5/5.8.5/i386-linux-thread-multi/Encode.pm line 162 * 주2) $lengh 값을 4배 곱한 후 처리 * 주3) 주1과 같은 에러, 명칭만 UTF-32 로 나옴 (utf-16과 utf-32의 경우는 스트링 앞에 바이트오더를 알려주는 BOM이란 게 붙는다는 설명으로 보아, 스트링을 둘로 쪼갠 후에 뒷쪽 부분에는 그 BOM이 안 붙은 상태라서 이렇게 되는듯 * 주4) 문자들마다 바이트 수가 다르므로, 제대로 쪼개지지 않음. * 주5) split_string 에 인자로 준 숫자보다 하나 적은 수의 문자를 세어서 쪼갬.(쪼갤 때 BOM도 두 바이트를 차지하기 때문인듯) * 주6) 주5)와 동일 * 주7) 아예 결과가 ("", "") 으로 나옴
use Encode; $str = "\x{ac00}\x{b098}"; # "가나" in unicode open OUT, ">out_unicode.txt"; print OUT "$str\n"; close OUT;별도의 인코딩 없이 저 유니코드 표현을 그대로 파일에 찍으면 UTF-8로 출력하더군요.$ hexdump out_unicode.txt 0000000 b0ea eb80 9882 000a <- 가 %EA%B0%80 나 %EB%82%98 그런데 두 바이트씩 끊은 후 little endian 으로 저장한 듯 0000007
그 다음은, 온갖 인코딩을 바꿔가면서 파일에 저장 =.=;;# Encode 모듈의 경우는, 저 펄의 유니코드 표현을 아래의 각 이름별로 encode에 넣은 후 출력 for $enc qw(EUC-KR UCS-2 UCS-2BE UCS-2LE UTF-16 UTF-16BE UTF-16LE UTF-32BE UTF-32LE UTF-8) { $str2 = encode($enc, $str); open OUT, ">out_$enc.txt"; print OUT "$str2\n"; close OUT; } # Text::Iconv 의 경우는, 펄의 내부 표현을 나타내는 이름이 뭔지 모르기 때문에 # 일단 utf-8 스트링을 지정한 후 그걸 아래의 각 이름별로 utf-8 -> $enc 변환 $str = "가나"; for $enc qw(EUC-KR UCS-2 UCS-2BE UCS-2LE UTF-16 UTF-16BE UTF-16LE UTF-32BE UTF-32LE UTF-8 UNICODE) { $converter = Text::Iconv->new("UTF-8", "$enc"); $str2 = $converter->convert($str); open OUT, ">conv_$enc.txt"; print OUT "$str2\n"; close OUT; }Encode()를 사용하여 Text::Iconv()를 사용하여 펄 내부 표현을 각각의 인코딩으로 변환 UTF-8 스트링을 각각의 인코딩으로 변환 -------------------------------------------------------------------- out_EUC-KR.txt conv_EUC-KR.txt 0000000 a1b0 aab3 000a 0000000 a1b0 aab3 000a 0000005 0000005 (가 %B0%A1 나 %B3%AA 인데 두 바이트씩 순서가 바뀌어 저장) -------------------------------------------------------------------- out_UCS-2.txt conv_UCS-2.txt 0000000 00ac 98b0 000a 0000000 ac00 b098 000a 0000005 0000005 (둘이 다르다! Encode에서는 UCS-2BE로, Text::Iconv는 LE로 저장했음) (파일이 저장되면서 두 바이트씩 바이트 순서가 바뀌어 저장되는지, 눈으로 봤을때는 좌측이 LE처럼 보임 =.=;) -------------------------------------------------------------------- out_UCS-2BE.txt conv_UCS-2BE.txt 0000000 00ac 98b0 000a 0000000 00ac 98b0 000a 0000005 0000005 -------------------------------------------------------------------- out_UCS-2LE.txt conv_UCS-2LE.txt 0000000 ac00 b098 000a 0000000 ac00 b098 000a 0000005 0000005 -------------------------------------------------------------------- out_UTF-16.txt conv_UTF-16.txt 0000000 fffe 00ac 98b0 000a 0000000 feff ac00 b098 000a 0000007 0000007 (이것도 다르다! Encode는 UTF-16BE로, Text::Iconv는 LE로 저장했음) (BOM 마크가 좀 이상하다... Encode는 BE의 BOM인 0xFeFF 를 앞에 붙이고 그게 파일로 저장되면서 순서가 바뀌었다고 볼 수 있는데, Text::Iconv가 붙인 BOM은 해석이 안 됨. BE의 BOM인 0xFeFF가 순서 바뀜 없이 저장된 거라면 그 뒤의 따라오는 "가나"가 16LE.txt와 동일하게 구성된 것과 상충하고, LE의 BOM은 0xFFeF 이므로 순서를 바꾸면 ef ff 여야 한다. -_-???) -------------------------------------------------------------------- out_UTF-16BE.txt conv_UTF-16BE.txt 0000000 00ac 98b0 000a 0000000 00ac 98b0 000a 0000005 0000005 -------------------------------------------------------------------- out_UTF-16LE.txt conv_UTF-16LE.txt 0000000 ac00 b098 000a 0000000 ac00 b098 000a 0000005 0000005 -------------------------------------------------------------------- out_UTF-32BE.txt conv_UTF-32BE.txt 0000000 0000 00ac 0000 98b0 000a 0000000 0000 00ac 0000 98b0 000a 0000009 0000009 -------------------------------------------------------------------- out_UTF-32LE.txt conv_UTF-32LE.txt 0000000 ac00 0000 b098 0000 000a 0000000 ac00 0000 b098 0000 000a 0000009 0000009 -------------------------------------------------------------------- out_UTF-8.txt conv_UTF-8.txt 0000000 b0ea eb80 9882 000a 0000000 b0ea eb80 9882 000a 0000007 0000007 -------------------------------------------------------------------- * 이건 좀 특별한 케이스 out_unicode.txt conv_UNICODE.txt 0000000 b0ea eb80 9882 000a 0000000 feff ac00 b098 000a 0000007 0000007 (Encode의 경우는 \x{ac00}\{b098}을 그대로 출력했더니 UTF-8과 동일하게 저장됐고, Text::Iconv 의 경우는 "UTF-8"을 "UNICODE"로 변환시켰더니만 UTF-16과 동일하게 저장됐음)
http://search.cpan.org/~dankogai/Encode-2.18/Unicode/Unicode.pm - perldoc Encode::Unicode 위 문서에 있는 내용에 따르면인코딩 명칭으로 "UTF-16(30)", "UTF-32(31)", "UCS-2(32)"를 사용할 경우, 두 모듈은 다르게 처리하더라...는 게 결국 힘빠지는 결말.iconv 쪽에서는 어느 문서를 봐야 하는지 알수가 없어 못 찾았지만, 저 설명대로라면 UTF-16(25) 이나 UTF-32(26) 는 Encode(27) 모듈은 제 맘대로 BE취급하고 Text::Iconv(28) 는 LE 취급한다 싶군요. 그건 그러려니 하더라도, UCS-2(29) 만큼은 두 모듈이 공히 BE로 처리해줘야 할 텐데 Iconv 쪽은 LE로 처리하는 것이 의아합니다. 뭐 해결은 못해도... 일단 현재 상황은 정확히 알게 되었으니 만족해야겠고...
- UTF-16(21)이나 UTF-32(22)는 뒤에 "BE"또는 "LE"를 생략하면 =>
- decode의 경우에는 BOM이 있는 지 체크해서 BOM이 있으면 그에 따라 처리하고, 없으면 루틴이 죽는다
- encode의 경우에는 BE로 인코딩한후 제일 앞에 BE에 해당하는 BOM을 붙인다.
- 단, UCS-2(23)의 경우는 예외로, 이건 IANA 등에도 등록되어 있으며 UCS-2BE(24)의 alias이다.
# 추측 if (eval "require Encode; require Encode::Guess;") { my @suspects = qw(euc-kr utf8); my $decoder = Encode::Guess::guess_encoding($string, @suspects); if (ref($decoder)) { # 추측 성공 return convert_encode($string, $decoder->name, $HttpCharset); } }
chcp 65001
라고 하면 된다던데, 왜 정작 한글을 출력해보면 이상하게 깨져 나올까...
코드값 = 0xAC00 + (초성값 * 21 * 28) + (중성값 * 28) + 종성값 초성값 : ㄱ=0, ㄲ=1, ㄴ=2 ... ㅎ=18 중성값 : ㅏ=0, ㅑ=1, ... ㅣ=20 종성값 : 없음 = 0, ㄱ = 1, ㄲ = 2...ㅎ=27
Encode 2, 4, 11, 19, 27 decode() 5 encode() 6 from_to() 7 | Text::Iconv 3, 12, 20, 28 | 인코딩 UCS-2 8, 16, 23, 29, 32 UCS-2BE 24 UTF-16 9, 14, 17, 21, 25, 30 UTF-32 10, 18, 22, 26, 31 UTF-8 13 변환 1, 15 추측 33 |