【JavaScript】文字のUnicodeコードポイントを取得する方法|charCodeAt・codePointAt・変換関数の使い分け完全ガイド

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のバイト配列に変換したい場面では TextEncoderTextDecoder を使います。ファイル処理やネットワーク通信で必要になります。

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コードに変換する方法文字列のバイト数を取得する方法