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

2026/09/30 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 コマンドにも追加し、1.2.0 で追加した Out-ProbedFile / Convert-ProbedContent にも用意しています。

1.2.0 で、判定処理を改善しました。 1.1.0 では、日本語環境でドイツ語などのファイルを読むと誤判定するため、-Strategy UtfUnknownOnly の指定をお勧めしていました。1.2.0 では、既定のままで正しく判定します。この記事も、1.2.0 に合わせて書き直しています。

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

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

SnowStack.EncodingProbe 1.2.0 解説 — ファイル出力・変換コマンドと世界の言語への対応

SnowStack.EncodingProbe.PowerShell 解説

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

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

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

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

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

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

特に、東アジア漢字文化圏の 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 として解釈され、正しく「한국어」と読めます。

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

この結果は、1.2.0 でも変わりません。

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

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

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

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

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

# 韓国語を読んで、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 は、それぞれ後者が前者を包含する関係にあるので、後者を選んでおけば実害が出ません。

繁体字と簡体字の取り違え(1.2.0 で改善)

1.1.0 では、台湾のカルチャーで簡体字(GBK)のファイルを読むと Big5 と、大陸のカルチャーで繁体字(Big5)のファイルを読むと GB18030 と、誤って判定していました。

Big5 と GBK も、バイト構造が似ていて、互いのバイト列として成立してしまうからです。

1.2.0 では、サードパーティ製の判定ライブラリ UTF.Unknown(後述)の結果と突き合わせるようにしました。UTF.Unknown が反対側の系統を高い信頼度で判定した場合は、その系統で判定をやり直します。

ファイル カルチャー 1.1.0 1.2.0
簡体字 GBK zh-TW 950 / big5 936 / gbk
繁体字 Big5 zh-CN 54936 / gb18030 950 / big5

ただし、この突き合わせが働くのは、ある程度の長さのある文書です。短い文では、UTF.Unknown が系統を判定できないことがあります。

香港のカルチャー(1.2.0 で対応)

1.2.0 から、香港・マカオのカルチャー(zh-HK / zh-MO)と広東語(yue)を、台湾とは分けて扱うようになりました。

とはいえ、香港の Big5(HKSCS)は台湾の Big5 の上位互換で、バイト列からは区別できません。判定結果は、台湾と同じ 950 / big5 です。

HKSCS 固有の文字は、Unicode の私用領域の文字として読み込まれます。詳しくは、1.2.0 の記事で解説しています。

SnowStack.EncodingProbe 1.2.0 解説 — ファイル出力・変換コマンドと世界の言語への対応

また、1.2.0 からは zh-Hant-HK や zh-Hans-CN のように、用字(Hant = 繁体字、Hans = 簡体字)を含むカルチャー名も、正しく解釈します。1.1.0 では、この形のカルチャー名を渡すと判定不能になっていました。

-Culture が効く場所

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

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

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

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

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

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 で確認しておくことをお勧めします。

この結果は、1.2.0 でも同じです。Set-ProbedContent の書き込みは、.NET の既定の動作に従い、符号化できない文字を ? に置き換えます。

なお、1.2.0 で追加した Convert-ProbedContent は、このように文字が失われる変換を行いません。変換先で表現できない文字があれば、そのファイルは変換せずにエラーを報告します。

(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 は、この分業を利用者が上書きするためのオプションです。

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

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

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

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

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

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

1.1.0 の Combined で救われなかった理由

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

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

ドイツ語のテキストに対して、独自判定は Shift-JIS という答えを出してしまいました。üß は windows-1252 で FC DF の 2 バイトになり、これが Shift_JIS の外字領域の 2 バイト文字として、構造上は成立してしまうからです。

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

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

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

1.2.0 では、既定のままで正しく読めます

1.2.0 では、Combined の設計を変えました。

独自判定が東アジアの旧マルチバイト文字コードを答えた場合も、UTF.Unknown にも判定させて、結果を突き合わせます。UTF.Unknown が十分な確かさ(信頼度 0.55 超)で「シングルバイトの文字エンコーディングだ」と判定した場合は、そちらを採用します。

本物の日本語のテキストに対して、UTF.Unknown がシングルバイトを高い信頼度で返すことはないので、日本語の判定結果は変わりません。

同じドイツ語のファイルを、1.2.0 で判定した結果です。

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

既定の Combined のままで、正しく読めるようになりました。両ホストで同じ結果になります。

NativeOnly は独自判定だけを使う指定なので、突き合わせを行いません。1.2.0 でも文字化けします。

なお、東アジア以外のカルチャー(ドイツ語やフランス語の環境など)では、東アジアの旧マルチバイト文字コードの判定そのものを行いません(1.1.0 でも同じです)。この問題が起きるのは、東アジアのカルチャーの環境だけです。

1.2.0 では、欧米のシングルバイトのファイルを読むために -Strategy を指定する必要は、基本的にありません。

ただし、おおむね 90 バイト以下の短いファイルは例外です。短い文に対する UTF.Unknown の精度の限界で、東アジアのカルチャーでは誤判定する場合があります。この場合は -Strategy UtfUnknownOnly でも正しく判定できるとは限らないので、-Encoding で文字エンコーディングを明示してください。

1.1.0 をお使いの場合は、引き続き -Strategy UtfUnknownOnly を指定してください。

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

-Strategy に指定できる値

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

正式名 別名 数値 動作内容
Combined default 0 最初に独自実装で判定し、不明の場合は UTF.Unknown で判定する。1.2.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() を使うと、Combined や NativeOnly という名前を解析できなくなります。

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

-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-Path は False を返します。

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

なお、不正なカルチャー名を渡したときのエラーメッセージには、.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 での明示指定を案内するようにしました。

1.2.0 の判定の改善で、新たに分かった限界もあります。

現象 状態
おおむね 90 バイト以下の短いシングルバイト系のテキストを、東アジアのカルチャーで誤判定する UTF.Unknown の精度の限界のため未対応
文字の一覧や記号・数字の多いシングルバイト系のテキストは、数百バイトあっても判定できない(CodePage が -1)ことがある 統計による推定の限界のため未対応。カルチャーからの推定を 1.3.0 以降で検討中
ウクライナ語(windows-1251 / KOI8-U)が判定できない UTF.Unknown の信頼度が低いため未対応
ルーマニア語(ISO-8859-16)が判定できない .NET がこの文字エンコーディングを提供していないため。判定失敗のエラーとして報告する
大陸のカルチャーで、HKSCS 入りの Big5 を GB18030 と判定する 未対応。-Culture zh-HK を指定してください

ルーマニア語については、1.1.0 では NullReferenceException が発生していました。1.2.0 で、判定失敗の非終了エラーとして報告するように直しています。

2 件目は、判定の成否が長さよりも内容で決まる、という限界です。約 200 バイトのイタリア語の windows-1252 のファイルでも、アクセント付き文字の一覧の行と、記号・数字の行が入っていたために、it-IT のカルチャーでも判定できませんでした。その 2 行を除いた約 110 バイトのファイルは、正しく判定できています。-Culture を指定しても、この限界は変わりません。詳しくは以下の記事で解説しています。

SnowStack.EncodingProbe 1.2.0 解説 — シングルバイト系の判定は、長さよりも内容で成否が決まります

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

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

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

状況 指定するもの
自分の国のテキストファイルだけを扱う どちらも不要
日本語環境で、韓国語・中国語・台湾・香港のファイルを読む -Culture ko-KR / -Culture zh-CN / -Culture zh-TW / -Culture zh-HK
日本語環境で、欧米のシングルバイトのファイルを読む 1.2.0 ではどちらも不要。1.1.0 では -Strategy UtfUnknownOnly
短い(90 バイト程度以下の)欧米のファイルを読む どちらも不要。-Encoding で明示する
欧米のファイルが判定できない(CodePage が -1) どちらも不要。-Encoding 1252 などで明示する
欧米の環境で、日本語のファイルを読む -Culture ja-JP
文字エンコーディングが分かっている どちらも不要。-Encoding で明示する

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

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

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

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

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

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

関連資料