posts

hugo.tomlをメモ帳で直したら文字化けした話(UTF-8・BOM・Shift-JISの違い)

Search Consoleにサイトマップを登録しようとしたところから始まった、想像以上に長いトラブルの記録です。たった1行を直すつもりが、設定ファイルの日本語が全部化け、直したと思ったら今度はHugoが動かなくなりました。

先に結論を書いておきます。

  • 日本語を含む設定ファイルを、Windowsのメモ帳で編集しない(VSCodeを使い、右下が「UTF-8」になっているか確認する)
  • 文字化けして見えても、ファイルが壊れているとは限らない(見るためのコマンド側の問題のこともある)
  • invalid character ... U+00EF 'ï' というエラーは、ファイルの先頭に付いたBOMが原因

発端:baseURLを1行直したかっただけ

Search Consoleでサイトマップを送ると「サイトマップ アドレスが無効です」と出ました。sitemap.xml を開くと、中のURLが全部 https://example.com/... という仮のドメインのまま。hugo.toml の baseURL が初期値のままだったのが原因です(この経緯は 第8回 に書いています)。

そこで、hugo.toml をメモ帳で開いて baseURL の1行だけ書き換えて保存しました。すると、ファイルの中の日本語が全部こうなりました。

description = "螟ァ蟄ヲ縺ァHTML/CSS繧貞ュヲ繧薙□遲・・′..."

そもそも文字コードとは

コンピュータは文字をそのまま扱えないので、すべての文字を数字に置き換えて保存しています。 「あ」をどの数字にするかという対応表が「文字コード」です。保存したときの対応表と、読むときの対応表がずれると、文字化けが起きます。

文字コード 特徴
Shift-JIS 昔からある日本語Windowsの標準。1文字を1〜2バイトで表す
UTF-8 世界中の文字を1つの表で扱える、今の標準。1文字を1〜4バイトで表す
UTF-8(BOM付き) UTF-8の先頭に「これはUTF-8です」という目印(BOM)を付けたもの

Hugoのファイルは、UTF-8(BOMなし)で保存するのが前提です。

原因の切り分け

これが単純そうに見えて、実は2段階になっていました。

1段階目:Shift-JISとUTF-8の取り違え

元の hugo.toml はUTF-8で書かれていました。それをメモ帳が違う文字コードとして扱って保存したため、日本語が化けました。

上の 螟ァ蟄ヲ縺ァ… は、UTF-8で書かれた文字を、Shift-JISのルールで読んでしまったときに出る、典型的な化け方です。この形の化け方を見たら、「UTF-8とShift-JISの取り違え」を疑うと早いです。

見るコマンドによって、化けたり化けなかったりする

ここでさらに混乱しました。PowerShellでファイルの中身を見ると、コマンドによって結果が違ったのです。

type hugo.toml                          # 化けて見える
Get-Content hugo.toml -Encoding UTF8    # 正しく見える

type は、ファイルを古い文字コードのつもりで表示しようとする癖があります。同じファイルなのに、見るコマンドによって化けたり化けなかったりするので、type の結果だけを見て「ファイルが壊れた」と決めつけるのは早すぎました。

2段階目:BOMでHugoが止まる

文字コードをUTF-8に合わせて保存し直し、Get-Content で見ても正しくなりました。ところが hugo を打つと、今度はこう言われました。

unmarshal failed: toml: invalid character at start of key: U+00EF 'ï'

原因は、メモ帳が「UTF-8」で保存するときに、ファイルの先頭に目に見えないBOMを付けていたことでした。BOM自体は正しい仕組みですが、hugo.toml の書式(TOML)ではBOMを想定していないので、先頭の見えない文字を「変な文字がある」と判断して止まります。

エラーに出ている U+00EF 'ï' は、BOMの最初の1バイト(EF)を1文字として読んだもの。つまり「ファイルの一番先頭に、見えない何かがある」という意味です。

直し方

そのとき筆者がやった方法:PowerShellでBOMなしで書き出す

メモ帳を通すとまた同じことが起きそうだったので、PowerShellから直接ファイルを書き出しました。

$text = @"
(ファイルの中身をここに)
"@
[System.IO.File]::WriteAllText("$PWD\hugo.toml", $text, (New-Object System.Text.UTF8Encoding $false))

UTF8Encoding $false が「BOMを付けない」という指定です。これで文字化けも、Hugoのエラーも直りました。

今のやり方:VSCodeで開いて、右下を確認する

今は設定ファイルも記事も、すべてVSCodeで編集しています。

  1. VSCodeでファイルを開く
  2. 画面の右下に出ている文字コードを見る
  3. 「UTF-8」になっていればOK。「UTF-8 with BOM」や「Shift JIS」になっていたら、そこをクリック →「エンコード付きで保存」→「UTF-8」を選ぶ

化けた状態で保存してしまった日本語は、あとから文字コードを切り替えても元に戻らないことがあります。筆者も、正しい中身を用意して丸ごと書き出し直しました。編集する前にファイルをコピーしておくと安心です。

その後:まだサイトマップが通らない

文字化けを直し、baseURL を https://noluckblog.com/ にしてサイトマップを送り直しても、まだ「取得できませんでした」でした。ブラウザで https:// で開くと「接続できません」。HTTPS化(証明書の導入)がまだだったのが原因で、第6回 の手順で証明書を入れたあと、ようやく

サイトマップは正常に処理されました
検出されたページ数: 23

となりました。1つ直すと次の問題が出てくる、という展開の連続でしたが、最後に成功が出たときは、それまでで一番達成感がありました。

つまずきポイントのまとめ

つまずいたこと 原因 学んだこと
サイトマップが無効と言われた baseURL が仮のドメインのままだった 設定ファイルの初期値を見落とさない
hugo.toml の日本語が全部化けた メモ帳がUTF-8のファイルを別の文字コードで保存した 日本語のファイルはVSCodeで編集し、右下の文字コードを見る
type では化け、Get-Content では正しく見えた type が古い文字コードで表示していた 「ファイルが壊れた」のか「見る側の問題」なのかを分けて考える
U+00EF 'ï' でHugoが止まった メモ帳が付けたBOM Hugoのファイルは「UTF-8(BOMなし)」で保存する
HTTPSのサイトマップが取得できない 証明書がまだ入っていなかった https:// にする前に、証明書を入れる

普段なにげなく保存している文字が、裏ではこんなに細かいルールで支えられていると実感した出来事でした。

あわせて読みたい