最終更新:

UnicodeとUTF-8の違いは?文字化け・文字数・正規化を図解


コードポイントとUTF-8の表現 A コードポイント U+0041 UTF-8:41 1バイト あ コードポイント U+3042 UTF-8:E3 81 82 3バイト 😀 コードポイント U+1F600 UTF-8:F0 9F 98 80 4バイト 絵文字全体が常に4バイトとは限らない コードポイントとUTF-8の表現 A コードポイント U+0041 UTF-8:41 1バイト あ コードポイント U+3042 UTF-8:E3 81 82 3バイト 😀 コードポイント U+1F600 UTF-8:F0 9F 98 80 4バイト 絵文字全体が常に4バイトとは限らない
絵文字全体が常に4バイトとは限らない
ひよこ ひよこ
文字化けはどうして起きるの?
ペンギン先生 ペンギン先生
保存したバイト列と、それを文字へ戻すときの文字コードが合わないと、意図と違う文字になることがあるよ。データの欠損や複数回の誤った変換でも起きる。Unicodeを使うだけで、取り込み時の設定ミスがなくなるわけではないんだ。
ひよこ ひよこ
Unicodeは文字コードの名前?
ペンギン先生 ペンギン先生
文字の符号化や扱いを定める標準で、文字に対応するコードポイントを定義しているよ。たとえばAはU+0041、あはU+3042。UTF-8は、それを実際のバイト列へ符号化する方式だね。番号の体系と、保存するときの表現を分けよう。
ひよこ ひよこ
UTF-8では何バイトになるの?
ペンギン先生 ペンギン先生
Unicodeのスカラー値を1〜4バイトで表すよ。Aは1バイト、あは3バイト、😀は4バイト。ただし絵文字が全部4バイトとは限らない。見た目が一つでも、複数のコードポイントをつないでいるものがあるんだ。
ひよこ ひよこ
JavaScriptのlengthと見た目が違うのは?
ペンギン先生 ペンギン先生
JavaScriptのlengthはUTF-16のコード単位の数だからだよ。😀はサロゲートペアという2つのコード単位で表すのでlengthは2。コードポイント数と、見た目のまとまりである書記素クラスタの数も別なんだ。
ひよこ ひよこ
家族の絵文字はどう数える?
ペンギン先生 ペンギン先生
👨‍👩‍👧‍👦は、人物とZWJを合わせた7つのコードポイントから成るよ。対応する表示環境では一つの絵文字に見えるけれど、UTF-16では11コード単位。入力欄の上限を何の単位で決めるかによって、数え方を選ぶ必要があるね。
ひよこ ひよこ
同じ見た目なのに一致しないこともある?
ペンギン先生 ペンギン先生
é一つと、eの後ろに結合アクセントを付けたものが例だよ。正規化のNFCで同じ表現にできる場合がある。ただし見た目が似た別文字を全部同じにするものではない。NFKCは互換文字も変換するので、用途を考えて選ぼう。
ひよこ ひよこ
文字化けしたらUTF-8で保存し直せば直る?
ペンギン先生 ペンギン先生
誤った解釈のまま保存すると、元の情報を失うことがある。まず元ファイルを残して、作成元の文字コードと読み込み設定を確認しよう。すでに置換文字の�や?になって情報が失われていれば、元データがないと戻せない場合があるよ。
ひよこ ひよこ
□が出るのも文字化けなの?
ペンギン先生 ペンギン先生
フォントが対応していないだけの場合もあるんだ。文字コードの不一致、欠損、フォントや絵文字の表示対応を分けて調べよう。見た目だけでコードポイントが同じと決めつけず、必要なら番号とバイト列を確認すると切り分けやすいよ。

「文字数」は何を数えるかで変わる

文字列UTF-8のバイト数コードポイント数JavaScriptのlength
A111
あ311
😀412
👨‍👩‍👧‍👦25711

JavaScriptでは次のように確認できます。

const text = '👨‍👩‍👧‍👦';
console.log(new TextEncoder().encode(text).length); // 25: UTF-8バイト数
console.log([...text].length); // 7: コードポイント数
console.log(text.length); // 11: UTF-16コード単位数
const segmenter = new Intl.Segmenter('ja', { granularity: 'grapheme' });
console.log([...segmenter.segment(text)].length); // 1: 書記素クラスタ数

Intl.Segmenterの対応と、実行環境が使うUnicodeの版は確認してください。書記素クラスタを数えることと、フォントで実際にどう描画されるかも厳密には別です。

正規化は文字化け修復ではない

'e\u0301'.normalize('NFC') === '\u00e9' は true になります。これは等価な文字列の表現をそろえる処理で、間違った文字コードで読み込んだバイト列を復元する処理ではありません。原文を保存したうえで、照合・検索・表示など目的に応じて変換してください。

参考資料

確認日:2026年9月27日。製品の仕様・料金・対応環境は導入時にも確認してください。