行数カウントは「テキストの大きさ」を測る基本ツール
行数カウントは、「この文字列は何行あるか?」を数える処理です。
ログの行数、テキストエリア入力の行数、ファイル内容の行数チェックなど、業務ではかなりよく出てきます。
そして行数カウントで一番大事なのは、
「どの文字を“改行”とみなすか」「最後の空行を数えるかどうか」
この2つをちゃんと決めることです。
ここを曖昧にしたまま書くと、OSやデータの違いでズレが出ます。
改行コードの違いをまず理解する
LF / CRLF / CR の3種類がある
行数カウントの前提として、改行コードの種類を知っておく必要があります。
"\n"(LF) … Linux, macOS など"\r\n"(CRLF) … Windows"\r"(CR) … 古いMacなどの名残データ
もし、単純に split("\n") だけで行数を数えると、
Windows由来の "\r\n" の \r が行末に残ったり、"\r" だけの改行を認識できなかったりします。
業務で外部ファイルや他システムからのテキストを扱うなら、
「LF も CRLF も CR も、全部“改行”として扱う」
という前提で行数カウントを書くのが安全です。
正攻法:split("\R") でどの改行でも行数を数える
Java 8以降なら「任意の改行」を一発で扱える
Java 8以降の正規表現には \R という便利なメタ文字があります。
これは「任意の改行シーケンス(LF / CRLF / CR など)」を意味します。
これを使えば、改行コードの違いを意識せずに行数を数えられます。
public final class LineCountUtil {
private LineCountUtil() {}
public static int countLines(String text) {
if (text == null || text.isEmpty()) {
return 0;
}
String[] lines = text.split("\\R");
return lines.length;
}
}
Java使い方はこうです。
String text = "1行目\n2行目\r\n3行目\r4行目";
System.out.println(LineCountUtil.countLines(text)); // 4
System.out.println(LineCountUtil.countLines("")); // 0
System.out.println(LineCountUtil.countLines(null)); // 0
Javaここで押さえておきたい重要ポイントは二つです。
一つ目は、「改行コードの違いを \\R に丸投げしている」ことです。
自分で \r\n|\n|\r と書いてもよいですが、\\R を使うと読みやすく、ミスも減ります。
二つ目は、「null や空文字のときは 0 行とみなす」というポリシーを決めていることです。
これにより、呼び出し側は LineCountUtil.countLines(text) をそのまま比較に使えて、
毎回 null チェックを書かなくて済みます。
「最後が改行で終わるときの空行」をどう扱うか
"1行目\n2行目\n" は 2行か?3行か?
行数カウントでよくハマるのが、
「最後が改行で終わる場合、その後の空行を数えるかどうか」 です。
例えば、次の文字列を考えます。
String text = "1行目\n2行目\n";
Java人によっては「2行」と数えますし、
「3行目は空行だから3行」と数える考え方もあります。
split("\\R") の挙動はこうなります。
String text = "1行目\n2行目\n";
String[] lines = text.split("\\R");
System.out.println(lines.length); // 2
Java末尾の空行はカウントされません。
ここで大事なのは、
「どちらが正しいか」ではなく「自分のシステムではどちらにするかを決める」
ということです。
もし「末尾の空行も1行として数えたい」要件があるなら、
自分でループして数える実装にする必要があります。
末尾の空行も含めて「改行の数+1」で数える方法
改行文字を数えて行数を求める
「末尾の空行も含めて、改行の数+1を行数とする」
というルールで数えたい場合は、こういう実装もできます。
public static int countLinesIncludingLastEmpty(String text) {
if (text == null || text.isEmpty()) {
return 0;
}
int count = 1; // 少なくとも1行はある前提
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (c == '\n') {
count++;
} else if (c == '\r') {
count++;
// CRLF の場合、次の \n をスキップ
if (i + 1 < text.length() && text.charAt(i + 1) == '\n') {
i++;
}
}
}
return count;
}
Java使い方はこうです。
System.out.println(countLinesIncludingLastEmpty("1行目\n2行目\n")); // 3
System.out.println(countLinesIncludingLastEmpty("1行目\n2行目")); // 2
Javaここでの重要ポイントは、
「CRLF(\r\n)を1つの改行として扱うために、\r の後ろの \n をスキップしている」
ことです。
このように、自前でループを書くと柔軟に制御できますが、
そのぶんバグを埋め込みやすくもなります。
だからこそ、まずは「末尾の空行を数える必要が本当にあるか?」を冷静に考えるのが大事です。
多くの業務では、split("\\R") の挙動(末尾の空行は数えない)で十分なことが多いです。
実務での行数カウントの使いどころ
例1:テキストエリアの行数制限
「1行1件で最大100行まで入力できます」といった画面を考えます。
String input = request.getParameter("items");
int lineCount = LineCountUtil.countLines(input);
if (lineCount > 100) {
errors.add("入力できる行数は最大100行までです。");
}
Javaここでは、「末尾の空行は行数に含めなくてよい」という前提で countLines を使っています。
例2:ログの行数をざっくり把握する
ログファイルの内容を文字列として読み込んだあと、
「何行あるか」をざっくり知りたい場面もあります。
String logText = Files.readString(path);
int lines = LineCountUtil.countLines(logText);
System.out.println("ログ行数: " + lines);
Javaここでのポイントは、
「OSや生成元が違っても、行数が安定して数えられる」
という安心感です。
まとめ:行数カウントユーティリティで身につけたい感覚
行数カウントはシンプルに見えて、実は設計のセンスが出る処理です。
押さえておきたいのは次の感覚です。
改行コードは \n だけではなく、\r\n や \r もある
Java 8以降なら split("\\R") で「どの改行でも分割できる」
null や空文字は 0 行とみなすポリシーにしておくと扱いやすい
末尾の空行を行数に含めるかどうかは、要件として明確に決める
もしあなたのコードのどこかに、
int lines = text.split("\n").length;
Javaのような行がそのまま書かれていたら、
それを題材にして、LineCountUtil.countLines のようなユーティリティに置き換えてみてください。
その小さな改善が、
「OSや外部システムの違いに強い、堅牢な文字列処理」を書けるエンジニアへの、確かな一歩になります。
