API のデバッグ
%E4%B8%AD%E6%96%87 を見て UTF-8 の漢字 2 文字だとすぐ分かる、JWT の . で区切られた 3 つの Base64 をどう JSON に戻すか分かる、リクエストパラメーターの + と %20 がなぜバックエンドで別物として扱われるかが分かります。
Guide
文字コード講座は 14 レッスンの対話形式チュートリアルで、開発中に何度も現れるのにほとんど誰もはっきり説明してくれない疑問に答えます。漢字 1 文字は結局何バイトなのか。なぜ "😀".length は 2 なのか。Base64 の末尾の = は何のためか。文字化けはファイルが壊れたのか、それとも救えるのか。各レッスンには自由に書き換えられるテストテキストと、いくつかのビューが用意されます。各文字のビット、バイト、Unicode コードポイント、UTF-8 のバイト単位の分解、Base64 の 6 ビット分割、URL パーセントエンコード、そして「別の符号化で読むと何が見えるか」です。目標はある数を読み取る、ある 16 進数文字列を書く、条件を満たす文字を入力することで、判定がその場で付きます。
更新日 2026-09-095 sources13 min read
文字コード講座は 14 レッスンの対話形式チュートリアルで、開発中に何度も現れるのにほとんど誰もはっきり説明してくれない疑問に答えます。漢字 1 文字は結局何バイトなのか。なぜ "😀".length は 2 なのか。Base64 の末尾の = は何のためか。文字化けはファイルが壊れたのか、それとも救えるのか。各レッスンには自由に書き換えられるテストテキストと、いくつかのビューが用意されます。各文字のビット、バイト、Unicode コードポイント、UTF-8 のバイト単位の分解、Base64 の 6 ビット分割、URL パーセントエンコード、そして「別の符号化で読むと何が見えるか」です。目標はある数を読み取る、ある 16 進数文字列を書く、条件を満たす文字を入力することで、判定がその場で付きます。
講座の順序は文字コードの歴史そのものです。ビットとバイト → ASCII の 128 の取り決め → Unicode が各文字に番号を振る → UTF-8 が番号を 1 から 4 バイトへ変える → UTF-16 と JavaScript の文字列長 → Base64 がバイトをプレーンテキストへ運ぶ → URL エンコード → 最後の 2 レッスンは文字化けに特化し、それがどう生じるのか、浣犲ソ からどうやって 你好 を逆算するのかを扱います。
学習時間は約 40 分。フロントエンドとバックエンドの開発者、データ処理に携わる人、そして文字化け、Base64、絵文字の長さに悩まされたことのあるすべての人に向いています。必要なのは 2 進数が数えられることだけです。
%XX になったかを示します。「別の符号化で読む」は同じバイト列を GBK、Latin-1、UTF-16 として読んだ姿を表示します。?lesson=n で特定のレッスンへ直接移動できます。このページの上部が講座本体です。テストテキストを変え、ビューを読み、答えを入力できます。
第 6 レッスン「UTF-8 の 2 バイト」は café をサンプルに、é の 2 バイトを書かせます。
第 14 レッスン「文字化けの逆算」は GBK として読まれた文字化け 浣犲ソ を示し、テストテキストから元の文を探させます。
文字 コードポイント 2 進のコードポイント UTF-8 テンプレート バイト
c U+0063 1100011 0xxxxxxx 63
a U+0061 1100001 0xxxxxxx 61
f U+0066 1100110 0xxxxxxx 66
é U+00E9 000 1110 1001 110xxxxx 10xxxxxx C3 A9
→ 11 ビットのペイロード 00011 101001 をテンプレートへ → 11000011 10101001
答え C3 A9 ✓テストテキスト 中文 UTF-8 バイト E4 B8 AD E6 96 87 GBK で読む → 涓枃
テストテキスト 你好 UTF-8 バイト E4 BD A0 E5 A5 BD GBK で読む → 浣犲ソ ← 問題と一致
答え 你好 ✓計算機にあるのは 0 と 1 だけです。1 ビット(bit)は 1 つのスイッチで、8 ビットで 1 バイト(byte)になり、 通りの値を表せます。位の重みを足し合わせると 2 進数が 10 進数になります。01000001 = 64 + 1 = 65。すべての文字は最終的にバイト列であり、「文字コード」とはどの数がどの文字を表すかを定めた表です。表が違えば符号化も違います。
1963 年の ASCII は 7 ビットで 128 文字を定義しました。0–31 は改行、タブ、ベルなどの制御文字、48–57 は数字の 0–9、65–90 は大文字、97–122 は小文字です。並び方はでたらめではありません。大文字と小文字は 32 だけ違い(2 進数の 6 ビット目)、c | 0x20 で大文字を小文字にできます。数字の文字は 48 を引くと数値になります。16 進数はバイトを読むときの慣用表記で、2 桁がちょうど 1 バイトです。0x48 0x69 = Hi。ASCII が使ったのは 7 ビットだけで、8 ビット目は空いていました。後に各国がこの 1 ビットをそれぞれ埋め(Latin-1、GBK、Shift_JIS……)、互換性がなくなり、これが文字化けの歴史的な根源になりました。
Unicode は世界中の文字に統一的な番号を振ります。これをコードポイント(code point)と呼び、U+ に 16 進数を付けて書きます。A は U+0041、中 は U+4E2D、😀 は U+1F600 です。範囲は U+0000 から U+10FFFF までで 17 の「面」に分かれ、よく使う文字はほぼ第 0 基本面(U+0000–U+FFFF)に、絵文字と珍しい漢字は追加面にあります。重要なのは、コードポイントは番号にすぎないという点です。それをバイトにする方法は UTF-8、UTF-16、UTF-32 といった「符号化形式」が決めます。「Unicode」は符号化の名前ではなく、utf-8 のほうが符号化です。
UTF-8(RFC 3629)は 1 つのコードポイントを 1–4 バイトで表し、規則はすべて先頭バイトのプレフィックスに書かれています。
| コードポイントの範囲 | バイト数 | テンプレート | ペイロードビット |
|---|---|---|---|
| U+0000–U+007F | 1 | 0xxxxxxx |
7 |
| U+0080–U+07FF | 2 | 110xxxxx 10xxxxxx |
11 |
| U+0800–U+FFFF | 3 | 1110xxxx 10xxxxxx 10xxxxxx |
16 |
| U+10000–U+10FFFF | 4 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
21 |
コードポイントの 2 進数をペイロードのビット数まで左に 0 で埋め、x に順に当てはめるとバイトが得られます。é = U+00E9 = 11101001 を 11 ビットに埋めると 00011 101001 になり、2 バイトのテンプレートに入れると 11000011 10101001 = C3 A9 です。
この設計には 3 つの利点があります。ASCII のテキストは UTF-8 で符号化しても1 バイトも変わらない(完全な下位互換)。どの後続バイトも 10 で始まるので、バイト列の途中のどこからでも次の文字の開始位置を見つけられます(自己同期。1 バイト欠けても全体がずれません)。長さの違う符号化が重ならないため、00 のようなゼロバイトは常に U+0000 だけを表します(C 文字列として安全)。代償は漢字が 1 文字 3 バイトになり GBK の 2 バイトより 50% 増えることですが、英字、約物、コードは影響を受けないため、UTF-8 は 98% を超える Web ページの符号化になりました。
第 7、8 レッスンでは 3 バイト文字と 4 バイト文字を自分で入力します。後者は今もよくある落とし穴を明らかにします。MySQL の utf8 文字セットは実際には 3 バイトまでしか対応しておらず、絵文字を保存するとエラーか切り捨てになり、utf8mb4 を使う必要があります。
.lengthJavaScript、Java、C#、Windows API の文字列は内部的に UTF-16 を使い、各コード単位(code unit)は 16 ビットです。基本面の文字はコード単位 1 つ、追加面のコードポイントは 2 つのコード単位に分かれ、これをサロゲートペア(surrogate pair)と呼びます(範囲は D800–DBFF と DC00–DFFF)。そのため "😀".length === 2 となり、"😀".split("") は無意味な半分ずつに切り、str[0] は上位サロゲートを返します。実際の文字数で数えるなら [...str].length(コードポイント単位の反復)、利用者が感じる「1 文字」単位(組み合わせ絵文字や濁点などを含む)で数えるなら Intl.Segmenter を使います。
Base64(RFC 4648)は暗号化ではなく、任意のバイトを 64 個の印字可能な ASCII 文字(A–Z a–z 0–9 + /)へ変換する運搬方法です。テキストしか受け取らない場所——メールの添付、JSON のフィールド、data: URL、JWT——にバイナリを詰め込むためです。やり方は、3 バイト = 24 ビットを 6 ビットずつ 4 つに切り()、それぞれを表で引いて 1 文字にします。Man = 4D 61 6E = 01001101 01100001 01101110 → 010011 010110 000101 101110 → T W F u。このためサイズは元の 4/3 に膨らみます。
バイト数が 3 の倍数でない場合、最後の組が足りません。1 バイト足りなければ = を 1 つ、2 バイト足りなければ = を 2 つ補い、出力長が常に 4 の倍数になるようにします。A は 1 バイト 8 ビットを 0 で 12 ビットまで埋め、010000 01(0000) に切って QQ となり、== が付きます。URL-safe 版は + / を - _ に置き換え(URL 内でエスケープされないように)、= を省略することが多く、復号時は長さに応じて補います。
URL の中でそのまま置ける ASCII 文字はごく一部です(RFC 3986 §2)。空白、&、=、?、/ のような構文上の意味を持つ予約文字、およびすべての非 ASCII 文字は、% + バイトの 16 進数で書く必要があります。つまり URL エンコードはまず UTF-8、次にバイトごとに % を付ける処理です。中 → E4 B8 AD → %E4%B8%AD となり、漢字 1 文字が 9 文字に変わります。JavaScript では encodeURIComponent がもっとも徹底して符号化し(/ ? & = も符号化します)、パラメーター値に入れるのに向いています。encodeURI は構造文字を残すのでアドレス全体に向きます。フォーム送信の application/x-www-form-urlencoded はさらに空白を + と書きますが、これは HTML フォームの歴史的な取り決めで、RFC 3986 の一部ではありません。
文字化けはデータの破損ではなく、書くときに符号化 A、読むときに符号化 B を使った結果です。UTF-8 の 中 は E4 B8 AD です。
ä¸ という 3 つの西ヨーロッパの文字になります。アクセント記号付きの文字列は「UTF-8 を西欧の符号化として読んだ」印です。E4 B8 が漢字 涓 になり、残りの AD は半分だけになります。「奇妙な漢字 + 半分の文字」は「UTF-8 を GBK として読んだ」印で、浣犲ソ や 涓枃 はこの類です。�(U+FFFD)が大量に現れます。元のバイトさえ失われていなければ、正しい符号化で読み直せば文字は元どおりになります。したがって文字化けを直す第一原則は、元のファイルを残し、変換を繰り返さないことです。変換のたびに不正なバイトが ? や � に置き換わり、それこそが本当の情報の消失になります。第 14 レッスンでは逆向きの作業をします。文字化けした 浣犲ソ を GBK としてバイト E4 BD A0 E5 A5 BD に戻し、それを UTF-8 として読んで 你好 を取り出します。現代的なやり方は、HTTP ヘッダー、HTML の <meta charset>、データベース接続、ファイル保存ダイアログでUTF-8 を統一して宣言し、取り違えが起きる隙を作らないことです。
UTF-16 にはビッグエンディアンとリトルエンディアンの 2 つのバイト順があり、ファイルの先頭にバイト順マーク BOM(FE FF または FF FE)を置くことがよくあります。UTF-8 にバイト順の問題はありませんが、Windows のメモ帳がかつて UTF-8 の BOM EF BB BF を既定で書き込んだため、シェルスクリプトの 1 行目がエラーになったり、CSV の最初の列名の前に見えない文字が入ったりしました。講座の UTF-8 分解ビューで先頭にこの 3 バイトがあれば、出所が分かります。
TextDecoder に依存します。GB18030、Big5、Shift_JIS の内部構造は扱いません。length は文字数」: JavaScript では UTF-16 のコード単位数で、絵文字や一部の珍しい字は 2 と数えます。データベースの VARCHAR(n) の n も、製品によってバイト数だったり文字数だったりします。encodeURIComponent が必要です。そうしないと &、#、空白が構造を壊します。%E4%B8%AD%E6%96%87 を見て UTF-8 の漢字 2 文字だとすぐ分かる、JWT の . で区切られた 3 つの Base64 をどう JSON に戻すか分かる、リクエストパラメーターの + と %20 がなぜバックエンドで別物として扱われるかが分かります。
第 13–14 レッスンの方法で「UTF-8 を GBK として読んだ」のかその逆かを判断し、エディターの「符号化を指定して開き直す」を使います(「符号化を変換」ではなく)。元のバイトを残すのが第一原則です。
絵文字を保存するなら utf8mb4。UTF-8 の保存量を見積もるときは漢字を 3 バイトで計算。VARCHAR の長さの単位がバイトか文字かを必ず確認します。
ニックネームを切り詰めるときは str.slice(0, n) ではなく [...str].slice(0, n) を使い、絵文字を半分にしないようにします。文字数を数えるときはコード単位、コードポイント、書記素クラスタのどれを使うかを決めます。
肌色修飾やゼロ幅接合子(ZWJ)で組み合わされた絵文字(👍🏽、👨👩👧 など)は複数のコードポイントでできており、コードポイントビューには 1 つずつ並びます。第 8 レッスンはちょうど 1 つの 4 バイト文字を求めるので、修飾のない基本の絵文字(😀 など)を使ってください。
どちらも不要です。c3a9、C3 A9、c3 a9 はすべて正解です。
講座はブラウザー内蔵の TextDecoder('gbk')(実際は GB18030 の上位集合)を使い、不正な並びには � を出力します。ツールによっては ? を出したり読み飛ばしたりしますが、字形が違うだけで意味は同じ——どれも「ここは解釈できない」という印です。
元のバイトが完全で、読み方だけが間違っていたなら直せます。エディターで正しい符号化として開き直すだけです。すでに「変換」が行われて大量の ? が現れている場合、その部分の情報は失われており、講座でも救えません。
このブラウザーの localStorage で、アップロードされません。「このレッスンを共有」のリンクはレッスン番号だけを運び、進捗は含みません。
講座はすべてブラウザー内で動作します。テストテキストの符号化・復号、Base64、URL エンコードはページ内の TextEncoder / TextDecoder と純粋な関数で行われ、どのサーバーにも送信されません。クリア状況は端末の localStorage に保存されます。
TextDecoder が対応する符号化とその復号アルゴリズム。講座の文字化けデモが使う GBK / windows-1252 デコーダーの規範: https://encoding.spec.whatwg.org/(参照日:2026-09-09)更新日 2026-09-09
全 14 レベルでビット、バイト、ASCII、Unicode コードポイント、UTF-8 の可変長エンコード、Base64 の区切り、URL エンコード、文字化けの原因を理解
このツールの翻訳は未完了です。一部は原文または代替言語で表示されます。
目標サンプル テキストは文字 A で、その 1 バイトは 01000001 と表示されます。この2進数を10進数に変換して入力します。
| 文字 | コードポイント | UTF-8 バイト | UTF-16 コード単位 | バイト数 |
|---|---|---|---|---|
| A | U+0041 | 41 | 0041 | 1 |
コンピュータは 0 と 1 だけを保存します。ビットは 1 つのスイッチです。 8 ビットで 1 バイトが構成され、0 ~ 255、256 の値を保持できます。バイナリ合計の値は次のとおりです: 01000001 = 64 + 1 = 65。すべてのテキスト、画像、サウンドは最終的にはバイト シーケンスであり、「エンコーディング」によってどの数値がどの文字を意味するかが定義されます。