JavaScriptを利用してWebアプリケーションを開発する際、文字列の長さを取得する処理は非常に頻繁に登場します。
ユーザー登録フォームでの入力制限や、SNSのような投稿機能における文字数カウントなど、正確な文字列操作が求められる場面は多岐にわたります。
一見すると単純な処理に思えますが、JavaScriptの文字列の扱いには特有の仕様があり、特に日本語や絵文字を扱う場合には注意が必要です。
この記事では、基本的なlengthプロパティの使い方から、特殊な文字を正確にカウントするための最新の手法まで詳しくご紹介します。
適切な方法を選択することで、バグの少ない堅牢なプログラムを記述できるようになります。
lengthプロパティの基本的な使い方
JavaScriptで文字列の長さを取得する最も標準的な方法は、Stringオブジェクトのlengthプロパティを使用することです。
このプロパティは、文字列に含まれる「コードユニット」の数を返します。
まずは、半角英数字や基本的な記号における動作を確認してみましょう。
// 半角英数字の長さを取得する
const simpleText = "Hello JS";
console.log(simpleText.length);
8
上記の例では、スペースを含めた8文字が正しくカウントされていることがわかります。
日本語の全角文字についても、基本的には1文字としてカウントされます。
// 日本語(全角文字)の長さを取得する
const japaneseText = "プログラミング";
console.log(japaneseText.length);
7
このように、一般的なテキストであればlengthプロパティだけで十分に要件を満たすことができます。
しかし、特定の条件下では期待通りの数値が返ってこないことがあるため注意が必要です。
サロゲートペア文字に関する注意点
JavaScriptの文字列は内部的にUTF-16というエンコーディング方式を採用しています。
UTF-16では、ほとんどの文字を16ビット(2バイト)で表現しますが、一部の文字は32ビット(4バイト)で表現されます。
この4バイトで表現される文字のことを「サロゲートペア」と呼びます。
lengthプロパティは、あくまで16ビット単位のコードユニット数を数えるため、サロゲートペア文字は「2」とカウントされてしまいます。
絵文字や特殊な漢字のカウント
サロゲートペアの代表例は、絵文字や一部の古い漢字(難読漢字など)です。
以下のコードで、実際にどのようにカウントされるかを見てみましょう。
// 絵文字を含む文字列
const emojiText = "🍎";
console.log(emojiText.length);
// 特殊な漢字(𠮷:つちよし)
const kanjiText = "𠮷";
console.log(kanjiText.length);
2
2
見た目上はどちらも1文字ですが、出力結果は「2」となります。
これは、ユーザーが期待する「文字数」とは乖離があるため、バリデーションなどで不具合の原因となることがよくあります。
結合文字と複雑な絵文字
さらに複雑な例として、複数のコードポイントが組み合わさって1つの絵文字に見える「結合文字」があります。
例えば、国旗の絵文字や、肌の色を指定した絵文字、家族の絵文字などが該当します。
// 家族の絵文字
const familyEmoji = "👨👩👧";
console.log(familyEmoji.length);
8
このように、見た目は1つの絵文字であっても、内部的には複数のキャラクターが連結されているため、lengthの値は非常に大きくなります。
このような複雑な文字列を正確に1文字として数えるには、別の手法を検討しなければなりません。
正確な文字数を取得するための最新手法
サロゲートペアや結合文字の問題を解決し、人間が見たままの文字数を取得する方法はいくつか存在します。
用途やターゲットブラウザの環境に合わせて、最適な手法を選択してください。
スプレッド構文やArray.fromを使用する
ES6(ECMAScript 2015)以降、JavaScriptのイテレータはサロゲートペアを適切に処理できるようになりました。
スプレッド構文(…)やArray.from()を使って文字列を配列に展開することで、サロゲートペアを1文字として数えることが可能です。
// スプレッド構文を利用したカウント
const text = "𠮷野家🍎";
const count = [...text].length;
console.log(count);
4
この方法であれば、「𠮷」や「🍎」を正しく1文字として扱うことができます。
ただし、先ほど紹介した「家族の絵文字」のような結合文字については、依然として正確にカウントできない点に注意してください。
Intl.Segmenterを使用する(推奨)
現在のJavaScriptにおいて、最も正確に文字数をカウントできる手法は Intl.Segmenter API です。
このAPIは、言語に応じたテキストの分割(セグメンテーション)を行うための標準機能です。
granularity: 'grapheme'(書記素単位)を指定することで、人間が認識する「1文字」の単位で正確に分割できます。
// Intl.Segmenterを利用した正確なカウント
const complexText = "👨👩👧と𠮷野家🍎";
const segmenter = new Intl.Segmenter("ja", { granularity: "grapheme" });
const segments = segmenter.segment(complexText);
// イテレータから配列に変換して長さを取得
const realCount = [...segments].length;
console.log(realCount);
6
この方法を使えば、結合文字を含む絵文字もサロゲートペアも、すべて期待通りにカウントされます。
モダンブラウザでは広くサポートされているため、2026年現在の開発においては第一選択となる手法です。
文字列カウント手法の比較まとめ
それぞれの方法の特徴を以下の表にまとめました。
| 手法 | サロゲートペア | 結合文字(絵文字など) | 推奨される用途 |
|---|---|---|---|
| lengthプロパティ | × (2とカウント) | × (複数とカウント) | 英数字のみの単純な判定 |
| Array.from() | ○ (1とカウント) | × (複数とカウント) | サロゲートペアのみ考慮すれば良い場合 |
| Intl.Segmenter | ○ (1とカウント) | ○ (1とカウント) | 厳密な文字数カウントが必要な場合 |
基本的には、「ユーザーに表示する文字数」を扱う場合は Intl.Segmenter を使用し、システムの内部的なデータサイズを扱う場合は length を使用するという使い分けが適切です。
実務で役立つ実践的なTips
文字数を取得するだけでなく、実務ではその値を元に文字列を切り取ったり、検証したりする場面が多くあります。
ここでは、よくあるケースに応じた実装例を紹介します。
入力フォームでの最大文字数制限
フォームの入力制限を実装する場合、HTMLのmaxlength属性だけでは不十分なことがあります。
なぜなら、ブラウザのmaxlength属性も内部的にはlengthプロパティと同じ基準でカウントされるためです。
JavaScript側でリアルタイムに正確な残文字数を表示するには、以下のようなロジックを検討しましょう。
function getCorrectLength(str) {
const segmenter = new Intl.Segmenter("ja", { granularity: "grapheme" });
return [...segmenter.segment(str)].length;
}
const userInput = "👨👩👧👦";
if (getCorrectLength(userInput) > 10) {
console.log("文字数が多すぎます");
}
文字列の切り抜き(サロゲートペア対応)
文字列を一定の長さで切り抜く(スライスする)場合も、単純なsubstringでは文字化けの原因になります。
サロゲートペアの途中で切り離されてしまうと、不正な文字列データが生成されるためです。
安全に切り抜くには、一度配列に変換してから処理を行うのが一般的です。
// 安全に3文字目までを取得する
const longText = "𠮷野家で🍎を食べる";
const safeSliced = [...longText].slice(0, 3).join("");
console.log(safeSliced);
𠮷野家
このように配列を介することで、文字の境界を壊さずに安全な操作が可能になります。
バイト数でカウントする場合
データベースの保存容量や通信制限の関係で、文字数ではなく「バイト数」を知りたい場合もあります。
その場合は、TextEncoder APIを使用するのが便利です。
// UTF-8でのバイト数を取得する
const encoder = new TextEncoder();
const encodedData = encoder.encode("こんにちは");
console.log(encodedData.length);
15
日本語はUTF-8では1文字3バイトとして扱われるため、5文字で15バイトという結果になります。
まとめ
JavaScriptでの文字列の長さ取得は、一見簡単に見えて奥が深いテーマです。
基本のlengthプロパティはコードユニット数を返すという性質を正しく理解しておくことが、トラブルを防ぐ第一歩となります。
現代のWeb開発においては、多種多様な絵文字や特殊文字が日常的に入力されます。
ユーザーにとって直感的な「文字数」を算出するためには、Intl.Segmenterやスプレッド構文を活用した実装を標準的に採用することをおすすめします。
それぞれのメソッドのメリットとデメリットを比較し、プロジェクトの要件に最適な手法を選択してください。
正確な文字数カウントを実装することで、UX(ユーザーエクスペリエンス)の向上とシステムの信頼性確保を同時に実現しましょう。
