JavaScriptで文字のUnicodeコードポイント(文字コード)を取得するには、charCodeAt() と codePointAt() の2つのメソッドがあります。どちらも「文字→数値」の変換を行いますが、サロゲートペア(絵文字や一部の漢字)を正しく扱えるかどうかに決定的な違いがあります。
この記事では、2つのメソッドの違いと使い分けから、逆変換関数(String.fromCharCode / fromCodePoint)、Unicodeエスケープシーケンス、文字種判定、TextEncoder/TextDecoderによるエンコーディング変換まで、コードポイント関連の知識を体系的に解説します。
この記事で学べること
- charCodeAt() と codePointAt() の違い・使い分け
- String.fromCharCode() / String.fromCodePoint() による逆変換
- Unicodeエスケープシーケンス(\uXXXX / \u{XXXXX})
- コードポイントを使った文字種判定(英数字・ひらがな・カタカナ・漢字)
- TextEncoder / TextDecoder によるUTF-8エンコーディング変換
- 主要な文字のコードポイント一覧表
charCodeAt():UTF-16コードユニットを取得する
charCodeAt() は文字列の指定位置にある文字のUTF-16コードユニット(0〜65535の整数)を返します。ASCII文字や日本語の大半はこれで正しく取得できます。
charCodeAt() の基本
// 基本構文: str.charCodeAt(index)
const str = 'Hello';
str.charCodeAt(0); // 72 (H)
str.charCodeAt(1); // 101 (e)
str.charCodeAt(4); // 111 (o)
str.charCodeAt(5); // NaN(範囲外)
// 日本語
'あ'.charCodeAt(0); // 12354
'漢'.charCodeAt(0); // 28450
// 引数省略時はデフォルトで 0(先頭文字)
'ABC'.charCodeAt(); // 65 (= charCodeAt(0))
// 16進数で確認
'A'.charCodeAt(0).toString(16); // "41"
'あ'.charCodeAt(0).toString(16); // "3042"
⚠ 注意:charCodeAt() が返すのはUTF-16コードユニット(0〜65535)です。絵文字や一部の漢字(?、?など)はサロゲートペアで2つのコードユニットに分割されるため、正しいコードポイントを取得できません。
codePointAt():Unicodeコードポイントを取得する(ES2015)
codePointAt() はES2015で追加されたメソッドで、Unicodeコードポイント(0〜1,114,111)を返します。サロゲートペア文字も正しく1つのコードポイントとして取得できます。
codePointAt() の基本
// 基本構文: str.codePointAt(index)
// BMP内の文字(U+0000〜U+FFFF)→ charCodeAtと同じ値
'A'.codePointAt(0); // 65 (charCodeAtと同じ)
'あ'.codePointAt(0); // 12354 (charCodeAtと同じ)
// サロゲートペア文字(U+10000以上)→ 正しいコードポイントを返す
'?'.codePointAt(0); // 134071 (正しい値)
'?'.charCodeAt(0); // 55362 (上位サロゲートの値)
'?'.codePointAt(0); // 127881
'?'.charCodeAt(0); // 55356 (上位サロゲートの値)
// 16進数で確認
'?'.codePointAt(0).toString(16); // "20bb7"
'?'.codePointAt(0).toString(16); // "1f389"
関連記事:サロゲートペアに対応したcodePointAt詳細解説
charCodeAt() vs codePointAt() 比較表
| 項目 |
charCodeAt() |
codePointAt() |
| 返す値 |
UTF-16コードユニット |
Unicodeコードポイント |
| 値の範囲 |
0〜65,535(16ビット) |
0〜1,114,111(21ビット) |
| サロゲートペア対応 |
❌ 上位/下位サロゲート値を個別に返す |
✅ 正しいコードポイントを返す |
| BMP内の文字 |
✅ 正しい値 |
✅ 正しい値(同じ結果) |
| 範囲外 |
NaN |
undefined |
| ECMAScript |
ES1(初期から存在) |
ES2015(ES6) |
| 推奨度 |
ASCII限定の処理向き |
✅ 基本はこちらを使う |
主要な文字での出力値の比較
| 文字 |
Unicode |
charCodeAt(0) |
codePointAt(0) |
一致 |
| A |
U+0041 |
65 |
65 |
✅ |
| あ |
U+3042 |
12354 |
12354 |
✅ |
| 漢 |
U+6F22 |
28450 |
28450 |
✅ |
| ? |
U+20BB7 |
55362 |
134071 |
❌ |
? (ほっけ) |
U+29E3D |
55399 |
171581 |
❌ |
| ? |
U+1F389 |
55356 |
127881 |
❌ |
? ポイント:U+0000〜U+FFFF(BMP:基本多言語面)の文字では両メソッドの結果は同じです。U+10000以上の文字(サロゲートペア)では codePointAt() のみが正しいコードポイントを返します。迷ったら codePointAt() を使えばOKです。
関連記事:charAt・ブラケット記法・at()の違い / 文字コードを取得・変換する方法
逆変換:コードポイントから文字を生成する
コードポイント(数値)から文字列を生成するには、String.fromCharCode() と String.fromCodePoint() を使います。
fromCharCode vs fromCodePoint
// String.fromCharCode() — UTF-16コードユニットから文字を生成
String.fromCharCode(65); // "A"
String.fromCharCode(12354); // "あ"
String.fromCharCode(72, 101, 108, 108, 111); // "Hello"(複数指定可)
// ❌ サロゲートペア文字は正しく変換できない
String.fromCharCode(134071); // "ꮷ"(意図しない文字)
// サロゲートペアを個別に指定する必要がある
String.fromCharCode(0xD842, 0xDFB7); // "?"
// ============================================
// String.fromCodePoint() — Unicodeコードポイントから文字を生成(ES2015)
String.fromCodePoint(65); // "A"
String.fromCodePoint(12354); // "あ"
// ✅ サロゲートペア文字もコードポイント1つで正しく変換
String.fromCodePoint(134071); // "?"
String.fromCodePoint(127881); // "?"
String.fromCodePoint(0x1F600); // "?"(16進数でもOK)
// 複数のコードポイントを一度に指定
String.fromCodePoint(12354, 12356, 12358); // "あいう"
| メソッド |
方向 |
サロゲートペア |
ES |
charCodeAt() |
文字 → 数値 |
❌ |
ES1 |
codePointAt() |
文字 → 数値 |
✅ |
ES2015 |
String.fromCharCode() |
数値 → 文字 |
❌ |
ES1 |
String.fromCodePoint() |
数値 → 文字 |
✅ |
ES2015 |
関連記事:Unicodeコードポイントから文字列を生成する方法 / ASCIIコードを文字列に変換する方法
Unicodeエスケープシーケンス
JavaScriptのソースコード内で、コードポイントを使って文字を表現する方法が2つあります。
エスケープシーケンス
// 1. \uXXXX 形式(ES1〜):4桁固定、BMP内のみ
'\u0041' // "A" (U+0041)
'\u3042' // "あ" (U+3042)
'\uD842\uDFB7' // "?" (サロゲートペアで記述)
// 2. \u{XXXXX} 形式(ES2015〜):可変桁数、全コードポイント対応
'\u{41}' // "A" (U+0041)
'\u{3042}' // "あ" (U+3042)
'\u{20BB7}' // "?" (1つで記述可能!)
'\u{1F389}' // "?" (1つで記述可能!)
// テンプレートリテラル内でも使える
console.log(`Hello \u{1F44B}`); // "Hello ?"
? 使い分け:\uXXXX はBMP内の文字専用です。サロゲートペアを含む全文字に対応するには \u{XXXXX} を使いましょう。現在のコードでは \u{} 形式を推奨します。
コードポイントで文字種を判定する
コードポイントの範囲を使って、文字がどの種類に属するかを判定できます。正規表現と組み合わせることで、柔軟な文字バリデーションが実現できます。
コードポイント範囲による判定
文字種判定ユーティリティ
// 半角英大文字: A-Z (U+0041〜U+005A)
const isUpperAlpha = (ch) => {
const cp = ch.codePointAt(0);
return cp >= 0x41 && cp <= 0x5A;
};
// 半角英小文字: a-z (U+0061〜U+007A)
const isLowerAlpha = (ch) => {
const cp = ch.codePointAt(0);
return cp >= 0x61 && cp <= 0x7A;
};
// 半角数字: 0-9 (U+0030〜U+0039)
const isDigit = (ch) => {
const cp = ch.codePointAt(0);
return cp >= 0x30 && cp <= 0x39;
};
// ひらがな: U+3040〜U+309F
const isHiragana = (ch) => {
const cp = ch.codePointAt(0);
return cp >= 0x3040 && cp <= 0x309F;
};
// カタカナ: U+30A0〜U+30FF
const isKatakana = (ch) => {
const cp = ch.codePointAt(0);
return cp >= 0x30A0 && cp <= 0x30FF;
};
// CJK統合漢字: U+4E00〜U+9FFF(基本的な漢字)
const isKanji = (ch) => {
const cp = ch.codePointAt(0);
return cp >= 0x4E00 && cp <= 0x9FFF;
};
// 使用例
console.log( isHiragana('あ') ); // true
console.log( isKatakana('ア') ); // true
console.log( isKanji('漢') ); // true
console.log( isDigit('5') ); // true
正規表現のUnicodeプロパティエスケープ(ES2018)
コードポイント範囲のハードコーディングの代わりに、\p{} を使ったUnicodeプロパティエスケープが利用できます。
Unicodeプロパティエスケープ
// \p{Script=...} で文字体系を指定(u フラグ必須)
/\p{Script=Hiragana}/u.test('あ'); // true
/\p{Script=Katakana}/u.test('ア'); // true
/\p{Script=Han}/u.test('漢'); // true
/\p{Script=Latin}/u.test('A'); // true
// カテゴリベースの判定
/\p{Letter}/u.test('あ'); // true(全言語の「文字」)
/\p{Number}/u.test('5'); // true(全言語の「数字」)
/\p{Emoji}/u.test('?'); // true
// 実用例:ひらがなのみで構成されているかチェック
const isAllHiragana = (str) => /^\p{Script=Hiragana}+$/u.test(str);
isAllHiragana('こんにちは'); // true
isAllHiragana('こんにちはWorld'); // false
関連記事:for-ofループでサロゲートペア文字列を抽出する方法
主要な文字のコードポイント一覧表
| カテゴリ |
文字 |
範囲(16進数) |
範囲(10進数) |
| 半角数字 |
0〜9 |
U+0030〜U+0039 |
48〜57 |
| 半角英大文字 |
A〜Z |
U+0041〜U+005A |
65〜90 |
| 半角英小文字 |
a〜z |
U+0061〜U+007A |
97〜122 |
| ひらがな |
ぁ〜ゟ |
U+3040〜U+309F |
12352〜12447 |
| カタカナ |
゠〜ヿ |
U+30A0〜U+30FF |
12448〜12543 |
| 全角英数字 |
A〜Z等 |
U+FF01〜U+FF5E |
65281〜65374 |
| CJK統合漢字 |
一〜鿿 |
U+4E00〜U+9FFF |
19968〜40959 |
| CJK拡張A |
㐀〜䶵 |
U+3400〜U+4DBF |
13312〜19903 |
| CJK拡張B(サロゲートペア) |
?〜? |
U+20000〜U+2A6DF |
131072〜173791 |
関連記事:HTML特殊文字と記号の文字コード表 / 文字列からASCIIコードに変換する方法
TextEncoder / TextDecoder でUTF-8バイト列に変換する
コードポイントとは別に、文字列をUTF-8のバイト配列に変換したい場面では TextEncoder と TextDecoder を使います。ファイル処理やネットワーク通信で必要になります。
TextEncoder / TextDecoder
const encoder = new TextEncoder(); // 常にUTF-8
const decoder = new TextDecoder('utf-8');
// 文字列 → UTF-8バイト配列
encoder.encode('A'); // Uint8Array [65] (1バイト)
encoder.encode('あ'); // Uint8Array [227, 129, 130] (3バイト)
encoder.encode('?'); // Uint8Array [240, 159, 142, 137] (4バイト)
// UTF-8バイト配列 → 文字列
decoder.decode(new Uint8Array([65])); // "A"
decoder.decode(new Uint8Array([227, 129, 130])); // "あ"
// 実用例:文字列のバイト数を取得
const getByteLength = (str) => new TextEncoder().encode(str).byteLength;
getByteLength('Hello'); // 5 (ASCII: 1バイト × 5)
getByteLength('あいう'); // 9 (ひらがな: 3バイト × 3)
getByteLength('?'); // 4 (絵文字: 4バイト)
| 文字 |
コードポイント |
UTF-8バイト数 |
UTF-16コードユニット数 |
.length |
| A |
U+0041 |
1 |
1 |
1 |
| あ |
U+3042 |
3 |
1 |
1 |
| ? |
U+20BB7 |
4 |
2 |
2 |
| ? |
U+1F389 |
4 |
2 |
2 |
関連記事:文字列のバイト数を取得する方法 / 文字列の長さを取得する方法
文字列の全コードポイントをイテレートする
文字列内の全文字のコードポイントを正しく取得するには、サロゲートペアを考慮した方法を使う必要があります。
3つのイテレーション方法
const str = 'あ??';
// ❌ for ループ + charCodeAt — サロゲートペアを正しく扱えない
for (let i = 0; i < str.length; i++) {
console.log(str.charCodeAt(i));
}
// → 12354, 55362, 57271, 55356, 57225
// (5つの値 = コードユニット数。サロゲートペアが分割される)
// ✅ for...of — コードポイント単位でイテレート
for (const ch of str) {
console.log(ch, ch.codePointAt(0));
}
// → "あ" 12354
// → "?" 134071
// → "?" 127881
// ✅ Array.from() + map — コードポイント配列を生成
const codePoints = Array.from(str).map(ch => ch.codePointAt(0));
// → [12354, 134071, 127881]
// ✅ スプレッド構文 — 同じ結果
const codePoints2 = [...str].map(ch => ch.codePointAt(0));
// → [12354, 134071, 127881]
⚠ Unicode正規化に注意:見た目が同じ文字でもコードポイントが異なる場合があります(例:「が」= U+304C と「か」+「゛」= U+304B U+3099)。コードポイントを比較する前に str.normalize('NFC') で正規化することを推奨します。
関連記事:Unicode正規化(NFC/NFD)の詳細解説
実務で使えるユーティリティ関数
コードポイントと16進数表記の相互変換
16進数表記との変換
// 文字 → U+XXXX 表記
const toUnicode = (ch) => {
const hex = ch.codePointAt(0).toString(16).toUpperCase();
return `U+${hex.padStart(4, '0')}`;
};
toUnicode('A'); // "U+0041"
toUnicode('あ'); // "U+3042"
toUnicode('?'); // "U+1F389"
// U+XXXX 表記 → 文字
const fromUnicode = (notation) => {
const hex = notation.replace(/^U\+/i, '');
return String.fromCodePoint(parseInt(hex, 16));
};
fromUnicode('U+0041'); // "A"
fromUnicode('U+1F389'); // "?"
文字列のUnicodeダンプ
Unicodeダンプ関数
/**
* 文字列の各文字のUnicode情報を出力
*/
const unicodeDump = (str) => {
return [...str].map((ch, i) => {
const cp = ch.codePointAt(0);
return {
index: i,
char: ch,
codePoint: cp,
hex: `U+${cp.toString(16).toUpperCase().padStart(4, '0')}`,
isSurrogatePair: cp > 0xFFFF,
};
});
};
console.table( unicodeDump('あ?A?') );
// ┌───────┬──────┬───────┬────────────┬──────┬─────────────────┐
// │ index │ char │ cp │ hex │ SP │ │
// ├───────┼──────┼───────┼────────────┼──────┤ │
// │ 0 │ あ │ 12354 │ U+3042 │ false│ │
// │ 1 │ ? │134071 │ U+20BB7 │ true │ │
// │ 2 │ A │ 65 │ U+0041 │ false│ │
// │ 3 │ ? │127881 │ U+1F389 │ true │ │
// └───────┴──────┴───────┴────────────┴──────┘ │
大文字⇔小文字の変換(コードポイント演算)
コードポイント演算でケース変換
// A(65) と a(97) の差は 32
// つまり大文字 + 32 = 小文字、小文字 - 32 = 大文字
const toLower = (ch) => {
const cp = ch.codePointAt(0);
if (cp >= 0x41 && cp <= 0x5A) {
return String.fromCodePoint(cp + 32);
}
return ch;
};
toLower('A'); // "a"
toLower('Z'); // "z"
toLower('あ'); // "あ"(非英字はそのまま)
全角英数字⇔半角英数字の変換
全角英数字と半角英数字のコードポイントの差は一定(0xFEE0 = 65248)です。この差を利用して簡単に相互変換できます。
全角⇔半角変換
// 全角英数字 → 半角英数字
// A(U+FF21) - A(U+0041) = 0xFEE0 (65248)
const toHalfWidth = (str) => {
return [...str].map(ch => {
const cp = ch.codePointAt(0);
// U+FF01〜U+FF5E → U+0021〜U+007E
if (cp >= 0xFF01 && cp <= 0xFF5E) {
return String.fromCodePoint(cp - 0xFEE0);
}
return ch;
}).join('');
};
toHalfWidth('Hello123'); // "Hello123"
toHalfWidth('Aあいう'); // "Aあいう"(ひらがなはそのまま)
// 半角英数字 → 全角英数字
const toFullWidth = (str) => {
return [...str].map(ch => {
const cp = ch.codePointAt(0);
if (cp >= 0x21 && cp <= 0x7E) {
return String.fromCodePoint(cp + 0xFEE0);
}
return ch;
}).join('');
};
toFullWidth('Hello123'); // "Hello123"
関連記事:アルファベットの全角文字を半角に変換する方法
シーザー暗号(コードポイント演算の応用)
シーザー暗号(ROT13)
/**
* ROT13: アルファベットを13文字ずらす(暗号化と復号が同じ処理)
*/
const rot13 = (str) => {
return [...str].map(ch => {
const cp = ch.codePointAt(0);
if (cp >= 65 && cp <= 90) return String.fromCodePoint((cp - 65 + 13) % 26 + 65); // A-Z
if (cp >= 97 && cp <= 122) return String.fromCodePoint((cp - 97 + 13) % 26 + 97); // a-z
return ch;
}).join('');
};
rot13('Hello World'); // "Uryyb Jbeyq"
rot13('Uryyb Jbeyq'); // "Hello World"(同じ関数で復号)
isWellFormed() / toWellFormed():孤立サロゲートの検出(ES2024)
ES2024で追加された isWellFormed() と toWellFormed() は、文字列内に孤立サロゲート(ペアになっていないサロゲートコードユニット)が含まれていないかを検出・修復するメソッドです。
isWellFormed / toWellFormed (ES2024)
// 正常な文字列
'Hello'.isWellFormed(); // true
'?'.isWellFormed(); // true(正しいサロゲートペア)
// 孤立サロゲートを含む文字列
'\uD800'.isWellFormed(); // false(上位サロゲートのみ)
'ab\uD800cd'.isWellFormed(); // false
// toWellFormed() で修復(孤立サロゲート → U+FFFD に置換)
'ab\uD800cd'.toWellFormed(); // "ab�cd" (U+FFFD = 置換文字)
// 実用例:encodeURIでエラーを防ぐ
const safeEncode = (str) => {
return encodeURIComponent(str.toWellFormed());
};
// encodeURIComponent('\uD800') はエラーになるが、
// toWellFormed() で事前に修復すれば安全
パフォーマンスの注意点
charCodeAt() vs codePointAt() の速度差
BMP内の文字(通常の英数字・日本語)のみを扱う場合、charCodeAt() は codePointAt() よりわずかに高速です(サロゲートペア判定が不要なため)。ただし差はごくわずかで、数百万文字規模の処理でない限り体感差はありません。
- ASCII限定の処理(パスワード文字チェック等)→
charCodeAt() でOK
- 多言語対応が必要な処理 →
codePointAt() を使う
- 大量文字列のイテレーション →
for...of より for ループ + codePointAt() + インデックス手動管理が最速だが、可読性とのトレードオフ
トラブルシューティング
| 症状 |
原因 |
対処法 |
| 絵文字のコードポイントが変な値になる |
charCodeAt() を使っている |
codePointAt() に変更する |
String.fromCharCode(134071) で変な文字が出る |
65535を超える値は fromCharCode で扱えない |
String.fromCodePoint(134071) を使う |
.length が見た目の文字数と一致しない |
.length はUTF-16コードユニット数を返す |
Array.from(str).length または [...str].length でコードポイント数を取得 |
| forループで文字列を1文字ずつ処理すると文字化けする |
str[i] はコードユニット単位でアクセスする |
for...of ループまたは [...str] に変更 |
encodeURIComponent() でエラーが出る |
文字列に孤立サロゲートが含まれている |
str.toWellFormed() で事前に修復する(ES2024) |
| 正規表現で絵文字がマッチしない |
正規表現に u フラグが付いていない |
/pattern/u のように u フラグを追加 |
まとめ
| やりたいこと |
推奨メソッド |
| 文字 → コードポイント |
str.codePointAt(0) |
| コードポイント → 文字 |
String.fromCodePoint(cp) |
| ソースコード内で文字を表記 |
\u{XXXXX} 形式 |
| 全コードポイントをイテレート |
for...of または [...str] |
| 文字種の判定 |
\p{Script=...}(正規表現 + uフラグ) |
| UTF-8バイト列との変換 |
TextEncoder / TextDecoder |
| 孤立サロゲートの検出・修復 |
isWellFormed() / toWellFormed()(ES2024) |
基本は charCodeAt() ではなく codePointAt() を使うことを覚えておけば、サロゲートペア問題でハマることはありません。逆変換も String.fromCodePoint() を使えば安全です。
関連記事:サロゲートペアに対応したcodePointAt詳細解説 / Unicodeコードポイントから文字列を生成する方法 / 文字列からASCIIコードに変換する方法 / 文字列のバイト数を取得する方法