Java Tips | 文字列処理:行分割

Java Java
スポンサーリンク

行分割は「1つの長い文字列を“行ごと”に扱えるようにする」技

業務システムでは、ログ、テキストファイル、メール本文、エラーメッセージなど、
「複数行の文字列」を扱う場面がたくさんあります。

でも、Javaの String はあくまで「1本の文字列」です。
そのままだと「何行目か」「1行ずつ処理したい」といったことがやりにくい。

そこで必要になるのが 行分割(line split) です。
「改行コードで文字列を区切って、行ごとの配列やリストにする」ことで、
1行ずつループしたり、特定の行だけ取り出したりできるようになります。


行分割で一番大事なポイントは「改行コードの違い」

OSごとに改行コードが違う問題

まず、行分割で絶対に押さえておきたいのが、
「改行コードは1種類じゃない」 という事実です。

代表的には次の3つがあります。

LF\n)… Unix系(Linux, macOSなど)
CRLF\r\n)… Windows
CR\r)… 古いMac(今はほぼ見ないが、データとして残っていることはある)

もし、行分割を単純に split("\n") でやってしまうと、
"\r\n"\r が行末に残ってしまったり、
"\r" だけの改行を認識できなかったりします。

業務で外部ファイルや他システムからのデータを扱うなら、
「LF も CRLF も CR も、全部“改行”として扱う」 という前提で行分割を書くのが安全です。


正攻法:正規表現で「どの改行コードでも分割できる」ようにする

split("\R")(Java 8以降)を使う

Java 8以降なら、実はかなり便利なやり方があります。
正規表現の \R は「任意の改行シーケンス」を意味します。

つまり、LF でも CRLF でも CR でも、全部まとめて「改行」として扱ってくれます。

public final class LineSplitter {

    private LineSplitter() {}

    public static String[] splitLines(String text) {
        if (text == null || text.isEmpty()) {
            return new String[0];
        }
        return text.split("\\R");
    }
}
Java

使い方はこうです。

String text = "1行目\n2行目\r\n3行目\r4行目";

String[] lines = LineSplitter.splitLines(text);
for (String line : lines) {
    System.out.println("[" + line + "]");
}
Java

出力イメージはこうなります。

[1行目]
[2行目]
[3行目]
[4行目]

ここで重要なのは、「改行コードの違いを意識せずに済む」ということです。
split("\\R") を使えば、どのOS由来の改行でも、きれいに行ごとに分割できます。

Java 7以前や \R を使いたくない場合

もし \R を使えない状況なら、
"\r\n" | "\n" | "\r" をまとめて扱う正規表現を自分で書きます。

public static String[] splitLinesLegacy(String text) {
    if (text == null || text.isEmpty()) {
        return new String[0];
    }
    return text.split("\\r\\n|\\n|\\r");
}
Java

やっていることは \R と同じで、
「CRLF か LF か CR のどれかで分割する」という意味です。


行分割ユーティリティを「List<String>」で返す形にする

配列よりリストの方が扱いやすい場面が多い

split は配列を返しますが、
業務コードでは List<String> の方が扱いやすいことが多いです。

例えば、ストリームAPIで処理したり、
forEach を使ったり、
後から行を追加・削除したりしやすくなります。

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public final class LineSplitter {

    private LineSplitter() {}

    public static List<String> splitLinesToList(String text) {
        if (text == null || text.isEmpty()) {
            return Collections.emptyList();
        }
        String[] array = text.split("\\R");
        return Arrays.asList(array);
    }
}
Java

使い方はこうです。

String text = "A\nB\r\nC";

List<String> lines = LineSplitter.splitLinesToList(text);
lines.forEach(line -> System.out.println("[" + line + "]"));
Java

ここでのポイントは、「null や空文字のときに“空のリスト”を返す」という方針です。
これにしておくと、呼び出し側で

for (String line : LineSplitter.splitLinesToList(text)) {
    ...
}
Java

と書いても、nullチェックなしで安全に回せます。


行末の空行をどう扱うかを決める

最後が改行で終わる場合、「空行」を1行として扱うか?

例えば、こんな文字列を考えます。

String text = "1行目\n2行目\n";
Java

最後に改行が入っているので、
「3行目は空行」とみなすかどうか、という問題が出てきます。

split("\\R") は、末尾の空行を基本的には無視します。

String text = "1行目\n2行目\n";

String[] lines = text.split("\\R");
System.out.println(lines.length); // 2
Java

「最後の空行も1行として扱いたい」という要件がある場合は、
少し工夫が必要です。

一つのやり方は、「自分でループしながら行を切り出す」方法です。
ただ、業務でそこまで厳密に「末尾の空行」を扱う場面は多くありません。

ここで大事なのは、
「split の挙動を知ったうえで、“自分のシステムではどう扱うか”を決めること」 です。


実務での行分割の使いどころ

例1:ログ文字列を行ごとに処理する

例えば、1つの文字列にまとめられたログを、
行ごとに処理したい場面を考えます。

String logText = """
ERROR: something bad
WARN: be careful
INFO: all good
""";

for (String line : LineSplitter.splitLinesToList(logText)) {
    if (line.startsWith("ERROR")) {
        System.out.println("エラー行: " + line);
    }
}
Java

ここでは、行分割ユーティリティのおかげで、
「ログ全体」ではなく「ログの1行1行」を自然に扱えるようになっています。

例2:テキストエリアの入力を1行ずつ検証する

ユーザーがテキストエリアに複数行のデータを入力する画面を考えます。
例えば、「1行に1つずつメールアドレスを入力してください」というようなケースです。

サーバ側では、こんな感じで使えます。

String input = request.getParameter("emails");
List<String> lines = LineSplitter.splitLinesToList(input);

for (int i = 0; i < lines.size(); i++) {
    String line = lines.get(i).trim();
    if (line.isEmpty()) {
        continue; // 空行はスキップ
    }
    if (!EmailValidator.isValid(line)) {
        errors.add((i + 1) + "行目のメールアドレスが不正です: " + line);
    }
}
Java

ここでのポイントは、「行番号(i + 1)を使ってエラーメッセージを出せる」ことです。
行分割しておくことで、「何行目に問題があるか」をユーザーに丁寧に伝えられます。


まとめ:行分割ユーティリティで身につけたい感覚

行分割は、「1つの長い文字列を“行ごと”に扱えるようにする」ための基本テクニックです。

押さえておきたいのは次の感覚です。

改行コードは \n だけではなく、\r\n\r もある
split("\\R")(または \\r\\n|\\n|\\r)で、どの改行コードでも分割できるようにする
配列ではなく List<String> で返すと、業務コードで扱いやすい
null や空文字のときは「空の配列/リスト」を返すと安全
末尾の空行をどう扱うかは、要件に応じて決める

もしあなたのコードのどこかに、

String[] lines = text.split("\n");
Java

のような行がそのまま書かれていたら、
それを題材にして、ここで作った LineSplitter.splitLinessplitLinesToList に置き換えてみてください。

その小さな改善が、
「OSや外部システムの違いに強い、堅牢な文字列処理」を書けるエンジニアへの、確かな一歩になります。

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