JavaScript Tips | 文字列ユーティリティ:生成 - 文字数カウント

JavaScript JavaScript
スポンサーリンク

「文字数カウント」で本当に数えたいものは何か

まず、ここをはっきりさせたいです。
「文字数を数える」と言っても、実は意味が分かれます。

画面上で「何文字入力されたか」を知りたい。
DBのカラム制限(バイト数)に収まるかを知りたい。
「最大 100 文字まで」といったバリデーションをしたい。

そして JavaScript では、「見た目の1文字」と string.length が必ずしも一致しない、という落とし穴があります。
ここをちゃんと理解しておくと、一気に“業務で使える”文字数カウントになります。


基本の length と、そのままでは危ない理由

str.length が数えているもの

JavaScript の文字列は、内部的には UTF-16 で表現されています。
str.length が数えているのは、「UTF-16 のコードユニットの数」です。

"ABC".length;      // 3
"あいう".length;   // 3
JavaScript

ここまでは直感どおりです。
ところが、絵文字や一部の漢字など、「サロゲートペア」と呼ばれる文字が出てくると話が変わります。

"😊".length;       // 2
"U+20BB7".length;      // 2(旧字体の「吉」いわゆる「つちよし」の吉など)
JavaScript

※「つちよし」の吉を記載できないのでUTFで書いています。

見た目は「1文字」なのに、length は 2 になります。
つまり、「画面上の1文字数」としては length は信用できない、ということです。


「見た目の1文字数」を数える安全な方法

for...of や Array.from を使う

「見た目の1文字」を数えたいときは、コードユニットではなく“コードポイント”単位で数える必要があります。
モダンな JavaScript では、for...ofArray.from を使うと、サロゲートペアも1文字として扱えます。

function countChars(str) {
  if (str == null) return 0;

  let count = 0;

  for (const _ch of String(str)) {
    count++;
  }

  return count;
}
JavaScript

ここが重要ポイントです。

for (const _ch of String(str)) は、「文字列を“1文字ずつ”取り出す」構文。
このとき、絵文字やサロゲートペアも1文字として扱われる。
そのたびに count++ していけば、「見た目の1文字数」が数えられる。

動きのイメージはこうです。

"ABC".length;          // 3
countChars("ABC");     // 3

"あいう".length;       // 3
countChars("あいう");  // 3

"😊".length;           // 2
countChars("😊");      // 1

"A😊B".length;         // 4
countChars("A😊B");    // 3
JavaScript

「入力欄は最大 20 文字まで」といった制限を、ユーザーの感覚に合わせてかけたいときは、必ずこういう「コードポイント単位」のカウントを使うべきです。


「バイト数」を知りたい場合(DB制限など)

文字数ではなく「何バイトか」が問題になるケース

RDB のカラム定義や、外部システムとの連携では、
「文字数」ではなく「バイト数」で制限されていることがあります。

例として、「UTF-8 で最大 255 バイトまで」といった制限です。
この場合、「全角1文字=3バイト(UTF-8)」などを意識する必要があります。

簡易的な UTF-8 バイト数カウント

ブラウザ環境なら、TextEncoder を使うと簡単に UTF-8 のバイト数を数えられます。

function countUtf8Bytes(str) {
  if (str == null) return 0;

  const s = String(str);
  const encoder = new TextEncoder();
  const bytes = encoder.encode(s);

  return bytes.length;
}
JavaScript

動きはこうなります。

countUtf8Bytes("ABC");      // 3
countUtf8Bytes("あ");       // 3(UTF-8 では全角1文字=3バイト)
countUtf8Bytes("😊");       // 4(絵文字は4バイトになることが多い)
JavaScript

DB のカラム制限が「UTF-8 で N バイトまで」のような場合は、
countChars ではなく countUtf8Bytes を使ってチェックする必要があります。


実務での使いどころと設計のポイント

「何を基準に制限するか」を先に決める

ここが一番大事です。

画面上の「見た目の文字数」で制限したいのか。
DB の「バイト数」で制限したいのか。

これを曖昧にしたまま length を使うと、
絵文字1つで「2文字扱い」になったり、
DB に入らないのにフロントではOKになったり、といったズレが出ます。

なので、ユーティリティをこう分けておくときれいです。

export function countChars(str) { ... }       // 見た目の1文字数
export function countUtf8Bytes(str) { ... }   // UTF-8 のバイト数
JavaScript

そして、呼び出し側で「どちらを使うか」を明示的に選ぶようにします。

バリデーションとセットで使う

例えば、「最大 20 文字まで」の入力欄ならこう書けます。

const maxChars = 20;
const value = input.value;
const length = countChars(value);

if (length > maxChars) {
  showError(`最大 ${maxChars} 文字までです(現在 ${length} 文字)。`);
}
JavaScript

「何文字まで」と「今何文字か」を一緒に見せてあげると、ユーザーにも親切です。


ちょっとだけ手を動かしてみる

コンソールで、次の順番で試してみてください。

"😊".length;
countChars("😊");

"A😊B".length;
countChars("A😊B");

countUtf8Bytes("A");
countUtf8Bytes("あ");
countUtf8Bytes("😊");
JavaScript

lengthcountChars の違い」
「文字によってバイト数が違う感覚」

を、自分の目で確かめてみてください。

そのうえで、自分のプロジェクトに

export function countChars(str) { ... }
export function countUtf8Bytes(str) { ... }
JavaScript

の2本を置いて、

「画面の文字数制限 → countChars」
「DBや外部連携のバイト制限 → countUtf8Bytes」

という使い分けをルール化してみてください。

それだけで、あなたの「文字数カウント」は
なんとなくの length 依存から、
ユーザーの感覚とシステム制約を両方意識した“業務レベルのユーティリティ”に変わります。

タイトルとURLをコピーしました