도입 — 문자 깨짐은 왜 일어나는가
파일의 실체는 바이트 열뿐. 「쓴 인코딩」과 「읽은 인코딩」이 어긋나면 같은 바이트 열이 다른 문자로 보인다 — 문자 깨짐의 정체를 여기서 다진다.
「縺薙s縺ォ縺。縺ッ」— 이 화면, 무슨 일이 일어나고 있는가
거래처에서 온 CSV 를 Excel 로 열었더니 일본어가 전부 「縺薙s縺ォ縺。縺ッ」 같은 한자 나열이 되어 있었다. 웹 페이지의 일부만 「ã“ã‚“」 같은 기호가 늘어선다. DB 에 등록했을 이름이 「????」가 되어 있다. — 대처로 「인코딩을 바꿔서 다시 열었더니 고쳐졌다」는 경험은 있어도, 왜 그것으로 고쳐지는가를 설명할 수 있는 사람은 의외로 적습니다.
출발점은 단 1 가지 사실입니다. 컴퓨터가 저장하거나 보낼 수 있는 것은 0x00〜0xFF 의 바이트의 나열뿐. 일본어 문자 「あ」나 「A」라는 문자 그 자체가 저장되는 것이 아닙니다. 문자를 바이트 열로 변환하는 규칙을 인코딩(부호화)이라 부르며, UTF-8 도 Shift_JIS 도 이 규칙의 일종입니다.
그리고 중요한 것이 다음 지점입니다. 파일에는 「이 바이트 열은 UTF-8 입니다」라는 표찰이 붙어 있지 않습니다. 읽는 쪽은 설정이나 선언이나 추측에 기대어 「아마 이 인코딩이겠지」라고 정하고 대응표를 찾습니다. 쓴 쪽의 규칙과 읽는 쪽의 규칙이 일치하면 원래 문자로 돌아오고, 어긋나면 — 다른 문자로 보입니다. 이것이 문자 깨짐입니다.
같은 3 바이트가, 찾아보는 대응표에 따라 「に」 「縺ォ」 「ã«」로 깨집니다. 바이트 열은 1 비트도 변하지 않았다는 점이 최대의 포인트입니다.
깨져도, 데이터는 깨지지 않았다
위의 그림을 다시 한번 보세요. E3 81 AB 라는 3 바이트는, 어느 방식으로 읽어도 완전히 같은 3 바이트 그대로입니다. 깨진 것은 데이터가 아니라 「읽는 방식의 선택」뿐. 그러므로 올바른 인코딩을 지정해서 다시 읽으면, 원래의 「に」가 그대로 돌아옵니다.
다만 1 가지만 주의가 있습니다. 깨진 모습 그대로 덮어써 저장해 버리면, 「縺ォ」라는 깨진 문자열이 새로운 바이트 열로 디스크에 기록되고, 원래 바이트 열은 사라집니다. 문자 깨짐을 만나면 「쓰지 말고, 먼저 읽는 방식을 의심한다」 — 이것이 실무에서의 첫 동작입니다.
이 강좌의 구호: 문자 깨짐을 보면 물어보세요 — 어느 인코딩으로 쓰인 바이트 열을, 어느 인코딩으로 읽어 버렸는가?
소문제 1-1 — 파일의 실체는 바이트 열뿐
문자 깨짐의 정체를, 바이트 열과 읽는 방식의 관계로 말할 수 있는지 확인합니다.
Q1. 텍스트 파일로 디스크에 저장되어 있는 「실체」는 무엇입니까.
디스크에 있는 것은 0x00〜0xFF 의 바이트의 나열뿐입니다. 「이것은 UTF-8 입니다」라는 표찰은 원칙적으로 붙어 있지 않고, 읽는 쪽이 어떤 근거(설정·선언·추측)로 인코딩을 정해서 읽습니다. 그래서 쓴 쪽과 읽는 쪽의 상정이 어긋날 여지가 생깁니다. 코드 포인트는 번호이며, 저장되는 것은 그것을 인코딩한 바이트 열입니다.
Q2. 같은 바이트 열 E3 81 AB 를 UTF-8 로 읽으면 일본어 문자 「に」, Shift_JIS 로 읽으면 「縺ォ」로 보입니다. 이 현상의 설명으로 올바른 것은 어느 것입니까.
인코딩이란 「바이트 열과 문자의 대응 규칙」 그 자체입니다. UTF-8 의 규칙으로 E3 81 AB 를 찾으면 U+306B 「に」, Shift_JIS 의 규칙으로 찾으면 E3 81 이 「縺」, AB 가 반각 가나 「ォ」가 됩니다. 데이터는 1 비트도 깨지지 않았습니다. 글꼴 문제(네모 □가 나오는 현상)와는 별개의 현상으로, 이 구별은 제 7 장에서 다시 다룹니다.
Q3. 문자가 깨진 파일에 대해, 대부분의 경우에 올바른 설명은 어느 것입니까.
깨짐의 대부분은 「읽는 방식의 어긋남」이므로 바이트 열은 무상합니다. 편집기에서 인코딩을 다시 지정해서 열면 원래대로 읽을 수 있습니다. 다만 깨진 모습 그대로 「저장」하면, 깨진 문자열이 새로운 바이트 열로 기록되어 복원이 단숨에 어려워집니다. 「깨지면 쓰지 말고, 먼저 읽는 방식을 의심한다」가 철칙입니다.
「번호를 매긴다」와 「바이트로 만든다」— 2 단계로 나누어 생각한다
여기서 이 강좌 전체를 관통하는 관점을 도입합니다. 문자가 바이트 열이 되기까지에는, 실은 2 개의 독립된 단계가 있습니다.
「Unicode 와 UTF-8 은 뭐가 달라?」라는 단골 의문은, 이 2 단계로 즉답할 수 있습니다. Unicode 는 제 1 단계(번호를 매기는 방식), UTF-8 은 제 2 단계(번호를 바이트로 바꾸는 방식). 토지의 번지를 정하는 방식과, 번지를 봉투에 어떻게 쓰는가의 차이라고 해도 좋겠습니다. 제 2 장에서 제 1 단계를, 제 3 장 이후에서 제 2 단계를 각각 차분히 다룹니다.
소문제 1-2 — 문자 집합과 인코딩을 구별한다
이 강좌를 관통하는 2 단계 — 번호를 매긴다(문자 집합)와 바이트로 만든다(인코딩) — 의 구별을 확인합니다.
Q4. 「문자 집합」과 「인코딩」의 역할 분담으로 올바른 것은 어느 것입니까.
문자 집합(예: Unicode)은 일본어 문자 「あ」 = U+3042 처럼 문자에 번호를 매기는 제 1 단계, 인코딩(예: UTF-8)은 U+3042 → E3 81 82 처럼 번호를 바이트 열로 변환하는 제 2 단계입니다. 역사적으로는 양쪽이 일체인 규격도 있었지만, Unicode 의 세계에서는 이 2 단계가 명확히 나뉘어 있으며, 나누어 생각하면 문자 깨짐의 논의가 단숨에 정리됩니다.
Q5. 「이 파일은 Unicode 로 저장되어 있습니다」라는 표현이 부정확한 이유는 어느 것입니까.
같은 Unicode 번호 U+3042 라도, UTF-8 이라면 E3 81 82, UTF-16 이라면 30 42 로, 저장되는 바이트 열은 인코딩에 따라 다릅니다. 「Unicode 로 저장」으로는 제 2 단계가 특정되지 않아, 파일을 올바르게 읽기 위한 정보로 부족합니다. 편집기의 저장 대화 상자에 UTF-8 이나 UTF-16 이 나란히 있는 것은 이 제 2 단계의 선택지입니다.
이 장에서 가져갈 것
- 파일의 실체는 바이트 열뿐. 「무슨 인코딩인가」의 표찰은 붙어 있지 않다
- 문자 깨짐의 정체는 「쓴 인코딩」과 「읽은 인코딩」의 어긋남. 바이트 열 자체는 깨지지 않은 경우가 많고, 읽는 방식을 고치면 돌아온다(단, 깨진 채로 저장하지 않는다)
- 문자는 번호(코드 포인트) → 바이트 열(인코딩)의 2 단계로 다루어진다. Unicode 는 제 1 단계, UTF-8 은 제 2 단계
다음 장에서는 제 1 단계 「번호를 매긴다」의 역사를, ASCII 의 128 문자에서 세계의 모든 문자를 담는 Unicode 까지 단숨에 따라갑니다.