-Culture と -Strategy の解説 — 外国語のテキストファイルを読む

2026/09/01 document update

SnowStack.EncodingProbe.PowerShell の -Culture-Strategy という、二つのオプションの解説です。

この二つは、ハマった人にしか刺さらない、しかし刺さる人には決定的なオプションだと思います。

日本語の Windows で日本語のテキストファイルだけを扱っているなら、どちらも使う必要はありません。

しかし、韓国語や中国語のテキストファイルを、日本語環境で開いたことがある方。

あるいは、ドイツ語やフランス語のテキストファイルが文字化けして困った方。

そういう方には、意味があると思います。

-Culture-Strategy は、1.0.x から Resolve-Encoding に用意されていました。バージョン 1.1.0 で、Get-ProbedContent / Set-ProbedContent / Add-ProbedContent の 3 コマンドにも追加しています。

コマンドそのものの解説は、以下の記事で行っています。

SnowStack.EncodingProbe.PowerShell 1.1.0 新コマンド解説

SnowStack.EncodingProbe.PowerShell 解説

(この記事に掲載した実行結果は、すべて実機で採取したものです。採取日は 2026年8月31日、環境は Windows 11 の日本語環境です。PowerShell 7.6.5 と Windows PowerShell 5.1.26100 の両方で採取しており、特に断らない限り両ホストで同じ結果になります)

文字エンコーディングの判定は、バイト列だけでは決まらない

まず前提から説明します。

文字エンコーディングの推測とは、テキストファイルをバイナリモードで開き、そのバイト列のパターンを解析して、該当する文字エンコーディングを推測する処理です。

アルゴリズムだけで、文字エンコーディングを完全に判定することは不可能です。

理由は単純で、同じバイト列が複数の文字エンコーディングとして成立してしまうからです。

特に、東アジア漢字文化圏の Shift_JIS・EUC-JP・CP949・Big5・GBK といった旧マルチバイト文字コードは、構造が互いに似通っています。

バイナリ解析だけで見分けるのは、非常に困難です。

ここで言う「旧マルチバイト文字コード」とは、Unicode が普及する前に各国が独自に定めた、1 文字を 2 バイト前後で表す文字エンコーディングのことです。日本の Shift_JIS や EUC-JP がこれにあたります。

しかし、実務上はそこまで困りません。

日本を含む東アジア諸国は、既に Unicode を中心とするテキスト環境へ移行しています。

旧マルチバイト文字コードを使うのは、古いデータや古い環境を扱う場合に限られます。

そして、旧マルチバイト文字コードを隣国のものと取り違える場面は、ほとんど起きません。

日本語 OS 上で台湾の Big5 を扱うことは、まずありません。韓国語 OS 上で日本の Shift_JIS を扱うこともほぼありません。

よって、その国で使われている旧マルチバイト文字コードだけを判定できれば、実用上は足ります。

そこで、OS のカルチャー(国情報)を取得し、その国に合わせた判定処理を走らせることで、判定の精度を高めています。

-Culture は、この「どの国の判定処理を使うか」を、利用者が上書きするためのオプションです。

-Culture — 同じバイト列が二通りに読める

韓国語のファイルを、日本語環境で読む

具体例を一つ挙げます。

C7 D1 B1 B9 BE EE という 6 バイトの並びがあります。

これは韓国語の CP949 で「한국어」(韓国語)を表すバイト列です。

ところが、このバイト列は EUC-JP としても成立してしまいます。

だから、バイト列だけを見ても、どちらなのか決められません。

実際に、日本語環境の PowerShell で読んでみます。

# カルチャーを指定せずに読む(日本語環境なので ja-JP になる)
Get-ProbedContent .\korean.txt

# カルチャーを韓国に指定して読む
Get-ProbedContent .\korean.txt -Culture ko-KR

結果は以下のとおりです。

指定 判定結果 読めた文字
-Culture なし(日本語環境) 20932 / euc-jp U+5EC3 U+53A9 U+5B22(廃厩嬢)← 文字化け
-Culture ko-KR 949 / cp949 U+D55C U+AD6D U+C5B4(한국어)← 正しい

カルチャーを指定しない場合、日本語環境なので EUC-JP として解釈され、「廃厩嬢」という意味不明な漢字が出てきます。

-Culture ko-KR を指定すると、CP949 として解釈され、正しく「한국어」と読めます。

同じファイル、同じコマンド、違うカルチャー。それだけで結果が変わります。

これは判定の不具合ではありません。バイト列としては、どちらも正しいのです。

判定処理には決めようがないので、利用者が教えてやる必要があります。

なお、日本語環境のコンソールでは韓国語や中国語が ???? のように表示されることがあります。

これはコンソールのコードページにその文字が無いための表示上の問題で、読み込み自体は成功しています。

文字列として確認したい場合は、ファイルへ書き出してエディターで開いてください。

# 韓国語を読んで、UTF-8 (BOM 付き) のファイルへ書き出す
Get-ProbedContent .\korean.txt -Culture ko-KR |
    Set-ProbedContent .\ko_ok.txt -Encoding utf8BOM

曖昧な組み合わせと、その決め方

同じ種類の曖昧さは、他にもあります。

判定処理が、どう決めているかを表にまとめました。

曖昧な組み合わせ 決め方
EUC-JP と Shift-JIS 改行が CRLF なら Shift-JIS、LF なら EUC-JP、改行が無ければ OS の既定
EUC-KR と CP949 CP949
EUC-TW と CP950 CP950(Big5)

一番上の EUC-JP と Shift-JIS の見分け方は、少し乱暴に見えるかも知れません。

しかし、EUC-JP は主に UNIX 系の環境で使われてきました。

一方、Shift_JIS は主に Windows で使われてきた文字エンコーディングです。

改行コードが CRLF なら Windows で作られた可能性が高く、LF なら UNIX 系で作られた可能性が高い、という判断になります。

根拠としては弱いのですが、他に手がかりがありません。 実用上は、これで大半が当たります。

下の 2 行は、より単純です。EUC-KR と CP949、EUC-TW と CP950 は、それぞれ後者が前者を包含する関係にあるので、後者を選んでおけば実害が出ません。

-Culture が効く場所

-Culture は、Resolve-Encoding同じ名前・同じ値を取ります。

判定方式の解決処理は 1 か所に集約してあるので、コマンドによって受け付ける名前が違う、ということは起きません。

そして、判定処理が走る場所すべてに効きます。

コマンド 効く場面
Resolve-Encoding 判定するファイル
Get-ProbedContent -Encoding 省略時に読み込むファイル
Set-ProbedContent / Add-ProbedContent -EncodingFrom の参照ファイル
Set-ProbedContent / Add-ProbedContent -Encoding 省略時(Auto)の、書き込み先・追記先の既存ファイル

つまり、読むときだけでなく、書くときにも効きます。

ConvertTo-DotNetEncoding にだけは -Culture がありません。ファイルを引数に取らず、判定処理そのものを呼び出さないためです。

書き込みでも指定を間違えると壊れます

書き込み側で -Culture を間違えると、どうなるか。

実際に試すと分かりやすいので、結果を載せます。

# 韓国語のファイルから継承して、韓国語の文字列を書き込む
Set-ProbedContent .\from_ko.txt -Value '안녕하세요' `
    -EncodingFrom .\korean.txt -Culture ko-KR -LineBreak Lf
Show-Bytes .\from_ko.txt
-Culture の指定 できたバイト列
-Culture ko-KR BE C8 B3 E7 C7 CF BC BC BF E4 0A(CP949)
-Culture ja-JP 3F 3F 3F 3F 3F 0A?????

ja-JP を指定すると、参照ファイルが EUC-JP と判定されます。

そして、韓国語の文字は EUC-JP では符号化できません。すべて 3F、つまり半角の ? に置き換わります。

元の文字は、この時点で完全に失われます。 ? から韓国語を復元する方法はありません。

-EncodingFrom を使う場面では、参照ファイルの判定が正しいかどうかを、先に Resolve-Encoding で確認しておくことをお勧めします。

Show-Bytes は、ファイルの中身を 16 進数で表示する自作のヘルパー関数です。定義は 新コマンド解説の記事 に載せています)

-Strategy — 判定処理の分業

独自判定と UTF.Unknown の役割分担

文字エンコーディングの判定処理は、二つの実装を使い分けています。

実装 得意な範囲
独自実装の判定処理 ASCII・JIS コード・Unicode・東アジア漢字文化圏の旧マルチバイト文字コード
UTF.Unknown(サードパーティ製) 欧米などの旧シングルバイト文字コード

UTF.Unknown は Mozilla Universal Charset Detector の系譜にあるライブラリで、世界の多数の言語に対応しています。

欧米のシングルバイト文字エンコーディングの判定は、非常に信頼できます。

しかし、日本語をはじめとする東アジアの旧マルチバイト文字エンコーディングの判定では、精度が落ちます。日本語では、Shift_JIS の半角カナ文字の判定で間違う確率が高くなります。

また、UTF.Unknown は BOM の無い UTF-16 と UTF-32 に対応していません。

UTF-16 と UTF-32 は「先頭に BOM を付けることを推奨」しているだけで、必須ではありません。規格上、BOM の無い UTF-16 と UTF-32 は有り得ます。

そこで、この弱点を埋めるために、東アジアと Unicode の判定は独自実装で行っています。

互いの弱点を補い合う分業になっています。

-Strategy は、この分業を利用者が上書きするためのオプションです。

欧米のテキストを日本語環境で読むと誤判定します

ここが、この記事で一番書きたかったところです。

windows-1252 のドイツ語テキストを用意しました。中身は Größe Straße für Maßnahmen. のような、ウムラウトとエスツェットを含む文章です。

これを日本語カルチャーの環境で、3 通りの判定方式にかけました。

# 判定方式を切り替えて、同じファイルを読む
Get-ProbedContent .\german.txt -Strategy Combined        # 既定
Get-ProbedContent .\german.txt -Strategy NativeOnly
Get-ProbedContent .\german.txt -Strategy UtfUnknownOnly

結果は以下のとおりです。

指定 判定結果 復号結果
-Strategy Combined(既定) 932 / shift_jis 文字化け
-Strategy NativeOnly 932 / shift_jis 文字化け
-Strategy UtfUnknownOnly 28591 / iso-8859-1 正しく復号

UtfUnknownOnly を指定したときだけ、「Größe」「Straße」が正しく読めます。

両ホストで同じ結果になります。

既定の Combined でも救われない理由

上の表で驚くのは、既定の Combined でも救われないという点だと思います。

Combined は「独自判定を先に走らせ、独自判定が答えを出せなかったときだけ UTF.Unknown へ委ねる」という設計です。

ドイツ語のテキストに対して、独自判定は Shift-JIS という答えを出してしまいました。

答えを出してしまった以上、UTF.Unknown の出番は来ません。

つまり、独自判定が「自信を持って誤答した」場合、Combined では救えないのです。

だから -Strategy という逃げ道が必要になります。

この結果は、分業の設計そのものを説明していると思います。

独自判定は東アジア漢字文化圏のマルチバイトを担当し、UTF.Unknown は欧米のシングルバイトを担当します。

担当外の入力を与えれば、間違えます。

欧米のシングルバイト文字エンコーディングを日本語カルチャーの環境で読むときは、-Strategy UtfUnknownOnly を指定してください。 これが、この記事の実用的な結論になります。

逆に、欧米のカルチャーの環境で日本語の Shift_JIS ファイルを読む場合は、-Culture ja-JP を指定することになります。こちらは -Strategy ではなく -Culture の出番です。

-Strategy に指定できる値

-Strategy の値には、以下が使えます。大文字と小文字は区別しません。

正式名 別名 数値 動作内容
Combined default 0 最初に独自実装で判定し、不明の場合は UTF.Unknown で判定する。省略時はこれになる
NativeOnly native 1 独自実装だけで判定する。UTF.Unknown は使用しない
UtfUnknownOnly utfunknown 3 UTF.Unknown だけで判定する。独自実装は使用しない

Combined / NativeOnly / UtfUnknownOnly が正式名で、NuGet パッケージ側の DetectionStrategy 列挙型のメンバ名と一致しています。

default / native / utfunknown と数値は、1.0.x のときから受け付けている書き方です。互換のために残してあります。

新しく書くスクリプトでは、正式名の使用をお勧めします。 意味が名前から読み取れるからです。

-Strategy-Culture と同様に、Resolve-Encoding と Probed 系の 3 コマンドで同じ値を取ります。

補足 — トルコ語環境での落とし穴

判定方式の名前を解析する処理で、一つ細かい対処を入れています。

文字列を小文字化するとき、ToLower() ではなく ToLowerInvariant() を使っています。

トルコ語環境では、'I' を小文字にすると 'ı'(点の無い i)になります。

そのため ToLower() を使うと、CombinedNativeOnly という名前を解析できなくなります。

このモジュールの存在意義は「実行環境によらず同じ結果を得られること」なので、こういう環境依存は潰しておく必要があります。

-Encoding を明示すれば、どちらも影響しません

一点、押さえておいていただきたいことがあります。

-Encoding を明示した場合、判定処理そのものが走りません。

したがって、-Culture-Strategy も影響しません。

# 判定を行わないので、-Culture も -Strategy も意味を持たない
Get-ProbedContent .\korean.txt -Encoding 949

文字エンコーディングが分かっているファイルなら、これが最も確実です。

-Culture-Strategy は、あくまで「文字エンコーディングが分からないファイルを、なるべく正しく推測する」ためのオプションです。

判定を当てにいくより、分かっているなら明示する方が堅実です。

不正な値は、ファイルを開く前に弾かれます

-Culture-Strategy に解釈できない値を渡すと、終了エラーになります。

しかも、ファイルを開く前に失敗します。

# 存在しない判定方式を指定して、書き込みを試みる
Set-ProbedContent .\never_created.txt -Value 'x' `
    -Encoding utf8NoBOM -Strategy Nonexistent -ErrorAction Stop

# ファイルが作られていないことを確認する
Test-Path .\never_created.txt

# 実行結果
False

エラー ID は InvalidStrategy で始まります。-Culture 'not a culture!' の場合は InvalidCulture です。

どちらの場合も、Test-PathFalse を返します。

書き込み系のコマンドで、これは重要です。処理の途中で失敗すると、書きかけの破損したファイルが残るからです。

なお、不正なカルチャー名を渡したときのエラーメッセージには、.NET の内部例外を含めていません。

CultureNotFoundException のメッセージは、.NET Framework と .NET Core で文言が違うからです。

5.1 : Culture is not supported. Parameter name: name ...
7.x : Culture is not supported. (Parameter 'name') ...

ランタイム固有の文言が利用者に見えてしまうと、「実行環境によらず同じ結果」という前提が崩れます。

そのため、モジュール自身のメッセージだけを表示するようにしました。

判定にはまだ限界があります

正直に書いておきます。

ISO-2022 系の判定には、まだ問題が残っています。

現象 状態
ISO-2022-TW が ISO-2022-CN と誤判定される 未対応
SO/SI 形式の 1 バイトカナを検出できない 未対応
判定できても .NET が扱えないコードページがある 1.1.0 で非終了エラー(CodePageNotAvailable)として報告する対応を実施

上の 2 件は判定エンジン側の課題で、1.2.0 以降で扱う予定です。

3 件目については、判定は成功しているのに、実行環境の .NET がそのコードページを提供していない、という状態です。ISO-2022-TW(50229)が該当します。

1.1.0 では、これを対象ファイルごとの非終了エラーとして報告し、-Encoding での明示指定を案内するようにしました。

該当する文字エンコーディングを確実に扱いたい場合は、-Encoding で明示的に指定してください。 判定を行わないので、これらの問題を回避できます。

二つのオプションの使い分け

最後に、どちらを使うべきかを整理します。

状況 指定するもの
自分の国のテキストファイルだけを扱う どちらも不要
日本語環境で、韓国語・中国語・台湾のファイルを読む -Culture ko-KR / -Culture zh-CN / -Culture zh-TW
日本語環境で、欧米のシングルバイトのファイルを読む -Strategy UtfUnknownOnly
欧米の環境で、日本語のファイルを読む -Culture ja-JP
文字エンコーディングが分かっている どちらも不要。-Encoding で明示する

整理すると、-Culture は「どの国の判定処理を使うか」、-Strategy は「どちらの判定実装を使うか」を選ぶオプションです。

前者は東アジア漢字文化圏の中での選択、後者は東アジアと欧米のどちらを想定するかの選択、と考えると分かりやすいと思います。

そして、ほとんどの利用者にとって、この二つは使う必要のないオプションです。

自分の国のテキストファイルを普通に扱っている限り、OS のカルチャーに自動で合わせるので、何も指定しなくて構いません。

以上、-Culture-Strategy の解説でした。

外国語のテキストファイルの文字化けで困った経験のある方に、この記事が役に立てば幸いです。

関連資料