文字カウントは「制限」と「バリデーション」の土台になる
文字カウントは、その名の通り「文字列の長さを数える」処理です。
一見すごく地味ですが、業務システムではかなり重要な役割を持ちます。
入力フォームで「名前は50文字以内」「備考は200文字以内」といった制限をかける。
DBのカラム長(VARCHAR(100) など)を超えないようにチェックする。
メールや外部APIに送る前に、文字数制限を満たしているか確認する。
こういう「制限」「バリデーション」のほとんどは、
どこかで必ず「文字数を数える」処理に行き着きます。
だからこそ、単なる length() で終わらせず、
「何を数えているのか」をちゃんと理解しておくことが大事です。
まずは基本:String#length() で「char の数」を数える
一番シンプルな文字カウントユーティリティ
Javaで一番基本になるのは、String#length() です。
これは「その文字列が何文字か」を返してくれます。
まずは素直にラップしたユーティリティを書いてみます。
public final class CharCounter {
private CharCounter() {}
public static int length(String text) {
if (text == null) {
return 0;
}
return text.length();
}
}
Java使い方はこうです。
System.out.println(CharCounter.length("ABC")); // 3
System.out.println(CharCounter.length("あいう")); // 3
System.out.println(CharCounter.length("山田太郎")); // 4
System.out.println(CharCounter.length("")); // 0
System.out.println(CharCounter.length(null)); // 0
Javaここで最初に押さえておきたい重要ポイントは二つです。
一つ目は、「null をどう扱うかを決めている」ことです。
ここでは「null のときは 0 を返す」という方針にしています。
「null はありえない」と決めて例外を投げる実装もありますが、
業務ユーティリティとしては「0にしておく」と扱いやすい場面が多いです。
二つ目は、「length() が数えているのは“char の数”である」ということです。
ほとんどの日本語や英数字は「1文字=1char」なので、
普段はあまり意識しなくても問題になりません。
ただし、絵文字など一部の文字では話が変わります。
ここが次の重要ポイントです。
「見た目の1文字」と「char の数」は必ずしも一致しない
絵文字や一部の漢字は「2char」で1文字
Javaの char は「UTF-16 の1ユニット」です。
多くの文字は1ユニットで表現できますが、
一部の文字(絵文字、異体字など)は「サロゲートペア」と呼ばれる2ユニットで表現されます。
例えば、こんなコードを見てみます。
String s1 = "A😊B";<br>System.out.println(s1.length()); // 4 になることが多い<br><br>String s2 = "U+20BB7"; // いわゆる「つちよし」の吉<br>System.out.println(s2.length()); // 2 になることが多い<br>Java見た目では "A" "😊" "B" の3文字、"U+20BB7" は1文字ですが、length() はそれぞれ 4 と 2 を返します。
つまり、length() は「見た目の文字数」ではなく、
「UTF-16 のユニット数(char の数)」を返している、ということです。
ここで深掘りしたい重要ポイントは、
「“文字数”という言葉が、何を意味しているかをはっきりさせる必要がある」
ということです。
画面の表示上の「見た目の文字数」を制限したいのか。
DBのカラム長(バイト数)を超えないようにしたいのか。
それとも単に length() の値でざっくりチェックしたいのか。
要件によって、「何を数えるべきか」が変わります。
ここを曖昧にしたまま実装すると、後で必ずズレが出ます。
「見た目の文字数」を数えたいなら codePoint を使う
codePointCount で「Unicodeコードポイントの数」を数える
「絵文字も含めて、見た目の1文字を1カウントにしたい」
という場合は、codePoint 単位で数える必要があります。
Javaには、String#codePointCount(int beginIndex, int endIndex) というメソッドがあります。
これを使うと、「サロゲートペアを1文字として数える」ことができます。
ユーティリティにするとこうなります。
public final class CharCounter {
private CharCounter() {}
public static int lengthByCodePoint(String text) {
if (text == null || text.isEmpty()) {
return 0;
}
return text.codePointCount(0, text.length());
}
}
Java使い方はこうです。
System.out.println(CharCounter.lengthByCodePoint("ABC")); // 3<br>System.out.println(CharCounter.lengthByCodePoint("あいう")); // 3<br>System.out.println(CharCounter.lengthByCodePoint("A😊B")); // 3<br>System.out.println(CharCounter.lengthByCodePoint("U+20BB7")); // 1<br>System.out.println(CharCounter.lengthByCodePoint("")); // 0<br>System.out.println(CharCounter.lengthByCodePoint(null)); // 0<br>Javaここでの重要ポイントは、
「length() と codePointCount() のどちらを使うかは、要件で決める」
ということです。
「絵文字を含めた見た目の文字数で制限したい」なら codePointCount。
「そこまで厳密でなくてよい」「絵文字は想定していない」なら length() でもよい。
大事なのは、「どちらを使っているかを自覚していること」です。
知らないまま使うのが一番危険です。
「文字数制限チェック」ユーティリティにしてみる
length を直接返すだけでなく、「制限を超えているか」を判定する
実務では、「文字数を数える」だけでなく、
「制限以内かどうか」をチェックすることが多いです。
例えば、「名前は50文字以内」というチェックをするユーティリティは、こう書けます。
public final class LengthValidator {
private LengthValidator() {}
public static boolean isWithinLength(String text, int maxLength) {
if (text == null) {
return true; // null は「未入力」として別途チェックする前提
}
return text.length() <= maxLength;
}
public static boolean isWithinCodePointLength(String text, int maxLength) {
if (text == null) {
return true;
}
int len = CharCounter.lengthByCodePoint(text);
return len <= maxLength;
}
}
Java使い方はこうです。
System.out.println(LengthValidator.isWithinLength("山田太郎", 4)); // true
System.out.println(LengthValidator.isWithinLength("山田太郎", 3)); // false
System.out.println(LengthValidator.isWithinCodePointLength("A😊B", 3)); // true
System.out.println(LengthValidator.isWithinCodePointLength("A😊B", 2)); // false
Javaここでのポイントは二つです。
一つ目は、「null の扱いを“別レイヤー”に分けている」ことです。
ここでは「null は長さチェックOK」として扱い、
「必須入力かどうか」は別のバリデーションで見る、という設計にしています。
これにより、「必須チェック」と「長さチェック」をきれいに分離できます。
二つ目は、「length() 版と codePoint 版を分けて用意している」ことです。
プロジェクトの方針として「絵文字まで考慮するかどうか」を決めたうえで、
どちらか一方を標準として使う、というのが理想です。
「バイト数」を気にする場合は別問題になる
DBのカラム長や外部システムの制限が「バイト数」のとき
ここまでの話は、「文字数」の話でした。
しかし、業務では「バイト数」で制限されることもあります。
例えば、DBのカラムが VARCHAR(100) で、
文字コードが UTF-8 の場合、
日本語1文字が3バイトになることが多いです。
この場合、「100文字以内」ではなく「100バイト以内」をチェックしないと、
DBに入れるときにエラーになる可能性があります。
バイト数を数えるには、getBytes(Charset) を使います。
import java.nio.charset.StandardCharsets;
public final class ByteCounter {
private ByteCounter() {}
public static int lengthInUtf8Bytes(String text) {
if (text == null || text.isEmpty()) {
return 0;
}
return text.getBytes(StandardCharsets.UTF_8).length;
}
}
Java使い方はこうです。
System.out.println(ByteCounter.lengthInUtf8Bytes("ABC")); // 3
System.out.println(ByteCounter.lengthInUtf8Bytes("あ")); // 3(UTF-8では多くの場合)
System.out.println(ByteCounter.lengthInUtf8Bytes("山田太郎")); // 12 など
Javaここでの重要ポイントは、
「文字数」と「バイト数」は別物
ということです。
画面の入力制限は「文字数」でよくても、
DBや外部APIは「バイト数」で制限されているかもしれません。
どちらを基準にするかを、要件と設計でちゃんと決めておく必要があります。
実務での文字カウントの使いどころ
例1:入力フォームのサーバサイドバリデーション
例えば、「備考は200文字以内」という入力項目を考えます。
String remark = request.getParameter("remark");
if (remark != null && !LengthValidator.isWithinCodePointLength(remark, 200)) {
errors.add("備考は200文字以内で入力してください。");
}
Javaここでは、「見た目の文字数」で制限したい前提で codePoint 版を使っています。
もし「絵文字は想定しない」「lengthで十分」という方針なら、isWithinLength を使えばOKです。
例2:DBカラム長を超えないようにチェックする
例えば、「ユーザー名はDBのカラムがUTF-8で30バイトまで」という制約があるとします。
String username = request.getParameter("username");
if (username != null && ByteCounter.lengthInUtf8Bytes(username) > 30) {
errors.add("ユーザー名は30バイト以内で入力してください。");
}
Javaここでは、「バイト数」を基準にチェックしています。
画面上の見た目とはズレる可能性がありますが、
DB制約を優先する必要がある場面ではこういう書き方になります。
まとめ:文字カウントユーティリティで身につけたい感覚
文字カウントは、「ただ length() を呼ぶだけ」のように見えますが、
その裏にはいくつかの重要な観点があります。
length() が数えているのは「char の数」であること。
絵文字などを「見た目の1文字」として数えたいなら codePointCount を使うこと。
DBや外部システムの制限が「バイト数」の場合は、getBytes で数える必要があること。
null の扱い、必須チェックと長さチェックの分離、といった設計上のルールを決めておくこと。
もしあなたのコードのどこかに、
if (text.length() > 100) { ... }
Javaのような行がそのまま書かれていたら、
それを題材にして、「本当に length() でよいのか」「codePoint かバイト数ではないか」を一度立ち止まって考えてみてください。
その小さな意識の変化が、
「文字列の中身と制約をちゃんと理解して扱えるエンジニア」への、確かな一歩になります。

