JavaScriptでの正確な文字数カウント(サロゲートペア・絵文字・Intl.Segmenter)

JavaScript 文字コード Unicode 開発効率

JavaScript で文字列の長さを取得する際、絵文字(サロゲートペア・結合文字)によって発生する文字数カウントの不一致の原因と、正しい実装コードです。

1. オンライン文字数・原稿用紙換算・バイト数 カウントツール

入力されたテキストの見た目の文字数、UTF-8 バイト数、単語数、行数をブラウザ内で即座に測定します。

🔢 この場で文字数をカウントする

文字数

0

行数

0

単語数(スペース区切り)

0

バイト数(UTF-8)

0


2. なぜ str.length では絵文字の数がズレるのか?

JavaScript の文字列は内部的に UTF-16 で表現されており、string.length プロパティは「見た目の文字数」ではなく UTF-16 コードユニット(16ビット)の数 を返します。

文字str.length の値理由
"A"1基本多言語面(BMP)に含まれる通常の1バイト文字
"あ"11コードユニットで収まる文字
"🍎" (りんご)216ビットに収まらないためサロゲートペア(2コードユニット)で表現される
"👨‍👩‍👧‍👦" (家族絵文字)114つの絵文字 + ZERO WIDTH JOINER (\u200D) の合成(結合文字)

3. 正確な文字数を取得する 2 つの実装パターン

① モダンで最も確実:Intl.Segmenter (ES2022 / 推奨)

ブラウザおよび Node.js 16+ で利用可能な グラフェムクラスタ(人間が1文字として認識する単位) を分割してカウントする標準APIです。

function getGraphemeCount(text) {
  // 日本語・絵文字に対応したグラフェムセグメンターを生成
  const segmenter = new Intl.Segmenter('ja', { granularity: 'grapheme' });
  const segments = segmenter.segment(text);
  return Array.from(segments).length;
}

console.log(getGraphemeCount("🍎abc"));       // 4 (正解)
console.log(getGraphemeCount("👨‍👩‍👧‍👦"));        // 1 (正解)

② 簡易版:スプレッド構文 ([...str]) または Array.from()

サロゲートペア単体の文字(🍎 や 𩸽 等)であれば、Code Point 単位で分解する配列化で対応可能です(※結合文字 👨‍👩‍👧‍👦 は除きます)。

const text = "🍎abc";

// ❌ 単純な .length
console.log(text.length); // 5 (絵文字が2文字扱い)

// ✅ スプレッド構文で Code Point 分割
console.log([...text].length); // 4
console.log(Array.from(text).length); // 4

4. バイト数(UTF-8)の計算方法

RDBの VARCHAR(N) 制限やファイル保存で必要な「UTF-8バイト長」の計測コードです。

// ブラウザ標準の TextEncoder を使用
function getUtf8ByteLength(text) {
  return new TextEncoder().encode(text).length;
}

console.log(getUtf8ByteLength("あ")); // 3 (UTF-8では日本語は3バイト)