文字コードや文字化けについて、日頃自分がつまずくポイントを整理してみました。
前半では文字コードの仕組み、後半では文字化けの原因と対処方法を整理しました。すぐに対処したい場合は、「文字化けが起きたときの基本手順」から読んで頂くと参考になると思います。
文字コードの仕組み
人間は、ひらがな、カタカナ、漢字、アルファベット、数字、記号などを文字として認識できます。一方、コンピュータでは、それらの文字を数値やバイト列に変換して扱います。
「文字コード」は、文字と数値を対応させ、データとして保存・送受信するための仕組みをまとめて指す言葉として使われています。
文字集合と符号化方式
文字コードを理解するうえで重要なのが、文字集合と符号化方式の違いです。
- 文字集合:使用できる文字を集め、それぞれに番号を割り当てたもの
- 符号化方式:文字に割り当てた番号を、実際のバイト列として表現する方法
たとえば、Unicodeでは各文字にコードポイントが割り当てられています。そのUnicodeをファイルや通信で扱うための符号化方式として、UTF-8、UTF-16、UTF-32などがあります。
同じUnicodeの文字でも、符号化方式が異なればバイト列は変わります。たとえば「A」のコードポイントはU+0041ですが、UTF-8では41の1バイト、UTF-16BEでは00 41の2バイトで表現されます。
文字集合は「どの文字に何番を割り当てるか」、符号化方式は「その番号をどのようなバイト列で保存するか」と考えると分かりやすいでしょう。
文字とバイト列の結びつけ
テキストエディタやアプリケーションは、ファイル内のバイト列を指定された符号化方式で読み取り、対応する文字として画面に表示します。
反対に、ファイルを保存するときは、画面上の文字を選択された符号化方式のバイト列へ変換します。
UTF-8はASCIIと互換性があるため、ASCIIで定義された英数字や一部の記号は同じバイト値で表現されます。ただし、日本語などASCIIに含まれない文字の表現方法は、符号化方式によって異なります。
主要な文字コード
代表的な文字集合と符号化方式を整理すると、次のようになります。
| 文字集合・規格 | 符号化方式 | 主な用途 | 主な特徴 |
|---|---|---|---|
| ASCII | ASCII | 英数字・記号 | 0から127までの7ビットで表現し、制御文字も含む |
| JIS X 0201・JIS X 0208など | Shift_JIS | 日本語のレガシー環境 | ASCII部分と半角カナは1バイト、ひらがなや漢字などは2バイト |
| JIS X 0201・JIS X 0208など | Windows-31J(CP932) | 日本語版Windowsのレガシー環境 | Shift_JISをもとに、NEC・IBM拡張文字などを追加したMicrosoftのコードページ |
| JIS X 0201・JIS X 0208など | EUC-JP | UNIX系の日本語レガシー環境 | ASCIIと共存できる形で日本語を符号化する |
| Unicode | UTF-8 | Web、Linux、macOSなど | 1から4バイトの可変長で、ASCIIと互換性がある |
| Unicode | UTF-16 | Windows APIや一部のアプリケーション | 16ビットのコード単位を使用する |
ASCII(American Standard Code for Information Interchange)は、英語のアルファベット、数字、記号、改行やタブなどの制御文字を扱う規格です。
Shift_JISは、日本語を扱うために広く使われてきた符号化方式です。半角カナは1バイト、ひらがなや漢字などの全角文字は基本的に2バイトで表現します。
Windows-31Jは、Shift_JISをもとにMicrosoftが拡張したコードページです。CP932やMS932と呼ばれることもあります。Shift_JISとWindows-31Jは完全に同じではないため、機種依存文字や一部の記号を扱うときに違いが問題となることがあります。
EUC-JPも日本語を扱う符号化方式ですが、ISO-2022-JPのスーパーセットではありません。同じ文字集合を利用する部分があっても、バイト列への変換方法は異なります。
Unicodeは、世界中の文字を統一的に扱うための規格です。UTF-8はUnicodeをバイト列へ変換する符号化方式であり、現在のWebやLinuxなどで広く使われています。
主要文字コードの具体的なマッピング例
アルファベットの「A」と、ひらがなの「あ」を比較すると次のようになります。
| 文字 | Unicodeコードポイント | UTF-8 | Shift_JIS・Windows-31J | EUC-JP |
|---|---|---|---|---|
| A | U+0041 |
41 |
41 |
41 |
| あ | U+3042 |
E3 81 82 |
82 A0 |
A4 A2 |
UnicodeコードポイントのU+3042は、文字そのものに割り当てられた番号です。UTF-8のE3 81 82は、その番号をUTF-8で保存したときのバイト列です。
このように、コードポイントと実際に保存されるバイト列は分けて考える必要があります。
文字化けの原因と解決策
文字化けは、ファイルを作成したときの符号化方式と、ファイルを開くときの解釈が一致しない場合に発生します。
文字化けが起こる仕組み
たとえば、UTF-8で保存された「あ」はE3 81 82の3バイトです。これをShift_JISとして読み取ると、アプリケーションは別の文字の組み合わせとして解釈しようとするため、正しく表示できません。
この段階では、表示だけが崩れており、元のバイト列が残っていることがあります。その場合は、正しい符号化方式を指定して開き直すことで正常に表示できます。
ただし、次のような場合は元の情報が失われている可能性があります。
- データの一部が欠損している
- 変換できない文字が別の文字へ置き換えられ、その状態で保存されている
- 異なる文字コードのデータが1つのファイル内に混在している
?は通常の疑問符、�はUnicodeのREPLACEMENT CHARACTER(U+FFFD)です。U+FFFDは、不正なバイト列や変換できないデータを処理したとき、代替文字として使われることがあります。
ただし、?や�が表示されただけでは、文字化けやデータ損失の証拠にはなりません。画面に表示されているだけであれば、元ファイルのバイト列が残っている可能性があります。一方、別の文字へ置換された内容で元ファイルを上書きすると、置換前の情報を復元できなくなることがあります。
白い四角形の「□」が表示される場合も、文字データが失われたとは限りません。使用しているフォントに対応するグリフがない場合にも四角形で表示されます。
文字化けが起きたときの基本手順
文字化けが起きたときは、次の順番で確認します。
- 元ファイルを上書きせず、コピーを作る
- ファイルを作成したOSやアプリケーションを確認する
-
fileやnkfなどで文字コードを推測する - テキストエディタで文字コードを指定して開き直す
- 正しく表示できたことを確認し、必要な場合だけ別ファイルへ変換する
文字コードの自動判定は推測です。判定結果だけで決めず、作成元の環境や実際の表示もあわせて確認します。
文字コードはいつ決まるか
新規ファイルは、アプリケーションが保存時に文字をバイト列へ変換した時点で、ファイルとしての符号化方式が決まります。未保存の文章を内部でどのように保持するかは、アプリケーションによって異なります。
既存ファイルには、すでに何らかのバイト列が保存されています。ファイルを開くときは、そのバイト列をどの符号化方式で解釈するかが重要です。また、別の符号化方式を指定して保存し直すと、ファイルのバイト列も変わります。
文字コードの不一致を特定する方法
文字コードを確認するときは、次のような情報を組み合わせて判断します。
- HTTPヘッダー、XML宣言、メールヘッダーなどのメタデータ
- ファイル先頭のBOM
- テキストエディタの自動判定
-
file、nkfなどのコマンド - ファイルを作成したOSやアプリケーション
文字コードの自動判定は、バイトパターンなどをもとにした推測です。短いファイルや、Shift_JISとEUC-JPなどのレガシーな文字コードでは誤判定することがあります。
自動判定だけで確定せず、ファイルの作成元や、実際に正しく表示できるかも確認します。
BOM(Byte Order Mark)の特徴
BOMは、ファイルの先頭に付加されることがある特殊なバイト列です。
UTF-16やUTF-32では、バイト順を判定するために使われます。一方、UTF-8にはバイト順の違いがないため、UTF-8のBOM(EF BB BF)は主にUTF-8であることを示す目印として使われます。
UTF-8でBOMを使用するかどうかは、利用するファイル形式やアプリケーションによって異なります。Unicode規格では、UTF-8のBOMを一律に推奨も禁止もしていません。使用するシステムの仕様に合わせて判断します。
「あいう」をUTF-8で保存した場合のファイルサイズは次のとおりです。
| 形式 | ファイルサイズ |
|---|---|
| BOMなしUTF-8 | 9バイト(3バイト×3文字) |
| BOM付きUTF-8 | 12バイト(BOMの3バイト+3バイト×3文字) |
文字化けしたファイルは保存しない
文字化けした状態のまま、元のファイルを上書き保存しないでください。まずファイルをコピーし、コピーしたファイルを使って確認や変換を行います。
文字化けした状態でファイルを上書き保存すると、誤って解釈された文字が別のバイト列として保存され、元に戻せなくなる可能性があります。
文字化けを確認した場合は、最初に元ファイルをコピーし、前述の基本手順に沿って確認します。
オンラインの文字化け解読ツールを利用する方法もありますが、機密情報、個人情報、顧客データ、ログ、ソースコードなどは外部サービスへ入力しないようにします。
ファイル名の文字化けの仕組みと対策
ファイルの中身ではなく、ファイル名だけが文字化けすることもあります。特に、圧縮・解凍ソフトや古いアプリケーションの間で、ファイル名の扱いが一致しない場合に発生します。
ZIP形式には、ファイル名がUTF-8であることを示すLanguage Encoding Flagや、Unicodeのファイル名を保持するための拡張フィールドがあります。圧縮側と解凍側のソフトウェアがこれらを正しく扱えば、異なるOS間でも日本語のファイル名を保持できます。
一方、古いソフトウェアや独自の実装では、ファイル名をローカルの文字コードとして扱うことがあります。この場合、圧縮側と解凍側の解釈が異なると文字化けします。
対策としては、次の方法があります。
- UTF-8のファイル名に対応した圧縮・解凍ソフトを使用する
- 圧縮側と解凍側で同じソフトウェアや設定を使用する
- 相手の環境が分からない場合は、ファイル名を半角英数字にする
- 元のZIPファイルを残したまま、別の解凍ソフトで試す
7-Zipなど、ZIPのUnicodeファイル名に対応したソフトウェアを利用する方法もあります。
主要なコマンドラインツールによる文字コードの確認と変換
文字コードの確認や変換には、file、nkf、iconv、xxdなどのコマンドを利用できます。
コマンドで変換するときも、元ファイルを直接上書きせず、別のファイルへ出力します。
ファイルタイプとエンコーディングの確認
Linuxでは、file -iでMIMEタイプと文字コードを確認できます。
file -i input.txt
macOSに標準搭載されているfileでは、バージョンによって-Iを使用します。
file -I input.txt
出力例は次のとおりです。
input.txt: text/plain; charset=utf-8
fileの文字コード判定も推測であるため、結果だけで確定しないようにします。
エンコーディングの推測
nkf --guessを使うと、ISO-2022-JP、Shift_JIS、EUC-JP、UTF-8、UTF-16、UTF-32など、nkfが対応している文字コードを推測できます。
nkf --guess input.txt
文字コードの変換
入力がWindows-31Jと分かっている場合は、入力と出力を明示してUTF-8へ変換できます。
nkf --ic=Windows-31J --oc=UTF-8 input.txt > output.txt
nkf -w input.txt > output.txtのように入力文字コードを省略した場合は、nkfが入力を推測します。nkfがあらゆる文字コードを判定できるわけではないため、作成元が分かっている場合は明示した方が確実です。
iconvでも入力と出力の文字コードを指定して変換できます。
iconv -f CP932 -t UTF-8 input.txt > output.txt
CP932などの文字コード名は、OSやiconvの実装によって利用できる別名が異なります。使用可能な名前はiconv -lで確認します。
iconv -l
作成元がWindowsの場合はCP932である可能性がありますが、すべてのShift_JISファイルがCP932とは限りません。作成元と使用文字を確認して指定します。
改行コードを含めた変換と確認
文字コードを変換したあと、必要に応じてdos2unixでCRLFをLFへ変換します。
iconv -f CP932 -t UTF-8 input.txt > output.txt
dos2unix output.txt
xxdを使用すると、変換前後のバイト列を16進数で確認できます。
xxd input.txt
xxd output.txt
nkfの導入
macOSではHomebrewから導入できます。
brew install nkf
Linuxでは、ディストリビューションのリポジトリにnkfが含まれているかを確認します。Windowsでは、WSLを使用する方法や、nkfの公式リポジトリから入手する方法があります。
パッケージ管理の仕組みについては、次の記事にまとめています。
MIMEタイプについて
file -iなどの出力に含まれるtext/plain; charset=utf-8は、MIMEタイプと文字コードの情報です。
MIME(Multipurpose Internet Mail Extensions)は、もともと電子メールでさまざまな形式のデータを扱うために作られました。現在はWebでも利用され、データの種類や文字コードを伝えるために使われています。
Linux環境におけるロケール設定と文字コード
LinuxやUNIX系の環境では、LANGやLC_CTYPEなどのロケール設定が、文字の分類、照合順序、日時、数値、メッセージ言語などに影響します。
主な環境変数は次のとおりです。
| 環境変数 | 意味 |
|---|---|
LANG |
個別のLC_*が設定されていない場合に使われる既定値 |
LC_CTYPE |
文字の分類やマルチバイト文字の扱い |
LC_COLLATE |
文字列の並び順や照合方法 |
LC_TIME |
日付や時刻の書式 |
LC_NUMERIC |
数値の表記方法 |
LC_MONETARY |
通貨の表記方法 |
LC_MESSAGES |
システムメッセージの言語 |
LC_ALL |
すべてのLC_*とLANGを一時的に上書きする変数 |
ロケールの優先順位は、LC_ALL、対象カテゴリのLC_*、LANGの順です。文字の扱いに直接関係するカテゴリは、主にLC_CTYPEです。
ロケール設定の確認・変更
現在の設定と、利用可能なロケールを確認します。
locale
locale -a
一時的にUTF-8のロケールを指定する例は次のとおりです。実際に利用できるロケール名はlocale -aで確認します。
export LANG=ja_JP.UTF-8
LC_ALLはすべてのカテゴリを上書きするため、通常は永続設定せず、トラブルシューティングなどで一時的に使用します。
systemdを使用するLinuxでシステム全体のロケールを変更する場合は、localectlを利用できます。
sudo localectl set-locale LANG=ja_JP.UTF-8
macOSやsystemdを使用しないLinuxでは設定方法が異なるため、利用しているOSの手順を確認してください。
端末の文字コード設定と注意点
文字化けやillegal byte sequenceなどの問題には、次の要素が関係します。
- ファイルに保存されているバイト列
- コマンドが使用するロケール
- 端末の表示設定とフォント
- 使用しているコマンドの実装
ファイルがWindows-31Jで、処理環境がUTF-8の場合は、UTF-8へ変換してから処理する方法があります。
iconv -f CP932 -t UTF-8 input.txt | grep '検索語'
LANG=ja_JP.sjisのように一時的にロケールを切り替える方法もありますが、そのロケールがインストールされている環境でしか使用できません。現在のLinux環境では用意されていないことも多いため、locale -aで確認します。
端末制御の補足
バイナリファイルを誤ってcatした場合など、端末の表示や制御が乱れたときは、stty saneで端末モードを初期状態に戻せることがあります。
stty sane
文字コードと改行コードの関係
改行コードは文字の符号化方式とは別に、行の終わりを示す制御文字です。
改行コードごとの特徴
主な改行コードは次のとおりです。
| 改行コード | 表記 | 16進数 | 主な環境 |
|---|---|---|---|
| CR | \r |
0D |
旧Mac OS |
| LF | \n |
0A |
UNIX、Linux、現在のmacOS |
| CRLF | \r\n |
0D 0A |
Windows、DOS |
現在はLFとCRLFが中心です。テキストエディタでは、改行コードを画面上の記号で表示することがありますが、表示される記号はエディタによって異なります。
改行コードが原因で起こる問題と対策
改行コードを正しく扱えないツールでは、次のような問題が発生することがあります。
- 複数行のテキストが1行として表示される
- 余分な空行が入る
- シェルスクリプトの実行時にエラーが発生する
- 差分に不要な変更が大量に表示される
多くのテキストエディタはLFとCRLFを自動判定できます。ただし、古いツール、シェルスクリプト、複数OSで共同編集するリポジトリでは注意が必要です。
対策としては、次の方法があります。
- エディタで改行コードを確認して保存する
-
dos2unixやunix2dosで変換する - Gitの
.gitattributesやcore.autocrlfで扱いを統一する
Microsoft WordやApple Pagesに表示される段落記号や行区切り記号は、文書内部の構造を示す編集記号です。プレーンテキストのLFやCRLFとは別の概念であり、テキスト形式へ書き出すときに改行コードへ変換されます。
まとめ
改めてこの記事の内容をまとめます。
- 文字集合は使用する文字と番号を定義し、符号化方式は番号をバイト列へ変換する
- UnicodeにはUTF-8、UTF-16、UTF-32などの符号化方式がある
- Shift_JISとWindows-31Jは完全に同じではない
- 文字化けは、保存されたバイト列と読み取り時の符号化方式が一致しない場合に発生する
- 文字化けした元ファイルは上書きせず、コピーを使って確認する
- 文字コードの自動判定は推測であり、作成元や実際の表示も確認する
- 文字コードの確認には
fileやnkf、変換にはnkfやiconvを利用できる - ZIPのファイル名は、圧縮・解凍ソフトがUnicode情報を正しく扱うかによって文字化けすることがある
- ロケールの優先順位は、
LC_ALL、対象カテゴリのLC_*、LANGの順となる - 改行コードは文字コードとは別の仕組みであり、主にLFとCRLFが使われる
最低限の仕組みを理解しておくことで、文字化けが発生したときに原因を切り分けやすくなります。