Windowsの文字コードと改行コードの知識マップ(知識グラフ)
Windowsのテキストはバイト列・文字コード・改行コード・BOMという別々の軸の組み合わせで成り立ち、UTF-8やUTF-16LEはUnicodeの符号点をバイト列にする文字コードである一方、CP932は日本語Windows独自のレガシーコードページで両者は前提を取り違えると文字化けを起こす。BOMはUTF-16のバイト順を示す目印であると同時に、UTF-8では「UTF-8である」というシグネチャとして使われ、Windows PowerShell 5.1は非ASCII文字を含むスクリプトを正しく読むためにBOMを必要とする。改行コードはCRLFとLFのどちらかで文字コードとは別問題であり、リポジトリ単位の扱いは.gitattributesで固定できるが、Gitは改行こそ変換しても文字コードは自動で正してくれない。文字化けは正しい文字コードで開き直せば戻ることが多いが、誤読した内容をそのまま保存すると元のバイト列が失われ、表示崩れではなく取り返しのつかないデータ破損になる。
文字コード(キャラクターエンコーディング)
文字列をバイト列として保存・伝送するための対応規則。Unicodeのような文字集合をどうバイト列にするかを定めるUTF-8やUTF-16、Windows独自のCP932のようなレガシーコードページなどが含まれる。
文字符号化方式
改行コード
行の区切りに使うバイト列。WindowsではCRLF(0D 0A)が伝統的で、Linux/macOSではLF(0A)が一般的。文字コードとは別の軸で決まり、同じ文字コードのファイルでも改行だけがCRLFとLFの間でずれることがある。
Unicode
文字を番号(符号点)で表すための文字集合の枠組み。UTF-8やUTF-16はこれをバイト列にする文字コードであり、Unicode自体とは別の概念である。
UTF-8
Unicodeの可変長エンコーディング方式。バイト列に強い制約があるため、CP932など他のエンコーディングのバイト列は厳密デコードで失敗しやすい。
UTF-16LE
Unicodeの符号点を2バイト単位のリトルエンディアンで表す文字コード。Windows APIやPowerShellの一部コマンドレットの既定出力に使われる。
UTF-16 リトルエンディアン
エディタ上のUnicode表記
CP932
Windowsの日本語コードページ(コードページ番号932)。実務上はWindows版のShift_JISに近いが、他システムのShift_JIS実装と1バイトも違わない保証はない。
Windowsの日本語コードページ
アクティブコードページ(ANSI/ACP)
Windowsの「ANSI」という表記が多くの場合指す、そのマシンに設定されている既定のコードページ。日本語環境では通常CP932になる。
BOM(Byte Order Mark)
ファイル先頭に置かれ、どのUnicodeエンコーディングかを示す数バイトの目印。UTF-8はEF BB BF、UTF-16 LEはFF FE、UTF-16 BEはFE FF。
バイトオーダーマーク
文字化け
実際のバイト列の解釈が意図したencodingとずれ、表示や内容が壊れて見える状態。
PowerShellの文字コード既定値
PowerShellのコマンドレットが読み書きに使う既定の文字コード。PowerShell 6以降はutf8NoBOMに統一されている一方、Windows PowerShell 5.1はコマンドレットごとに既定がそろっていない。
.gitattributes
リポジトリ単位でテキストファイルの改行変換(CRLF/LF)などの取り扱いを固定するGitの設定ファイル。
誤読内容の再保存によるデータ破損
文字化けして見える内容を正しい文字コードで開き直さずそのまま保存し、元のバイト列を失うこと。表示崩れとは異なり、あとから正しい文字コードを知っても元に戻らない。
UTF-8やUTF-16のような現代の文字コードでは、Unicodeは文字を番号で表す枠組みであり、これらの文字コードがこれをバイト列に変換する(CP932のようなUnicode以前のレガシーコードページはUnicodeを介さない)
現代のWindows実務では新規テキストの第一候補としてUTF-8を使う
Windows APIやPowerShellの一部の出力は既定でUTF-16LEを使う
日本語Windowsのレガシーテキストは実務ではCP932として意識したほうが安全である
Windowsの「ANSI」という表記は多くの場合そのマシンのアクティブコードページを指す
UTF-8やUTF-16のようなUnicode系の文字コードでは、仕様はBOMの有無まで決めて初めて運用ルールとして成立する(CP932のようなレガシーコードページにはBOMの概念自体がない)
書き込み時と読み込み時で前提とする文字コードが一致しないと文字化けが起きる
UTF-8はUnicodeの符号点を1〜4バイトの可変長バイト列にエンコードする文字コードである
UTF-16LEはUnicodeの符号点を2バイト単位のリトルエンディアンでエンコードする文字コードである
非ASCII文字を含む場合、同じバイト列をUTF-8として書きCP932として読む、またはその逆をすると、正しい文字列に戻らない(ASCII範囲のみのバイト列はどちらの前提で読んでも一致するため問題は表面化しない)
UTF-8で書かれたバイト列をCP932の前提で読むと、別の文字列に見える文字化けが起きる
非ASCII文字を含むUTF-8のファイルをアクティブコードページの前提で読むと、文字化けが起きる
UTF-16LEのログをアクティブコードページ前提のツールへ渡すと、NULバイトが混ざるなど文字化けの一種として見える
BOMはUnicodeが定めるU+FEFFという符号点をファイル先頭に置く目印である
PowerShell 6以降では、すべてのテキスト出力の既定がutf8NoBOM(UTF-8 BOMなし)である(Windows PowerShell 5.1はコマンドレットごとに既定がそろっていない)
Windows PowerShell 5.1では、BOMのないファイルをGet-Contentやスクリプト読み込みでアクティブコードページとみなす(PowerShell 6以降はutf8NoBOMに統一されており該当しない)
Windows PowerShell 5.1で、非ASCII文字を含むスクリプトファイルを実行する場合に限り、BOMがないとアクティブコードページとして読まれてスクリプトが壊れる(PowerShell 6以降はutf8NoBOMに統一されているため該当しない)
Gitリポジトリで管理する場合、リポジトリ単位の改行コードの扱いは.gitattributesで固定できる(Git管理下にないテキストファイルの改行コードには関与しない)
文字化けして見える内容に気づかずそのまま保存すると、元のバイト列が失われ表示崩れではなくデータ破損になる