0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Get-Contentに-Encoding UTF8を付けたら、4つ直って2つ壊れた

0
Last updated at Posted at 2026-08-25

同じフォルダにログが2つあった。片方は自分のスクリプトが書いたもの。もう片方は、そのスクリプトが起動したnodeが書いたもの。

Get-Contentで読むと、自分のは正しく読めて、nodeのは化けた。-Encoding UTF8を付けたら、nodeのが直って、自分のが化けた。

同じGet-Contentなのに、どちらか片方しか正しく読めない。そこで、同じ日本語12文字を14通りの書き方で書いて、2通りの読み方で読み返しました。

この「4つ直って2つ壊れた」はWindows PowerShell 5.1での話です。そのあと、同じWindows 11 / ACP=932のマシンでPowerShell 7.6.5でも同じテストを走らせました。ファイル書き込み側の問題はほぼ消えました。ただし、nodeのUTF-8出力をPowerShellで受ける経路には、別の問題が残りました。

測定環境(まず5.1)

項目 値
OS Windows 11 ja-JP (NT 10.0.26100.0)
PowerShell Windows PowerShell 5.1.26100.9168
CurrentCulture ja-JP
system ACP(レジストリ) 932
Console::OutputEncoding 932 shift_jis
$OutputEncoding 20127 us-ascii
測定日 2026-08-25

2026-08-26 訂正。OSを10.0.26200.0と書いていましたが、実測は10.0.26100.0でした。8-25の測定ではOSのビルド番号を出力していませんでした。**測っていない数字が入っていました。**測定器に[System.Environment]::OSVersionの行を足して、8-26に測り直した値に差し替えています。表の他の値は、8-26に同じ2台で測り直して28行すべて一致しました。

この記事は.ps1スクリプト自体の文字コードの話ではありません。スクリプトが書いた「ログやデータのファイル」を読み返すときの話です。

測り方

テスト文字列は日本語12文字。ファイル名が見つからないときに多くのツールが出す、あの一行です。

読み方は2つ。

呼び方 コマンド
bare Get-Content -Raw
-Enc UTF8 Get-Content -Raw -Encoding UTF8

書き方を14通り試して、それぞれ両方で読み返し、元の12文字に戻るかを見ました。

結果(Windows PowerShell 5.1)

# 書き方 先頭バイト bare -Enc UTF8
1 Out-File既定 FF FE D5 30 OK OK
2 Out-File -Encoding utf8 EF BB BF E3 OK OK
3 Out-File -Encoding ascii 3F 3F 3F 3F 化ける 化ける
4 Set-Content既定 83 74 83 40 OK 化ける
5 Set-Content -Encoding UTF8 EF BB BF E3 OK OK
6 Add-Content既定 83 74 83 40 OK 化ける
7 Tee-Object -FilePath FF FE D5 30 OK OK
8 リダイレクト FF FE D5 30 OK OK
9 IO.File WriteAllText (UTF8) E3 83 95 E3 化ける OK
10 IO.File WriteAllBytes (UTF8) E3 83 95 E3 化ける OK
11 pythonをcmd.exeでリダイレクト E3 83 95 E3 化ける OK
12 nodeをcmd.exeでリダイレクト E3 83 95 E3 化ける OK
13 nodeの出力をPSで受けてOut-File FF FE 5D 7E 化ける 化ける
14 nodeの出力をPSで受けて-Encoding utf8 EF BB BF E7 化ける 化ける

1. どちらの読み方も、単独では全部を読めない

bareで読むと9から12が化けます。これは.NET / python / nodeが書いたファイルです。

一方、-Encoding UTF8を付けて読むと4と6が化けます。これはPowerShell自身が既定で書いたファイルです。

つまり5.1で-Encoding UTF8を付けると、**UTF-8 BOM無しで書かれた9・10・11・12の4ケースが直り、CP932で書かれた4・6の2ケースが壊れます。**これがタイトルの「4つ直って2つ壊れた」です。

「-Encoding UTF8を付ければ直る」は成り立ちません。付けると、別のものが壊れます。

2. 先頭バイトを見れば、どのエンコーディングで書かれたか分かる

先頭 エンコーディング この表で該当する行
FF FE UTF-16 LE 1・7・8・13
EF BB BF UTF-8 BOM付き 2・5・14
83 74 CP932 4・6
E3 83 UTF-8 BOM無し 9・10・11・12
3F 3F ?に潰れている 3

分かるのは「どのエンコーディングで書かれたか」までです。**誰が書いたかは、バイト列だけでは分かりません。**上の表で書き手と対応づけられているのは、14行とも自分で書き分けたからです。

それでも、今回の14ケースでは、読む前に先頭を見れば、どちらの読み方を使えばいいかは決まりました。

今回の14ケースでは、すべて日本語を含んでいるため、この判別ができました。中身がASCIIだけのファイルは、CP932とUTF-8でバイト列が同じなので、先頭を見ても区別できません。区別する必要も無いのですが。

3. 3番だけは復旧できない

3番の-Encoding asciiで書いたファイルは、日本語が全部 ?(0x3F)になっています。

他の行は読み方を変えれば戻りますが、**3番はファイルに元の情報が残っていません。**バックアップから戻すしかありません。

4. 13番と14番が一番たちが悪い

**エンコーディングとしては正しくても、中身がすでに壊れていることがあります。**13番と14番がそれです。

この2つは同じ失敗です。nodeの出力をPowerShellが受けた時点で、文字列がもう別物になっています。違うのは、そのあとの保存の仕方だけ。

# 保存の仕方 先頭バイト ファイルとしては
13 Out-File(5.1の既定はUTF-16LE) FF FE 5D 7E 正しいUTF-16
14 Out-File -Encoding utf8 EF BB BF E7 正しいUTF-8(BOM付き)

中身はどちらも 繝輔ぃ繧、繝ォ縺瑚ヲ九▽縺九j縺セ縺帙s。正しい先頭はE3 83 95 E3(ファ…)のはずでした。これは、元のUTF-8バイト列をCP932として読んだときの文字列と一致します。二重に変換されているので、どちらの読み方でも戻りません。

・3番は開いた瞬間に ?が並ぶので気づけます
・14番はBOMが正しく付いた、UTF-8として完全に妥当なファイルです
・先頭バイトを検査するCIを入れても、14番は合格します

BOMの有無は、中身が正しいことの証明にはなりません。

実務でどうしたか

書くとき

・自分のスクリプトからはSet-Content -Encoding UTF8に統一する
・既定のSet-Content / Add-Contentを使わない
・-Encoding asciiは使わない

外部プロセスの出力を受けるとき

・cmd.exe側でリダイレクトする
・PowerShellで受けてからOut-Fileに流さない(13番・14番になる)

対照:ACPを固定して、PowerShellのバージョンだけを変えた

最初はLinux / PowerShell 7.4.6 / ACP=65001と比べていました。それだとPowerShellのバージョンとACPが同時に違うので、どちらが効いたのか分けられません。

そこで同じWindows機にPowerShell 7.6.5を入れて、ACPを932に固定したまま測り直しました。

項目 1本目 2本目
PowerShell 5.1.26100.9168 7.6.5
OS NT 10.0.26100.0 同じ
CurrentCulture ja-JP 同じ
system ACP(レジストリ) 932 932
実行時刻 14:23:58 14:24:23

ここで一度失敗しました。最初ACPを[System.Text.Encoding]::Default.CodePageで読んでいましたが、これは5.1ではACPを返す一方、PowerShell 7では常に65001を返します(.NET Coreの既定がUTF-8のため)。ACPと書いた行が、ACPではありませんでした。レジストリのHKLM:\SYSTEM\CurrentControlSet\Control\Nls\CodePageのACPを読み直して、両方932であることを確認しています。

PowerShell 7.6.5での結果です。

# 書き方 先頭バイト bare -Enc UTF8
1 Out-File既定 E3 83 95 E3 OK OK
2 Out-File -Encoding utf8 E3 83 95 E3 OK OK
3 Out-File -Encoding ascii 3F 3F 3F 3F 化ける 化ける
4 Set-Content既定 E3 83 95 E3 OK OK
5 Set-Content -Encoding UTF8 E3 83 95 E3 OK OK
6 Add-Content既定 E3 83 95 E3 OK OK
7 Tee-Object -FilePath E3 83 95 E3 OK OK
8 リダイレクト E3 83 95 E3 OK OK
9 IO.File WriteAllText (UTF8) E3 83 95 E3 OK OK
10 IO.File WriteAllBytes (UTF8) E3 83 95 E3 OK OK
11 pythonをcmd.exeでリダイレクト E3 83 95 E3 OK OK
12 nodeをcmd.exeでリダイレクト E3 83 95 E3 OK OK
13 nodeの出力をPSで受けてOut-File E7 B9 9D E8 化ける 化ける
14 nodeの出力をPSで受けて-Encoding utf8 E7 B9 9D E8 化ける 化ける

**14ケース中、11ケースがOK / OKになりました。**残ったのは3番と13・14番です。

書き出しのエンコーディングも全部E3 83 95 E3、つまりBOM無しUTF-8に揃っています。5.1ではUTF-16LE・CP932・BOM付きUTF-8に散らばっていたものが、1つになりました。

ACPは932のまま動かしていません。変わったのはPowerShellのバージョンだけです。

PowerShell 7にしても、13番と14番は直らなかった

7.6.5では書き込み側がほぼUTF-8に揃いました。それでも

・13番node → PowerShell → Out-File
・14番node → PowerShell → Out-File -Encoding utf8

の2つは、どちらも壊れたままでした。3番も同じく壊れたままです(こちらは日本語が ?に潰れていて、ファイルに元の情報が残っていないため)。

環境の欄を並べるとこうなっています。

項目 5.1 7.6.5
[Console]::OutputEncoding 932 shift_jis 932 shift_jis
$OutputEncoding 20127 us-ascii 65001 utf-8

PowerShell 7にしたことで$OutputEncodingはUTF-8になりましたが、[Console]::OutputEncodingは932のままでした。

そして今回の測定では、nodeがUTF-8で出した日本語をPowerShellで受ける経路だけが、壊れて残りました。

この時点では、この2つが結びついているかどうかを切り分けていませんでした。言えたのは「7.6.5では[Console]::OutputEncodingが932のままだった」と「その環境で13番・14番が壊れた」の2つが同時に観測された、というところまでです。**2026-08-26に切り分けました。**下の「切り分けた結果」を見てください。

この1台で分かったこと

1. PowerShell 7では書き込み側が単純になった

5.1では、今回試した正常系でもUTF-16LE・CP932・UTF-8 BOM付き・UTF-8 BOM無しが混在していました。7.6.5では、今回試した正常系がすべてUTF-8 BOM無しになりました。

そのためGet-Content file.txtでもGet-Content file.txt -Encoding UTF8でも、今回の正常系は読めました。5.1で起きていた「どちらか片方しか読めない」が消えています。

2.しかし、外部プロセスとの境界は別問題

経路 7.6.5での結果
node → cmd.exe → ファイル OK
node → PowerShell → ファイル NG

同じnodeの出力です。PowerShellを経由するかどうかだけが違います。ファイルをUTF-8にするだけでは終わりません。

3. $OutputEncodingと[Console]::OutputEncodingは同じものではない

7.6.5では$OutputEncodingがUTF-8、[Console]::OutputEncodingがShift_JISでした。

ここを混同すると「PowerShell 7はUTF-8だからnodeのUTF-8出力も大丈夫」と考えてしまいます。今回の実測では、そうなりませんでした。

自分の環境で測れるようにした

この表を自分のマシンで再現するスクリプトを書きました。100% ASCIIで書いてあるので、保存の仕方でスクリプト自身が壊れることはありません。日本語のテスト文字列は、UTF-8のバイト配列から組み立てています。

python / nodeが入っていない環境では11から14が測れません。その場合は「測れなかった」と表の下に出るようにしました。黙って省略はしません。

測っていないこと

・上の表はWindows 11 ja-JPの1台で測った値です。他の機種では確認していません
・-Encoding Default / -Encoding OEMは測っていません
・PowerShell 7.6.5以外の7.xは測っていません
・CP949(韓国語)/ CP936(簡体字)での挙動は測っていません

切り分けた結果(2026-08-26 追記)

この記事に「次に測る」と書いていた実験を実行しました。読み方は先に決めてありました。

[Console]::OutputEncodingを変えて、13番・14番だけを測り直しています。同じプロセスの中で、A(そのまま)→ B(UTF-8に変更)→ C(Aに戻す)と3回。

13番・14番は5.1と7.6.5の両方で、BのときだけOKになりました。そしてCで戻したら、また壊れました。

先に決めておいた表の、上の行です。

結果 そこから言えること
13・14がOKになる 今回の環境では、外部プロセスの標準出力を受け取るときのエンコーディング設定が効いていた
13・14がNGのまま [Console]::OutputEncodingが932だったことだけでは説明できない。原因はまだ特定できていない

同時に分かったことが3つあります。

**$OutputEncodingは関係ありませんでした。**5.1では20127 us-ascii、7.6.5では65001 utf-8と値が違いますが、13番・14番の結果は同じでした。

**PowerShellのバージョンも関係ありませんでした。**13番・14番に関しては5.1も7.6.5も同じ挙動です。上げても直りません。

**この設定を通るのは、PowerShellが受け取る経路だけでした。**11番・12番(cmd.exeのリダイレクト)は[Console]::OutputEncodingが932のままでもOKです。同じnodeの出力で、同じConsole設定で、11番・12番は通り13番・14番は壊れます。

**「PowerShellでは常にこれが原因」ではありません。**Windows 11 ja-JP / ACP=932の1台で切り分けた結果です。

書き方を変えられるなら、cmd.exe側でリダイレクトするのが一番簡単です。

node app.js > out.txt

同じプロセスの中で受け取る必要があるなら、受け取る前にこの1行を入れます。

[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)

まとめ

Windows 11 ja-JP / ACP=932の同じマシンで、PowerShellを5.1から7.6.5に変えて測りました。

・今回の14ケース中、3番と13・14番を除く11ケースがOK / OKになった
・3番(-Encoding ascii)は、ファイルに元の情報が残っていないので変わらなかった
・nodeのUTF-8出力をPowerShellで受ける13・14番は、7.6.5でも壊れた

「PowerShell 7にすればUTF-8問題は解決する」ではありません。

**ファイルI/O側はかなり改善するが、外部プロセスとの境界は別問題。**これが今回測った結果です。

そして、その境界がなぜ壊れるのかは、**2026-08-26に切り分けました。**今回の環境では[Console]::OutputEncodingでした。上の「切り分けた結果」に書いています。

これとは別の問題

この記事は「書いたファイルを読み返す」ときの話です。

.ps1スクリプトそのものが日本語環境だけで壊れる、という別の問題があります。そちらは測り終わったら別の記事にします。


測ってほしいことがあれば

手元で再現できるものなら、実際に動かして数字で返します。コメントに環境と再現手順を書いてもらえると早いです。結果がどちらに転んでも、そのまま出します。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?