program_growth_labの日記

このブログは Python 初心者が AI 相棒と一緒に成長していくログです

第7回:【Windows】PowerShellでBOMを確認する方法:先頭3バイトで判定(PowerShell 5.1対応)

こんにちは。いーかです。
この記事では、Windowsで「このファイル、BOM付いてる?」を PowerShellだけで確実に確認する方法をまとめます。
CSV/JSON/TXTなど、UTF-8テキスト全般にそのまま使えます。


⚠️ 注意:PowerShell 5.1は“環境差”と“.ps1文字化け”がある(最初にここだけ)

PowerShellバージョンによって使える引数や挙動が違うことがあります。
実際に私の環境(Windows PowerShell 5.1)では、次の地雷がありました。

  • Get-Content -AsByteStream存在しない

  • Format-Hex の引数(例:-Count)が 環境によって合わないことがある

  • .ps1UTF-8(BOMなし) で保存すると文字化けして、スクリプト自体が壊れることがある(超あるある)

もし .ps1 を作って実行するなら、VS CodeUTF-8 with BOM」(または UTF-16 LE)で保存すると事故が減ります。

この記事はその前提で、Windows PowerShell 5.1でも確実に動く「.NET(ReadAllBytes)」を主役にして紹介します。
PowerShell 7系の人向けに Format-Hex は補足として最後に載せます)


私の環境(参考)

自分のバージョンを確認する方法

$PSVersionTable.PSVersion

結論(1行)

ファイル先頭に EF BB BF が見えたら「UTF-8 BOMあり」です。PowerShell 5.1なら .NET(ReadAllBytes) が最短・確実です。


まず前提:BOMって何を見ればいい?

UTF-8のBOM(Byte Order Mark)は、ファイル先頭の3バイトが固定です。

  • UTF-8 BOMありEF BB BF

  • BOMなし → 先頭に EF BB BF出ない

※注意:BOMが無い=UTF-8確定ではありません(Shift-JISなどもあり得ます)。
この記事は「UTF-8 BOM(EF BB BF)があるか」を判定します。


重要:.\sample.csv は例です(ここで詰まりやすい)

この記事の .\sample.csv例のファイル名です。
あなたが確認したいファイルに置き換えてください。

  • カレントフォルダにあるなら:.\yourfile.csv

  • 場所が違うならフルパスで:D:\AI\blog\sample.csv


最短・確実(PS5.1対応):先頭3バイトを表示する(確認用)

※この方法は環境差に強く、Windows PowerShell 5.1でも安定して動きます。

# 例:確認したいファイルに置き換えてください
$path = ".\sample.csv"

# ファイルの存在チェック(コピペ事故防止)
if (-not (Test-Path -LiteralPath $path)) {
  Write-Error "ファイルが見つかりません: $path"
  return
}

# .NETでバイトとして読む(確実)
$all = [System.IO.File]::ReadAllBytes($path)

# 3バイト未満(空ファイルなど)は判定不能
if ($all.Length -lt 3) {
  "ファイルが3バイト未満です(空ファイルなど)。BOM判定できません。"
  return
}

# 先頭3バイトを16進で表示(EF BB BF が出たらBOMあり)
$head = $all[0..2]
'{0:X2} {1:X2} {2:X2}' -f $head[0], $head[1], $head[2]

見方(ここだけ)

  • EF BB BFUTF-8 BOMあり

  • それ以外 → BOMなし(または別形式)


最短・確実(PS5.1対応):BOM判定まで一気に(True/False)

$path = ".\sample.csv"

if (-not (Test-Path -LiteralPath $path)) {
  Write-Error "ファイルが見つかりません: $path"
  return
}

$all = [System.IO.File]::ReadAllBytes($path)

$hasBom =
  ($all.Length -ge 3) -and
  ($all[0] -eq 0xEF) -and
  ($all[1] -eq 0xBB) -and
  ($all[2] -eq 0xBF)

$hasBom
  • TrueBOMあり(UTF-8 with BOM)

  • FalseBOMなし(または別形式)


仕上げ:人間向けのメッセージで出す(コピペOK)

$path = ".\sample.csv"

if (-not (Test-Path -LiteralPath $path)) {
  Write-Error "ファイルが見つかりません: $path"
  return
}

$all = [System.IO.File]::ReadAllBytes($path)

$hasBom =
  ($all.Length -ge 3) -and
  ($all[0] -eq 0xEF) -and
  ($all[1] -eq 0xBB) -and
  ($all[2] -eq 0xBF)

if ($hasBom) {
  'BOMあり(UTF-8 BOM: EF BB BF)'
} else {
  'BOMなし(または別形式)'
}

複数ファイルをまとめてチェック(フォルダ運用向け・PS5.1対応)

※フォルダ内のCSV/JSON/TXTを「BOMあり/なし」で一覧表示します。
※空ファイルなど 短すぎるファイルは「判定不能」にします(落ちないようにする)。

$dir = "."
$patterns = @("*.csv","*.json","*.txt")

foreach ($pat in $patterns) {
  Get-ChildItem -Path $dir -Filter $pat -File | ForEach-Object {

    $path = $_.FullName
    $all  = [System.IO.File]::ReadAllBytes($path)

    if ($all.Length -lt 3) {
      '{0}`t{1}' -f '判定不能(短すぎ)', $path
    } else {
      $hasBom =
        ($all[0] -eq 0xEF) -and
        ($all[1] -eq 0xBB) -and
        ($all[2] -eq 0xBF)

      '{0}`t{1}' -f ($(if ($hasBom) { 'BOMあり' } else { 'BOMなし' }), $path)
    }
  }
}

よくある質問:なぜ「文字」じゃなく「バイト」で見るの?

BOMは「文字」ではなく、ファイル先頭に付く バイト列の目印だからです。

  • 文字として読むと、環境やデコードで挙動がブレる

  • バイトとして読むと、EF BB BF をそのまま確認できる

BOM確認は バイトで見るのが正解です。


補足:Format-Hex を使う場合(PowerShell 7系向け)

Format-Hex は便利ですが、引数は環境差があります。
まず自分の環境で使える引数を確認してください。

Get-Help Format-Hex -Full

30秒まとめ

  • UTF-8 BOMありなら先頭3バイトが EF BB BF

  • BOMなし=UTF-8確定ではない(Shift-JIS等もあり得る)

  • PowerShellはバージョン差がある(5.1は引数で詰まりやすい)

  • PS5.1なら .NET ReadAllBytes が最短・確実

  • フォルダ一括チェックで「混在」を早期発見できる


今日の1アクション

怪しいファイルを1つ選んで、まずはこれだけ:

$path=".\sample.csv"
$all=[System.IO.File]::ReadAllBytes($path)
($all.Length -ge 3) -and ($all[0] -eq 0xEF) -and ($all[1] -eq 0xBB) -and ($all[2] -eq 0xBF)

True なら BOMあり
次はVS CodeUTF-8(BOMなし) に保存し直すと再発が減ります。


今日のひとこと(いーかの成長ログ)

“見えない問題”は、バイトで見ると一発で見える。