종합 연습 — 문자 깨짐을 추리한다
깨짐새에는 지문이 있다. 실무의 장애 조사를 본뜬 8 개의 케이스로, 「어느 인코딩으로 쓰인 바이트 열을, 어느 인코딩으로 읽었는가」를 전 장의 지식으로 추리한다.
마무리의 8 문제 — 깨진 화면은 「증거」이다
마지막 장은 종합 연습입니다. 지금까지의 장에서, 깨짐새에는 지문이 있음을 배웠습니다. 「縺」 「繧」 「繝」가 늘어서면 UTF-8 의 Shift_JIS 오독, ã 가 늘어서면 Latin-1 오독, � 의 연발이라면 Shift_JIS 계의 UTF-8 오독, ? 로의 치환이라면 변환 시의 데이터 상실. 깨진 화면은 더 이상 의미 불명의 기호가 아니라, 오독의 조합을 말해 주는 증거입니다.
8 개의 케이스 모두에서, 구호로 되돌아가 주세요 — 어느 인코딩으로 쓰인 바이트 열을, 어느 인코딩으로 읽어 버렸는가?
| 막히면 | 돌아갈 장 |
|---|---|
| 바이트 열과 읽는 방식·문자 집합과 인코딩의 구별 | 제 1 장 |
| 코드 포인트·U+XXXX 의 읽는 법 | 제 2 장 |
| UTF-8 의 바이트 구조·E3 의 의미·BOM | 제 3 장 |
| Shift_JIS 의 2 바이트 구조·다메 문자·CP932 | 제 4 장 |
| 서로게이트 페어·length 세는 법 | 제 5 장 |
| 선언과 실체·Excel 과 BOM·DB 의 연결 설정 | 제 6 장 |
4 가지 단골 패턴. 왼쪽 위와 오른쪽 위는 다시 읽으면 복원할 수 있는 「오독」, 오른쪽 아래는 원본 데이터가 사라진 「치환」 — 구별이 복구 가부의 판단에 직결됩니다.
추리를 지탱하는 3 가지 사실(복습)
- 히라가나의 UTF-8 은 E3 81 / E3 82 / E3 83 으로 시작한다(제 3 장). 이 2 바이트는 Shift_JIS 의 표에서는 「縺」 「繧」 「繝」 — 그래서 UTF-8 → Shift_JIS 오독에서는 이 3 문자가 대량 발생한다. 예를 들어 일본어 단어 「文字化け(문자 깨짐)」 자체를 오독하면 「譁�ュ怜喧縺�」가 된다
- Shift_JIS 의 2 번째 바이트는 ASCII 영역에 파고든다(제 4 장). UTF-8 로 읽으면 대부분이 불법이 되어 �(환경에 따라서는 ? 표시)가 되고, 2 번째 바이트의 영문자만이 파편으로 남는다
- Latin-1 계는 1 바이트 = 1 문자라서 반드시 「읽혀」 버린다. UTF-8 의 3 바이트가 3 문자의 라틴 문자로 부풀고, E3 유래의 ã 가 표지가 된다
그럼, 시작합시다.
케이스 7-1 — 깨짐새의 지문을 읽는다
제 1·3·4 장의 범위입니다. 깨진 문자의 나열 그 자체가 증거가 됩니다.
Q1. 케이스: 거래처에서 온 CSV 를 열었더니 「縺薙s縺ォ縺。縺ッ」처럼, 「縺」 「繧」 「繝」와 반각 가나가 섞인 문자열이 늘어서 있었습니다. 가장 유력한 추리는 어느 것입니까.
히라가나의 UTF-8 바이트 열은 E3 81 / E3 82 / E3 83 으로 시작하고, 이 2 바이트를 Shift_JIS 의 표로 찾으면 각각 「縺」 「繧」 「繝」가 됩니다. 그래서 UTF-8 의 일본어를 Shift_JIS 로 오독하면, 이 3 문자가 비정상적인 빈도로 나타나고, 남은 바이트가 반각 가나(ォ 나 ッ 등)로 보입니다. 반대 방향(Shift_JIS 를 UTF-8 로 읽기)이라면 다음 문제처럼 대체 문자투성이가 되므로, 깨짐새의 겉모습만으로 방향까지 특정할 수 있습니다.
Q2. 케이스: 다른 파일에서는 일본어 「テスト」라고 쓰여 있었을 곳이 「�e�X�g」 — 대체 문자 「�」 사이에 드문드문 영문자가 남는 형태로 깨져 있었습니다. 가장 유력한 추리는 어느 것입니까.
「テスト」의 Shift_JIS 는 83 65 83 58 83 67. UTF-8 로 읽으면, 0x83 은 「이어지는 바이트」의 형태를 한 고립 바이트라서 불법이 되어 �(U+FFFD)로 대체됩니다. 한편 2 번째 바이트인 65(e), 58(X), 67(g)는 ASCII 범위이므로 그대로 살아남는다 — 제 4 장에서 배운 「Shift_JIS 의 2 번째 바이트는 ASCII 영역에 파고든다」가 여기서 지문이 됩니다. 「� 가 다발 + 영문자의 파편」은 Shift_JIS 계를 UTF-8 로 읽은 사인입니다.
Q3. 케이스: 해외제 도구를 거친 일본어 텍스트가 「ã“ã‚“ã«ã¡ã¯」 같은, 악센트 붙은 라틴 문자의 나열이 되었습니다. 게다가 원래의 1 문자당 3 문자 전후로 부풀어 있습니다. 가장 유력한 추리는 어느 것입니까.
일본어의 UTF-8 은 1 문자 3 바이트이고, 선두 바이트는 거의 E3〜E9. Latin-1 계의 표에서는 E3 가 ã, E5 가 å, E6 가 æ 에 해당하기 때문에, 「ã 등이 3 문자 간격으로 나타나고, 문자 수가 약 3 배로 부푼다」는 지문이 나옵니다(사이의 바이트 일부는 눈에 보이지 않는 제어 코드가 됩니다). 1 바이트 = 1 문자의 표로 읽고 있으므로 대체 문자는 나오기 어렵고, q2 패턴과의 구별 포인트입니다. charset 선언을 무시·결락시킨 해외제 도구나 메일 소프트웨어의 단골 깨짐새입니다.
케이스 7-2 — 현장의 장애를 구분한다
제 4〜6 장의 범위입니다. CSV·메일·치환 — 증상의 차이에서 원인의 층을 구분해 주세요.
Q4. 케이스: 자사 시스템이 출력한 UTF-8(BOM 없음)의 CSV 에 대해, 「Excel 에서 더블 클릭하면 일본어가 『縺薙s…』처럼 깨진다」는 문의가 왔습니다. 원인과 대책의 조합으로 올바른 것은 어느 것입니까.
「縺」의 지문에서 UTF-8 을 Shift_JIS(CP932)계로 읽었다고 추리할 수 있고, 더블 클릭으로 열린 Excel 의 기본 동작과 부합합니다. 선언의 구조가 없는 CSV 에서는, BOM(EF BB BF)이 몇 안 되는 「실체에 심을 수 있는 라벨」이며, 이것을 붙이는 것이 정석입니다(제 6 장). 그리고 이 경우에도 파일의 실체는 무상하므로, 이용자 쪽에서도 가져오기 기능으로 UTF-8 을 명시하면 올바르게 읽을 수 있습니다.
Q5. 케이스: 고객 명부를 이행했더니, 일부 고객명이 「????」가 되어 있었습니다. 「縺薙s…」형의 깨짐과의 결정적인 차이는 어느 것입니까.
오독(縺薙s형)은 바이트 열이 무상하므로 가역이지만, ? 로의 치환은 변환 처리가 「나타낼 수 없는 문자」를 ? 라는 다른 문자로 바꿔 써서 저장한 결과로, 비가역입니다. ① 이나 髙 같은 CP932 의 기종 의존 문자를 엄밀한 변환에 돌렸을 때 등에 일어납니다(제 4 장). 대처도 달라서, 오독이라면 다시 읽기, 치환이라면 이행 원본 데이터로부터의 재투입이 필요합니다. 「깨짐」과 「치환」을 구별해 말할 수 있는 것이, 복구 가부의 판단에 직결됩니다.
Q6. 케이스: 수신한 메일의 본문은 정상인데, 제목만이 「=?UTF-8?B?...?=」 같은 기호열이나 의미 불명의 문자가 되어 있습니다. 생각할 수 있는 원인으로 가장 적절한 것은 어느 것입니까.
메일의 헤더는 역사적으로 ASCII 밖에 허용되지 않았기 때문에, 일본어 제목은 「=?인코딩명?B?Base64 데이터?=」라는 MIME 인코딩으로 싸서 보내집니다. 제목과 본문에서는 인코딩의 선언 장소가 다르므로, 한쪽만 깨지는 것은 오히려 자연스러운 증상입니다. 수신 쪽이 MIME 을 해석하지 못한다·선언과 실체가 어긋나 있다·도중의 시스템이 헤더를 망가뜨렸다, 등이 전형적 원인으로, 「선언과 실체」의 사고방식(제 6 장)이 헤더 단위로 적용되는 예입니다.
케이스 7-3 — 추리의 총마무리
전 장 횡단입니다. 구호 — 어느 인코딩으로 쓰인 바이트 열을, 어느 인코딩으로 읽었는가 — 로 되돌아가 주세요.
Q7. 케이스: Shift_JIS 전제의 오래된 연계 프로그램에 「ソフト一覧.csv」라는 데이터를 통과시키면, 가타카나 「ソ」를 포함하는 행만 열이 어긋나게 들어옵니다. 가장 유력한 원인은 어느 것입니까.
「특정 문자를 포함하는 행만 깨진다」는, 제 4 장의 다메 문자의 지문입니다. 「ソ」(83 5C)의 2 번째 바이트가 백슬래시와 같은 값이기 때문에, 바이트 단위로 이스케이프 처리를 하는 프로그램이 문자의 중간을 기호로 오해합니다. 대처는, Shift_JIS 를 올바르게 인식하는 라이브러리로 처리한다·주고받기를 UTF-8 로 바꾼다, 등 「문자의 경계를 올바르게 다루는」 방향입니다. 가타카나 자체가 나쁜 것도, 자리 넘침도 아닙니다.
Q8. 케이스: EUC-JP 로 축적된 구 시스템의 데이터를 UTF-8 의 신 시스템으로 이행합니다. 올바른 절차는 어느 것입니까.
강좌의 총정리입니다. 선언은 라벨에 지나지 않고(제 6 장), 실체인 바이트 열은 변환 처리를 해야 비로소 변합니다. 변환이란 「EUC-JP 의 표로 읽어 코드 포인트(번호)로 되돌리고, UTF-8 의 규칙으로 바이트 열을 다시 만든다」는, 2 단계(제 1 장)를 경유하는 이사입니다. 깨진 뒤에 고치는 것이 아니라, 경계를 넘기 전에 올바르게 변환한다 — 「쓴 인코딩」과 「읽은 인코딩」을 계속 일치시키는 것이, 문자 코드 실무의 전부입니다.
강좌를 마치며 — 2 단계와 1 가지 물음을 지니고 다닌다
수고하셨습니다. 이 강좌에서 암기해 달라고 한 대응표는, 실은 겨우 몇 줄입니다. 대신 가져가 주었으면 하는 것은, 어떤 장면에서도 통용되는 관점이었습니다.
- 문자는 항상 번호(코드 포인트) → 바이트 열(인코딩)의 2 단계로 다루어진다
- 파일의 실체는 바이트 열뿐. 선언(라벨)은 읽는 방식의 지시에 지나지 않으며, 실체를 변환하지 않는다
- 깨지면 묻는다 — 어느 인코딩으로 쓰인 바이트 열을, 어느 인코딩으로 읽어 버렸는가. 깨짐새의 지문이 방향을 가르쳐 준다
- 오독은 가역, 치환(?)은 비가역. 깨진 채로 저장하지 않는다
다음에 문자 깨짐을 만났을 때, 당신은 이제 「인코딩을 적당히 바꿔서 다시 여는 사람」이 아닙니다. 증거로부터 오독의 조합을 특정하고, 선언과 실체 중 어느 쪽을 고쳐야 할지 판단할 수 있는 사람입니다. 제 3 장의 시뮬레이터는 앞으로도 자유롭게 사용할 수 있습니다 — 수상한 바이트 열을 만나면, 꼭 직접 재현해 보세요.