バッチファイルでよくある「UTF‑8のテキストが文字化けする」「UTF‑16のファイルを for /f で読めない」「日本語が ??? になる」といった問題は、cmd.exeのコードページとファイルのエンコーディングの不一致が主因です。
この記事では根本原因の理解から、chcp 65001 による切り替え・PowerShell委譲・cmd /U の使い分けまで、実践例付きで体系的に解説します。
日本語の一般的な文字化けについては「バッチの文字化けを直す方法(chcp 65001・UTF-8・Shift-JIS)」も合わせてご覧ください。
- 問題の本質:cmd.exeとUnicodeの歴史的制約
- ステップ1:現状確認(コードページとBOMを調べる)
- 原因1:バッチファイル自体をBOM付きUTF-8で保存している
- 原因2:コンソールのコードページとファイルのエンコーディングが不一致
- 原因3:for /f がUTF-8/UTF-16を正しく読めない
- UTF-8・UTF-16でファイルに書き出す
- cmd /U でパイプ・リダイレクトをUTF-16LEにする
- Unicodeファイル名(日本語・絵文字)の操作
- 実践例A:UTF-8 CSVをfor /fで処理してコンソールに表示
- 実践例B:処理ログをUTF-8 BOMなしで記録する
- 実践例C:Shift-JIS → UTF-8 BOMなし 変換バッチ
- 切り分け手順:それでも化けるときのフロー
- よくある落とし穴5選
- よくある質問(FAQ)
- まとめ
問題の本質:cmd.exeとUnicodeの歴史的制約
cmd.exe(コマンドプロンプト)は歴史的に Shift-JIS(コードページ932)前提で動作しており、Unicodeネイティブではありません。
| コードページ | chcp番号 | 主な用途 | cmd.exeとの相性 |
|---|---|---|---|
| Shift-JIS | 932 | 日本語Windows標準 | ◎ 安定 |
| UTF-8 | 65001 | Web・クロスプラットフォーム | △ 部分的(for /f が不安定) |
| UTF-16LE | 1200 | Windowsメモ帳・PowerShell出力 | ✕ 直接読み書き不可 |
| US-ASCII | 437 | 英語環境 | ◎ 安定(英数字のみ) |
つまり UTF-8やUTF-16ファイルを扱うためには、何らかの橋渡しが必要です。
ステップ1:現状確認(コードページとBOMを調べる)
現在のコードページを確認する
:: 現在のコードページを確認 chcp :: 出力例: :: Active code page: 932 (Shift-JIS) :: Active code page: 65001 (UTF-8) :: Active code page: 1200 (UTF-16LE)
ファイルのエンコーディングを確認する(BOMチェック)
BOM(Byte Order Mark)はファイル先頭の特殊バイト列で、エンコーディングの識別子です。
| エンコーディング | BOM(先頭バイト) | メモ帳保存時の名称 |
|---|---|---|
| UTF-8 BOM付き | EF BB BF | “UTF-8 (BOM付き)” |
| UTF-8 BOMなし | (なし) | “UTF-8” |
| UTF-16LE | FF FE | “Unicode” |
| UTF-16BE | FE FF | “Unicode(big endian)” |
| Shift-JIS | (なし) | “ANSI” |
@echo off
:: ファイルの先頭バイトをHEXで表示してBOMを確認
powershell -NoProfile -Command ^
"$b=(Get-Content -Encoding Byte -ReadCount 4 -TotalCount 4 'input.txt')[0];"
"if($b[0] -eq 0xEF -and $b[1] -eq 0xBB -and $b[2] -eq 0xBF){'UTF-8 BOM付き'}"
"elseif($b[0] -eq 0xFF -and $b[1] -eq 0xFE){'UTF-16LE (BOM付き)'}"
"elseif($b[0] -eq 0xFE -and $b[1] -eq 0xFF){'UTF-16BE'}"
"else{'BOMなし(UTF-8またはSJIS)'}"
原因1:バッチファイル自体をBOM付きUTF-8で保存している
バッチファイル(.bat)を BOM付きUTF-8 で保存すると、先頭の3バイト(EF BB BF)がコマンドとして解釈されエラーになります。
:: NG: バッチファイル自体をBOM付きUTF-8で保存した場合 :: 先頭3バイト(EF BB BF)がコマンドとして解釈される :: :: エラー例: :: '・@echo' is not recognized as an internal or external command :: :: 対処: バッチファイルはBOMなしUTF-8 または ANSI(SJIS) で保存する :: VSCode: 右下エンコーディング表示 → "UTF-8"(BOMなし)を選択 :: メモ帳: [名前を付けて保存] → 文字コード "UTF-8"(BOMなし)
バッチファイルの保存形式:.bat 本文は BOMなしUTF-8 または ANSI(Shift-JIS)で保存します。日本語文字列は外部ファイルやPowerShellから読み込む設計が安全です。
原因2:コンソールのコードページとファイルのエンコーディングが不一致
コンソールが CP932(SJIS)のまま UTF-8 ファイルを type や echo で表示しようとすると文字化けします。
解決策:chcp 65001 で一時切り替え(表示・簡易処理用途)
表示が目的であれば chcp 65001 に切り替えてから処理し、最後に元に戻すのが最小限の対処です。
@echo off
setlocal
:: 元のコードページを保存
for /f "tokens=2 delims=: " %%A in ('chcp ^| find ":"') do set "_orig=%%A"
:: UTF-8 モードに切り替え
chcp 65001 >nul
:: UTF-8 テキストを表示
type utf8_file.txt
:: 元のコードページに戻す
chcp %_orig% >nul
endlocal
注意:chcp 65001 は for /f による行読み込みが不安定になることがあります(日本語文字の欠落・空行化)。ファイルの行処理にはPowerShell委譲を使ってください。
原因3:for /f がUTF-8/UTF-16を正しく読めない
for /f は内部的にANSIを前提としており、UTF-8/UTF-16の行を正確に読み取れません。
for /f の基本は「外部コマンドの結果を変数に格納する方法(for /f 活用)」を参照してください。
解決策:PowerShell Get-Content でエンコーディングを明示する
UTF-8 ファイルを行処理する:
@echo off setlocal :: PowerShell経由でUTF-8ファイルを1行ずつ処理 for /f "usebackq delims=" %%L in (` powershell -NoProfile -Command "Get-Content -Encoding UTF8 -Path 'C:\data\utf8.txt'" `) do ( echo 行: %%L ) endlocal
UTF-16LE(Windowsメモ帳「Unicode」)ファイルを行処理する:
@echo off setlocal :: PowerShell経由でUTF-16LE(Unicodeメモ帳保存)ファイルを処理 for /f "usebackq delims=" %%L in (` powershell -NoProfile -Command "Get-Content -Encoding Unicode -Path 'C:\data\utf16.txt'" `) do ( echo 行: %%L ) endlocal
PowerShellのバッチからの呼び出し方詳細は「PowerShellをバッチファイルから呼び出す方法」も参照してください。
UTF-8・UTF-16でファイルに書き出す
> リダイレクトで作られるファイルの文字コードは現在のコードページに依存します。確実に特定エンコーディングで保存するにはPowerShellを使います。
リダイレクトの詳細は「リダイレクト(> >> 2>&1)の使い方完全ガイド」も参照してください。
UTF-8 BOMなしで書き出す(推奨)
@echo off
setlocal
set "MSG=UTF-8 BOMなし書き出しのテスト"
:: PowerShell 5.x: UTF8はBOM付きになるため、.NET直接指定でBOMなし
powershell -NoProfile -Command ^
"[IO.File]::WriteAllText('out-utf8nobom.txt', $env:MSG,
[Text.Encoding]::UTF8)"
:: PowerShell 7+: UTF8NoBOM を使える
:: pwsh -NoProfile -Command "Set-Content -Path out.txt -Value $env:MSG -Encoding UTF8NoBOM"
endlocal
UTF-8 BOM付きで書き出す
@echo off setlocal set "MSG=UTF-8 BOM付き書き出しのテスト" :: PowerShell 5.x: Set-Content の UTF8 は BOM付き powershell -NoProfile -Command ^ "Set-Content -Path 'out-utf8bom.txt' -Value $env:MSG -Encoding UTF8" endlocal
UTF-16LE で書き出す
@echo off setlocal set "MSG=UTF-16LE 書き出しのテスト" powershell -NoProfile -Command ^ "Set-Content -Path 'out-utf16.txt' -Value $env:MSG -Encoding Unicode" endlocal
| 方法 | エンコーディング | BOM | PowerShell 5.x | PowerShell 7+ |
|---|---|---|---|---|
| Set-Content UTF8 | UTF-8 | 付き | BOM付き | BOMなし |
| WriteAllText UTF8 | UTF-8 | なし | BOMなし | BOMなし |
| WriteAllLines UTF8 | UTF-8 | なし | BOMなし(末尾改行あり) | BOMなし |
| Set-Content Unicode | UTF-16LE | 付き | BOM付き | BOM付き |
cmd /U でパイプ・リダイレクトをUTF-16LEにする
cmd /U を使うと、パイプとリダイレクトのストリームが UTF-16LE になります。
:: cmd /U: パイプとリダイレクトのストリームをUTF-16LEにする cmd /U /C "dir C:\data\ > C:\work\dir-utf16.txt" :: UTF-16LE出力をPowerShellで読む powershell -NoProfile -Command "Get-Content -Encoding Unicode 'C:\work\dir-utf16.txt'"
注意:cmd /U は工程全体でUTF-16LEを扱える場合に限定します。下流がANSIを期待していると別の文字化けを招きます。
Unicodeファイル名(日本語・絵文字)の操作
ファイル名が非ASCIIの場合も扱いに注意が必要です。
@echo off setlocal :: 日本語ファイル名はダブルクォートで囲めば通常操作可能 copy "C:\data\日本語レポート.txt" "D:\backup\日本語レポート.txt" :: 絵文字・異体字などはcmd.exeで扱えない場合がある :: → PowerShell -LiteralPath を使う(ワイルドカード展開なし) powershell -NoProfile -Command ^ "Copy-Item -LiteralPath 'C:\data\レポート2024.txt' ^ -Destination 'D:\backup\レポート2024.txt' -Force" endlocal
実践例A:UTF-8 CSVをfor /fで処理してコンソールに表示
ヘッダー行をスキップしてデータ行をカンマ区切りで処理する例です。
@echo off setlocal :: UTF-8 CSVを読んでコンソール(SJIS)に表示 :: ヘッダー行をスキップしてデータ行だけ処理 set "SKIP=1" for /f "usebackq skip=1 tokens=1,2,3 delims=," %%A in (` powershell -NoProfile -Command "Get-Content -Encoding UTF8 -Path 'C:\data\sales.csv'" `) do ( echo 日付: %%A 商品: %%B 金額: %%C ) endlocal
実践例B:処理ログをUTF-8 BOMなしで記録する
日時付きログをUTF-8 BOMなしで追記するサブルーチンパターンです。Excelや他ツールとの連携に適しています。
@echo off
setlocal
set "LOGFILE=C:\logs\process_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.log"
call :log 処理開始
:: --- ここに処理を記述 ---
call :log ファイルのコピー完了
:: -------------------------
call :log 処理終了
echo ログ: %LOGFILE%
endlocal
goto :EOF
:log
:: UTF-8 BOMなしで追記(.NETのStreamWriter使用)
powershell -NoProfile -Command ^
"$w=New-Object IO.StreamWriter('%LOGFILE%',$true,[Text.Encoding]::UTF8);"
"$w.WriteLine('[%DATE% %TIME%] %1');$w.Close()"
goto :EOF
実践例C:Shift-JIS → UTF-8 BOMなし 変換バッチ
既存のSJISファイルをUTF-8 BOMなしに一括変換します。Web公開やGit管理前の前処理として使えます。
@echo off
setlocal
set "SRC=C:\data\sjis_file.txt"
set "DST=C:\data\utf8_file.txt"
:: Shift-JIS (cp932) から UTF-8 BOMなしへ変換
powershell -NoProfile -Command ^
"$content = Get-Content -Encoding Default -Path '%SRC%';"
"[IO.File]::WriteAllLines('%DST%', $content, [Text.Encoding]::UTF8)"
if errorlevel 1 (
echo [ERROR] 変換に失敗しました
exit /b 1
)
echo 変換完了: %SRC% -^> %DST%
endlocal
切り分け手順:それでも化けるときのフロー
chcpでコンソールのコードページを確認する- 対象ファイルのBOM・エンコーディングをエディタまたはPowerShellで確認する
typeでファイルを直接表示し、コンソールの問題か中身の問題かを切り分ける- PowerShell の
Get-Content -Encoding UTF8で正しく読めるか確認する - 正しく読めるなら → cmd.exe の限界。PowerShell委譲に切り替える
- PowerShellでも化けるなら → ファイル自体のエンコーディングが想定と異なる
日本語文字化けの詳細な切り分けは「バッチファイルで日本語が文字化けする原因と解決策」も参照してください。
よくある落とし穴5選
落とし穴1:chcp 65001 で for /f が不安定になる
UTF-8モードでの for /f は日本語が欠落したり空行になることがあります。
:: NG: chcp 65001 のまま for /f を使うと文字が欠落することがある chcp 65001 >nul for /f "delims=" %%L in (utf8.txt) do echo %%L :: → 日本語が欠けたり空行になることがある :: OK: PowerShell経由でエンコーディングを明示して読む for /f "usebackq delims=" %%L in (` powershell -NoProfile -Command "Get-Content -Encoding UTF8 -Path 'utf8.txt'" `) do echo %%L
落とし穴2:PowerShell 5.x の Set-Content UTF8 はBOM付き
WindowsデフォルトのPowerShell 5.xでは、UTF8指定はBOM付きになります。ExcelやWebサーバーとの連携でトラブルになることがあります。
:: NG: PowerShell 5.x の UTF8 は BOM付き(Excel等が誤認することがある)
powershell -NoProfile -Command "Set-Content -Path out.txt -Value $env:MSG -Encoding UTF8"
:: → EF BB BF (BOM) が先頭に付く
:: OK: .NET の StreamWriter または WriteAllText を使う
powershell -NoProfile -Command ^
"[IO.File]::WriteAllText('out.txt', $env:MSG, [Text.Encoding]::UTF8)"
:: → BOMなし UTF-8
落とし穴3:リダイレクト > で作ったファイルはコードページ依存
echo 日本語 > file.txt で作ったファイルはコンソールのコードページ(通常SJIS)で保存されます。
:: NG: リダイレクト出力のコードページはコンソールに依存
@echo off
echo 日本語テキスト > output.txt
:: → chcp 932(SJIS)環境では SJIS で保存される
:: OK: PowerShell で明示的に書き出す
powershell -NoProfile -Command ^
"[IO.File]::WriteAllText('output.txt', '日本語テキスト', [Text.Encoding]::UTF8)"
落とし穴4:for /f の tokens 分割でマルチバイト文字が壊れる
UTF-8の日本語文字(2〜3バイト)の区切り文字誤認識でフィールドが壊れることがあります。
:: NG: tokens= でフィールド分割するとUTF-8の2バイト目が, に見えることがある
for /f "tokens=1,2 delims=," %%A in (utf8_csv.txt) do echo %%A %%B
:: OK: PowerShell で分割してから渡す
for /f "usebackq delims=" %%L in (`
powershell -NoProfile -Command
"Import-Csv -Encoding UTF8 -Path 'utf8_csv.txt' | ForEach-Object { $_.Name + ',' + $_.Value }"
`) do echo %%L
落とし穴5:type コマンドのBOM自動処理の誤解
type はBOM付きUTF-16LEを自動変換しますが、BOMなしUTF-8はchcp 65001切り替えが必要です。
:: Windows の type コマンドはBOM付きUTF-16LEファイルを自動変換して表示できる :: しかしBOMなしUTF-8では化ける :: :: BOM付きUTF-16LE → type で正しく表示される type utf16le_bom.txt :: BOMなしUTF-8 → chcp 65001 に切り替えてから type chcp 65001 >nul type utf8_nobom.txt chcp 932 >nul
よくある質問(FAQ)
for /f は不安定で、日本語が欠落することがあります。PowerShell の Get-Content -Encoding UTF8 経由で読み込む方法に切り替えてください。-Encoding Unicode を指定します。[IO.File]::WriteAllText() または [IO.File]::WriteAllLines() に [Text.Encoding]::UTF8 を指定します。Set-Content -Encoding UTF8(PowerShell 5.x)はBOM付きになるため注意してください。Set-Content -Encoding UTF8 がBOM付きUTF-8を出力しますが、PowerShell 7+(pwsh)の既定はBOMなしUTF-8です。移植性のためには明示的に指定することを推奨します。Get-Content -Encoding Default(SJIS読み込み)と [IO.File]::WriteAllText()(UTF-8 BOMなし書き出し)を組み合わせて変換できます。フォルダ内のファイルを一括変換する場合は Get-ChildItem と組み合わせてください。まとめ
バッチでUnicodeファイルを扱う際の指針をまとめます。
| 用途 | 推奨方法 |
|---|---|
| UTF-8ファイルを表示する | chcp 65001 → type(表示のみ) |
| UTF-8ファイルを行処理する | PowerShell Get-Content -Encoding UTF8 + for /f |
| UTF-16LEファイルを行処理する | PowerShell Get-Content -Encoding Unicode + for /f |
| UTF-8 BOMなしで書き出す | [IO.File]::WriteAllText()(.NET直接) |
| UTF-8 BOM付きで書き出す | PowerShell 5.x: Set-Content -Encoding UTF8 |
| SJIS→UTF-8変換 | PowerShell Get-Content -Encoding Default + WriteAllText |
| バッチファイルの保存形式 | BOMなしUTF-8 または ANSI(SJIS) |
| Unicodeファイル名を操作する | PowerShell -LiteralPath を使う |
cmd.exeは歴史的な制約でUnicodeネイティブではありませんが、PowerShellへの委譲でほぼすべての問題を解決できます。
chcp・文字化けの全般については「バッチの文字化けを直す方法(chcp 65001・UTF-8・Shift-JIS)」、日本語固有の問題は「バッチで日本語が文字化けする原因と解決策」も合わせてご覧ください。

