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?

G21 【外伝】はいはい文字化け文字化け UTF-8 Shift_JIS BOMを疑う順番

Posted at

G21 【外伝】はいはい文字化け文字化け UTF-8 Shift_JIS BOMを疑う順番

連載Index(読む順・公開済(リンク)はここが最新): S00_門前の誓い_総合Index

「はいはい文字化けね」で終わらせると、だいたい長引くことがあります。
文字コードは「原因を当てる」より、疑う順番を揃えた方が早いです。

現場で止まりやすいのは、次のような場面です。

  • 同じCSV/TSVのはずなのに、日本語だけ崩れる(文字化けする)
  • 先頭行だけ列がズレる/ヘッダ一致だけ失敗する
  • 「UTF-8で渡した」と言われたのに、こちらでは再現しない
  • バイトで切ったら文字が欠ける/例外になる

半角は1バイト、全角は2バイト以上。そこまでは分かっていても、切り分けに必要なのはその先です。
どう読むか(UTF-8 / Shift_JIS)先頭に何が混ざるか(BOM) を、順番で整理します。


1. このページで押さえること

  • UTF-8 / Shift_JIS の違いを「読み方の違い」として説明できる
  • BOM(先頭の印)が混ざると何が起きるかを説明できる
  • 「文字数」と「バイト数」を混同せずに扱える
  • .NET 8 を基本に、.NET Framework 4.8でも考え方は同じ

2. 結論(疑う順番)

2-1. まずはこの順番

文字化けや先頭ズレに気づいた瞬間、いきなりUTF-8/Shift_JISを当てに行くと外しやすいです。
最初にやるべきは「文字コードを当てる」ではなく、入力の前提を揃えることになります。

なぜなら、文字コードは「推理」ではなく 運用の前提 で決まることが多いからです。
同じUTF-8でも、BOMの有無や、相手側の都合(Excel/古い連携)で読み方が変わることもあります。

まず 1) で確認するのは、この2点です。

  1. 入力の前提を確認する
  • どこから来たテキストか(Excelで手作り/外部システムの連携/別アプリの出力/ログ保存など)
  • 受け側で「何として読むか」を決められるか(規約化できるか)

ここが揃うと、次に何を疑うべきかが絞れます。

  • Excel由来なら「Shift_JIS」や「UTF-8(BOMあり)」が混ざりやすい
  • 外部連携なら、相手の仕様書に「文字コード」「BOM有無」が書かれていることが多い
  • 自システム内の生成物なら、こちらの出力側をUTF-8に寄せて統一できることが多い
  • ログなら、書き出し側のEncodingが決まっているので、読む側はそれに合わせるだけで済むことが多い

前提が揃ったら、疑う順番は次です。

  1. 先頭だけおかしいならBOMを疑う
  • ヘッダ一致だけ失敗する
  • 先頭列だけズレる
  • 先頭に見えない文字が混ざる
  1. 文字化けならUTF-8とShift_JISの読み方を疑う
  • 送った側はUTF-8のつもり
  • 受け側はShift_JISのつもり
    このすれ違いが一番多いです。
  1. 切り詰め/固定長/上限チェックなら「文字」と「バイト」を分けて考える
  • string.Length は「見た目の文字数」ではありません
  • バイト数は Encoding.GetByteCount 側で決まります

2-2. 一度覚えておくとよい判断フロー


3. BOM(先頭の印)を疑うポイント

BOMは、ファイル先頭に付く「印」です。
UTF-8でも付くことがあり、先頭3バイト EF BB BF が混ざります。

BOMが混ざると、現場ではこう見えます。

  • CSV/TSVの先頭ヘッダだけ一致しない
  • 先頭列だけキーが見つからない
  • 先頭だけ変な文字が混ざって見える

3-1. BOMで先頭ヘッダが一致しなくなる例

Id のつもりが \uFEFFId になり、比較が落ちることがあります。
見た目では分かりません。

// 先頭にBOM由来のU+FEFFが混ざることがあります
static string RemoveLeadingBomChar(string s)
{
    if (string.IsNullOrEmpty(s)) return s;
    return s[0] == '\uFEFF' ? s.Substring(1) : s;
}

CSVのヘッダ照合で引っかかる場合は、最初のトークンだけでも除去して差分が出るか確認すると早いです。

3-2. バイト列でBOMを確認する(最短)

static bool HasUtf8Bom(byte[] bytes)
{
    return bytes.Length >= 3
        && bytes[0] == 0xEF
        && bytes[1] == 0xBB
        && bytes[2] == 0xBF;
}

4. UTF-8 と Shift_JIS(読み方の違い)

文字コードの違いは、ざっくり言うと「同じバイト列をどう読むか」の違いです。
UTF-8で書いたものをShift_JISとして読むと崩れ、逆も同様です。

4-1. 実務で多い組み合わせ

  • 自システム内はUTF-8で統一しているつもり
  • ただしExcel経由のCSVはShift_JIS(またはUTF-8 BOMあり)が混ざる
  • 外部連携は相手の都合でShift_JISが来ることがある

この混在が「端末だけ」「ファイルだけ」に見える原因になりがちです。

4-2. 読み込み側はEncodingを明示する

「既定に任せる」が一番ブレます。
読み込み側で明示し、想定外なら弾く/分岐する設計へ寄せます。

using System.Text;

var textUtf8 = File.ReadAllText(path, Encoding.UTF8);
var textSjis = File.ReadAllText(path, Encoding.GetEncoding("shift_jis"));

4-3. .NET 8でShift_JISを使う時の注意

.NET 8 では、Shift_JISなどのコードページを使うために登録が必要になることがあります。

using System.Text;

// 起動時に一度だけ
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

// 以降、shift_jisが取得できます
var sjis = Encoding.GetEncoding("shift_jis");

.NET Framework 4.8は、環境によって最初から使えることが多いです。


5. 文字数とバイト数を混同しない

「半角=1バイト、全角=2バイト以上」の知識は入口として有用です。
ただし実務で詰まるのは、次の混同です。

  • string.Length を「バイト数」だと思って切る
  • 固定長ファイルの仕様(バイト単位)を「文字数」で処理してしまう
  • エンコーディングが変わるのに上限チェックが同じだと思ってしまう

5-1. バイト数はEncodingで決まる

using System.Text;

var s = "Aあ😀";

Console.WriteLine($"Length(char数): {s.Length}");
Console.WriteLine($"UTF-8 bytes: {Encoding.UTF8.GetByteCount(s)}");

Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
var sjis = Encoding.GetEncoding("shift_jis");
Console.WriteLine($"Shift_JIS bytes: {sjis.GetByteCount(s)}");

Length は「UTF-16の要素数」に近く、見た目の文字数と一致しないことがあります。
バイト数は GetByteCount 側で確認するとズレません。


6. 実務レシピ(迷いを減らす)

6-1. まず規約にできるかを確認する

  • 入力はUTF-8に統一できるか
  • BOMは付けるか付けないか
  • 改行はCRLF/LFどちらに寄せるか
  • Excel要件があるなら、Excelで想定する形式(UTF-8 BOMあり/Shift_JIS)を明確にする

規約にできるなら、推測より規約が強いです。

6-2. 規約にできない場合は「受け口で分岐」を用意する

  • 受け口でBOMの有無を確認する
  • UTF-8/Shift_JISのどちらとして扱うかを選べるようにする
  • 判定できない場合は「読めない」扱いにしてログへ残す

「なんとなく読めた」状態で先へ進むと、後で原因が追えなくなります。

6-3. ログに残す観点(切り分けを速くする)

最低限、次が残っていると切り分けが速くなります。

  • ファイルサイズ
  • BOMの有無
  • 読み込みに使ったEncoding名(UTF-8 / Shift_JISなど)
  • 先頭行(必要なら先頭数十バイトのHex)

7. チェックリスト(レビューで止める)

  • File I/OでEncodingを明示している
  • BOM方針(付ける/付けない)が決まっている、混在させない
  • 先頭ヘッダ不一致の時にBOM混入を疑える作りになっている
  • string.LengthEncoding.GetByteCount を混同していない
  • .NET 8でShift_JISを扱う場合に Encoding.RegisterProvider(...) が入っている
  • Excel要件がある場合に「Excelで読む前提の形式」が明記されている

関連トピック


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?