Perl Unicode FAQ

Perl Unicode FAQ (perlunifaq)

Perl에서 Unicode를 다루면서 자주 나오는 질문과 답을 정리한 문서예요. perlunitut를 읽고 나서 보는 걸 전제로 해요.

출처: perldoc - perlunifaq

질문과 답 (Q and A)

perlunitut가 정말 Unicode 튜토리얼은 아니죠? (perlunitut isn't really a Unicode tutorial, is it?)

아니요. 그리고 이것도 정말 Unicode FAQ는 아니에요.

Perl은 지원되는 모든 문자 인코딩에 대한 추상화된 인터페이스를 갖고 있어서, 사실 이것은 일반적인 Encode 튜토리얼이자 Encode FAQ예요. 하지만 많은 사람이 Unicode를 특별하고 신비로운 것으로 생각하길래, 그들을 실망시키고 싶지 않아서 문서 이름을 Unicode 튜토리얼이라고 정했어요.

Perl은 어떤 문자 인코딩을 지원하나요? (What character encodings does Perl support?)

Perl이 지원하는 문자 인코딩을 보려면 실행하세요:

perl -MEncode -le "print for Encode->encodings(':all')"

어떤 perl 버전을 써야 하나요? (Which version of perl should I use?)

가능하면 가장 최신 버전으로 업그레이드하되, 최소한 5.8.1 이상은 쓰세요. 튜토리얼과 FAQ는 최신 릴리스를 전제로 해요.

모듈도 확인하고 필요하면 업그레이드하세요. 예를 들어 HTML::Entities는 changelog에 안 밝혀져 있지만, 제대로 동작하려면 버전 >= 1.32가 필요해요.

이미지 같은 이진 데이터는요? (What about binary data, like images?)

binmode $fh 외에는 특별히 취급할 게 없어요. (binmode가 필요한 이유는, 그게 없으면 Perl이 Win32 시스템에서 줄바꿈을 변환할 수 있기 때문이에요.)

하지만 텍스트 문자열과 이진 문자열을 절대 섞지 마세요. 이진 스트림에 텍스트가 필요하다면 텍스트 문자열을 먼저 적절한 인코딩으로 인코딩한 뒤 이진 문자열과 결합하세요. "인코딩하지 않으면요?"도 참고.

언제 디코드하거나 인코드해야 하나요? (When should I decode or encode?)

perl 프로세스 외부의 무엇(데이터베이스, 텍스트 파일, 소켓, 다른 프로그램)과 텍스트를 주고받을 때마다요. 상대가 Perl로 작성됐더라도 마찬가지예요.

디코드하지 않으면 어떻게 되나요? (What if I don't decode?)

인코딩된 이진 문자열이 텍스트 문자열과 함께 쓰일 때마다 Perl은 그 이진 문자열이 ISO-8859-1(latin-1)로 인코딩됐다고 가정해요. latin-1이 아니었다면 데이터가 불쾌하게 변환돼요. 예를 들어 UTF-8이었다면 멀티바이트 문자의 개별 바이트가 별개의 문자로 보이고, 다시 UTF-8로 변환돼요. 이런 이중 인코딩은 이중 HTML 인코딩(>)이나 이중 URI 인코딩(%253E)에 비유할 수 있어요.

이런 조용한 암묵적 디코딩을 **"업그레이드(upgrading)"**라고 불러요. 긍정적으로 들릴 수도 있지만 피하는 게 최선이에요.

인코드하지 않으면 어떻게 되나요? (What if I don't encode?)

무엇을 어떻게 출력하느냐에 따라요.

파일핸들을 통한 출력 (Output via a filehandle)

  • 문자열의 문자가 모두 코드 포인트 255 이하이면 Perl은 그 코드 포인트에 해당하는 바이트를 출력해요. 인코딩된 문자열에서 일어나는 일이죠. 우연히 전부 코드 포인트 255 이하인 인코딩되지 않은 문자열에서도 일어날 수 있어요.
  • 그 외에는 Perl이 문자열을 UTF-8로 인코딩해 출력해요. 이는 인코딩하는 걸 소홀히 한 문자열에서만 일어나요. 그런 일은 없어야 하므로, Perl은 이 경우 "wide character" 경고도 던져요.

다른 출력 메커니즘 (예: exec, chdir, ..)

텍스트 문자열은 Perl 내부 형식의 바이트를 사용해 보내져요.

내부 형식이 흔히 UTF-8이라서 이런 버그는 알아채기 어려워요. UTF-8이 보통 원하는 인코딩이니까요! 하지만 게으르지 마세요. Perl 내부 형식이 UTF-8이라는 사실을 이용하지 마세요. 이상한 버그를 피하고, 유지보수 프로그래머에게 신중히 생각했음을 보여주려면 명시적으로 인코드하세요.

자동으로 디코드·인코드하는 방법이 있나요? (Is there a way to automatically decode or encode?)

특정 핸들에서 오는 모든 데이터가 정확히 같은 방식으로 인코딩된다면, PerlIO 시스템에 encoding 레이어로 모든 것을 자동 디코드하라고 말할 수 있어요. 그렇게 하면 레이어가 있는 핸들을 쓰는 것에서 디코드·인코드를 깜빡 잊을 일이 없어요.

파일을 열 때 이 레이어를 제공할 수 있어요:

open my $fh, '>:encoding(UTF-8)', $filename;  # auto encoding on write
open my $fh, '<:encoding(UTF-8)', $filename;  # auto decoding on read

이미 열린 파일핸들이 있으면:

binmode $fh, ':encoding(UTF-8)';

DBI용 일부 데이터베이스 드라이버도 자동으로 인코드·디코드하지만, 때로 UTF-8 인코딩으로 제한될 수 있어요.

어떤 인코딩이 쓰였는지 모르면요? (What if I don't know which encoding was used?)

뭘 해서든 알아내려고 하고, 할 수 없으면 추측하세요. (추측한 사실을 주석으로 문서화하는 걸 잊지 마세요.)

문서를 웹 브라우저에서 열고, 모든 문자가 제대로 보이는 걸 시각적으로 확인할 때까지 문자 집합·문자 인코딩을 바꿔 보세요.

인코딩을 자동으로 확실하게 감지하는 방법은 없어요. 그래서 사람들이 문자 집합 표시 없이 데이터를 계속 보낸다면 그들에게 교육해야 할 수도 있어요.

Perl 소스에서 Unicode를 쓸 수 있나요? (Can I use Unicode in my Perl sources?)

네, 쓸 수 있어요! 소스가 UTF-8로 인코딩됐다면 use utf8 pragma로 그걸 표시할 수 있어요:

use utf8;

이건 입력에도 출력에도 아무것도 하지 않아요. 소스를 읽는 방식에만 영향을 줘요. 문자열 리터럴, 식별자(물론 \w에 따른 "단어 문자"여야 해요), 심지어 사용자 정의 구분자에도 Unicode를 쓸 수 있어요.

Data::Dumper가 UTF8 플래그를 복원하지 않아요. 고장난 건가요? (Data::Dumper doesn't restore the UTF8 flag; is it broken?)

아니요. Data::Dumper의 Unicode 능력은 그래야 하는 대로예요. 평가(eval)로 데이터를 다시 읽을 때 UTF8 플래그를 복원해야 한다는 불만이 있었지만, 정말로 플래그를 봐서는 안 되고, Data::Dumper가 이 규칙을 깨야 한다는 표시도 없어요.

무슨 일이 일어나나면: Perl이 문자열 리터럴을 읽을 때 가능한 한 8비트 인코딩을 고수해요. (dump했을 때 원래는 내부적으로 UTF-8로 인코딩됐을지라도요.) 텍스트 문자열에 다른 문자가 더해져 그것을 포기해야 할 때, 문자열을 조용히 UTF-8로 업그레이드해요.

출력용 문자열을 제대로 인코드했다면 이것 중 어느 것도 신경 쓸 일이 아니고, 예전처럼 dump된 데이터를 그냥 eval하면 돼요.

정규식 문자 클래스가 때로 ASCII 범위에서만 매치되는 이유는요? (Why do regex character classes sometimes match only in the ASCII range?)

Perl 5.14부터(그리고 5.12에서 부분적으로) 프로그램 앞부분에 use feature 'unicode_strings'를 넣기만 하면 돼요. 그 어휘 범위 안에서는 이 문제가 없어야 해요. use feature ':5.12'use v5.12 아래, 또는 명령줄에서 Perl 5.12 이상을 -E로 쓰면 자동으로 켜지기도 해요.

이걸 요구하는 이유는 Unicode가 등장하기 전 방식에 의존하는 옛 프로그램을 깨지 않게 하려는 거예요. 그 옛 프로그램은 ASCII 문자 집합만 알았으므로 추가 문자에 대해 제대로 동작하지 않을 수 있어요. 문자열이 UTF-8로 인코딩되면 Perl은 프로그램이 Unicode를 다룰 준비가 됐다고 가정하지만, 그렇지 않으면 ASCII만 원한다고 가정해서, ASCII가 아닌 문자가 Unicode에서 무엇인지로 인식되지 않아요. use feature 'unicode_strings'는 문자열이 UTF-8로 인코딩됐든 아니든 Perl이 모든 문자를 Unicode로 취급하도록 해서 문제를 피해요.

하지만 이전 Perl에서, 또는 기능 범위 밖의 서브루틴에 문자열을 넘길 때는 인코딩을 utf8::upgrade($string)으로 UTF-8로 바꿔 Unicode 규칙을 강제할 수 있어요. 이는 이미 업그레이드된 문자열을 검사하고 바꾸지 않으므로 어떤 문자열에든 안전하게 쓸 수 있어요.

더 자세한 논의는 CPAN의 Unicode::Semantics를 참고하세요.

어떤 문자가 대문자·소문자로 제대로 바뀌지 않는 이유는요? (Why do some characters not uppercase or lowercase correctly?)

앞 질문의 답을 참고하세요.

문자열이 텍스트 문자열인지 이진 문자열인지 어떻게 판별하나요? (How can I determine if a string is a text string or a binary string?)

할 수 없어요. 어떤 이는 UTF8 플래그로 판별하지만, 그건 오용이고 Data::Dumper 같은 잘 동작하는 모듈을 나쁘게 보이게 해요. 8비트 인코딩(기본 ISO-8859-1)으로 문자열을 저장할 때는 플래그가 꺼져 있으므로, 이 목적에는 플래그가 쓸모없어요.

이건 당신, 프로그래머가 추적해야 할 일이에요. 미안해요. 이를 돕기 위한 일종의 "헝가리안 표기"를 채택하는 걸 고려해 볼 수 있어요.

FOO 인코딩에서 BAR 인코딩으로 어떻게 변환하나요? (How do I convert from encoding FOO to encoding BAR?)

먼저 FOO 인코딩 바이트 문자열을 텍스트 문자열로, 그다음 텍스트 문자열을 BAR 인코딩 바이트 문자열로:

my $text_string = decode('FOO', $foo_string);
my $bar_string  = encode('BAR', $text_string);

또는 텍스트 문자열 단계를 건너뛰고 한 이진 인코딩에서 다른 것으로 직접:

use Encode qw(from_to);
from_to($string, 'FOO', 'BAR');  # changes contents of $string

또는 자동 디코드·인코드가 모든 일을 하게:

open my $foofh, '<:encoding(FOO)', 'example.foo.txt';
open my $barfh, '>:encoding(BAR)', 'example.bar.txt';
print { $barfh } $_ while <$foofh>;

decode_utf8와 encode_utf8은 무엇인가요? (What are decode_utf8 and encode_utf8?)

이것들은 decode('utf8', ...)encode('utf8', ...)의 대체 문법이에요. 데이터 교환에는 이 함수를 쓰지 마세요. 대신 decode('UTF-8', ...)encode('UTF-8', ...)를 쓰세요. 아래 "UTF-8과 utf8의 차이"를 참고하세요.

"wide character"란 무엇인가요? (What is a "wide character"?)

한 바이트보다 많은 공간을 차지하는 문자를 일컫는 용어예요.

Perl 경고 "Wide character in ..."은 그런 문자 때문에 발생해요. 인코딩 레이어를 지정하지 않으면 Perl은 한 바이트에 맞추려 해요. 그럴 수 없으면 이 경고(warnings가 켜져 있을 때)를 내고 UTF-8 인코딩 데이터를 대신 써요.

이 경고를 피하고 한 스트림에 서로 다른 출력 인코딩을 갖지 않으려면, 예를 들어 PerlIO 레이어로 항상 인코딩을 명시적으로 지정하세요:

binmode STDOUT, ":encoding(UTF-8)";

내부 (INTERNALS)

"UTF8 플래그"란 무엇인가요? (What is "the UTF8 flag"?)

내부를 해킹하거나 이상함을 디버깅하는 게 아니라면, UTF8 플래그에 대해 전혀 생각하지 마세요.is_utf8, _utf8_on, _utf8_off를 아예 쓰지 말아야 할 가능성이 매우 높아요.

UTF8 플래그(SvUTF8이라고도 함)는 현재 내부 표현이 UTF-8임을 나타내는 내부 플래그예요. 플래그가 없으면 ISO-8859-1로 가정돼요. Perl은 이 둘 사이를 자동으로 변환해요. (사실 Perl은 보통 표현을 ASCII로 가정해요. 위 "정규식 문자 클래스가 때로 ASCII 범위에서만 매치되는 이유는요?" 참고.)

Perl의 내부 형식 중 하나가 우연히 UTF-8이에요. 불행히도 Perl은 비밀을 지키지 못해서 모두가 이것을 알아요. 혼란의 큰 원인이죠. 내부 형식이 어떤 알 수 없는 인코딩인 척하고, 항상 명시적으로 인코드·디코드해야 한다고 생각하는 게 낫다는 뜻이에요.

use bytes pragma는요? (What about the use bytes pragma?)

쓰지 마세요. 텍스트 문자열에서 바이트를 다루는 건 말이 안 되고, 바이트 문자열에서 문자를 다루는 것도 말이 안 돼요. 제대로 변환(디코드/인코드)하면 모든 게 잘 풀려요 — 디코드된 데이터에선 문자 수를, 인코드된 데이터에선 바이트 수를 얻을 수 있어요.

use bytes는 대개 뭔가 유용한 걸 하려다 실패한 시도예요. 그냥 잊어버리세요.

use encoding pragma는요? (What about the use encoding pragma?)

쓰지 마세요. 불행히도 프로그래머의 환경과 사용자의 환경이 같은 인코딩을 쓸 거라고 가정해요. 소스 코드와 STDIN, STDOUT에 같은 인코딩을 쓰게 되죠. 프로그램이 다른 머신으로 복사되면 소스 코드는 바뀌지 않지만 STDIO 환경은 바뀔 수 있어요.

소스 코드에 non-ASCII 문자가 필요하다면 UTF-8 인코딩 파일로 만들고 utf8을 쓰세요. 사용자의 locale 같은 것에 따라 STDIN·STDOUT·STDERR의 인코딩을 설정해야 한다면 open을 쓰세요.

:encoding과 :utf8의 차이는 무엇인가요? (What is the difference between :encoding and :utf8?)

UTF-8이 Perl의 내부 형식 중 하나이므로, 종종 인코딩·디코딩 단계를 건너뛰고 UTF8 플래그를 직접 조작할 수 있어요.

:encoding(UTF-8) 대신 :utf8을 간단히 쓸 수 있는데, 데이터가 이미 내부적으로 UTF8로 표현됐다면 인코딩 단계를 건너뛰어요. 이는 쓸 때는 좋은 동작으로 널리 받아들여지지만, 읽을 때는 위험할 수 있어요. 잘못된 바이트 시퀀스가 있을 때 내부 불일치를 일으키거든요. 입력에 :utf8을 쓰면 때로 보안 침해로 이어질 수 있으니, 대신 :encoding(UTF-8)을 쓰세요.

decode·encode 대신 _utf8_on·_utf8_off를 쓸 수도 있지만, 이는 나쁜 스타일로 여겨져요. 특히 _utf8_on:utf8과 같은 이유로 위험할 수 있어요.

원라이너를 위한 단축키도 있는데 perlrun의 -C를 참고하세요.

UTF-8과 utf8의 차이는 무엇인가요? (What's the difference between UTF-8 and utf8?)

UTF-8은 공식 표준이에요. utf8은 Perl이 받아들이는 데 자유로운 방식이에요. 그렇게 자유롭지 않은 것과 소통해야 한다면 UTF-8을 쓰는 걸 고려해 보세요. 너무 자유로운 것과 소통해야 한다면 utf8을 써야 할 수도 있어요. 전체 설명은 Encode의 "UTF-8 vs. utf8 vs. UTF8"에 있어요.

UTF-8은 내부적으로 utf-8-strict로 알려져 있어요. 튜토리얼은 내부적으로 실제로 utf8이 쓰이는 곳에서도 일관되게 UTF-8을 쓰는데, 그 구별이 어려울 수 있고 대부분 무관하기 때문이에요.

예를 들어 utf8은 Unicode에 존재하지 않는 코드 포인트(9999999 같은)에도 쓸 수 있지만, 그걸 UTF-8로 인코드하면 치환 문자를 얻게 돼요(기본 동작. 다른 처리 방법은 Encode의 "Handling Malformed Data" 참고).

좋아요, 꼭 알고 싶다면: "내부 형식"은 UTF-8이 아니라 utf8이에요. (다른 인코딩이 아닐 때 말이죠.)

길을 잃었어요. 내부 형식이 정말 어떤 인코딩인가요? (I lost track; what encoding is the internal format really?)

길을 잃은 게 잘한 일이에요. 내부 형식이 특정 인코딩이라는 것에 의존하면 안 되거든요. 하지만 물었으니: 기본적으로 내부 형식은 문자열의 이력에 따라 ISO-8859-1(latin-1) 또는 utf8이에요. EBCDIC 플랫폼에서는 이것도 다를 수 있어요.

Perl은 문자열을 내부적으로 어떻게 저장했는지 알고, 인코드할 때 그 지식을 써요. 다시 말해: 특정 문자열의 내부 인코딩이 뭔지 알아내려 하지 말고, 그냥 원하는 인코딩으로 인코드하세요.

함께 보기 (SEE ALSO)

perlunicode, perluniintro, Encode

더 알아보기 (Learn more)

  • perluniintro — Perl Unicode 입문
  • perlunicode — Perl의 Unicode 지원 상세
  • perlrun-C — 원라이너 Unicode 단축키