split は「正規表現で区切る」メソッドだという前提
String#split は、 「文字列を区切り文字で分割する」メソッド ですが、
まず一番大事なポイントは
「引数は“ただの区切り文字”ではなく、“正規表現”として解釈される」
ということです。
ここを意識していないと、
なぜか変なところで分割される. や | で split したら想定外の結果になる
末尾の空要素が消えてしまう
といった「落とし穴」にハマります。
ここから、よくある落とし穴を順番にかみ砕いていきます。
落とし穴1:引数が「正規表現」だという事実を忘れる
ドットやパイプで split するとおかしくなる理由
例えば「ピリオドで区切りたい」場合、ついこう書きがちです。
String s = "a.b.c";
String[] parts = s.split(".");
Java一見よさそうですが、これは 大事故 です。
なぜなら、正規表現において . は
「何か1文字」
という意味だからです。
つまり split(".") は「どんな1文字でも区切り」と解釈されます。
結果、ほぼすべての文字で分割されてしまいます。
正しくは「. を“ただのドット”として扱う」ために、正規表現の世界でエスケープが必要です。
String s = "a.b.c";
String[] parts = s.split("\\.");
// 結果: ["a", "b", "c"]
Javaここで \\. となっているのは、
正規表現としては \. と書きたい
でも Java の文字列リテラルでは \ 自体もエスケープが必要
なので、コード上は \\. になる、という二重エスケープのせいです。
同じく、| も正規表現では「OR(または)」を意味します。
String s = "A|B|C";
String[] parts = s.split("|"); // これも事故
Javasplit("|") は「文字と文字の間が全部区切り」となるので、
一文字ずつバラバラになります。
これも \| としてエスケープすべきです。
String[] parts = s.split("\\|");
Java対策の基本方針
「split の引数は正規表現」という前提に立って、
. や |, *, +, ?, (, ), [, ], {, }, ^, $ など
正規表現で意味を持つ文字を区切りに使いたいときは必ずエスケープする
という意識を持っておくと安全です。
落とし穴2:末尾の空文字が勝手に捨てられる仕様
想定外に配列のサイズが小さくなるパターン
次に厄介なのが 「末尾の空要素が削られてしまう」 という仕様です。
例えば CSV を簡易的に split するとして、こういう文字列があったとします。
String s = "a,b,";
String[] parts = s.split(",");
System.out.println(parts.length); // いくつだと思う?
System.out.println(Arrays.toString(parts));
Java直感的には ["a", "b", ""] の3要素を期待するかもしれません。
ところが実際は
2
[a, b]
になります。
理由は、split(String regex) のデフォルト仕様が
「末尾の空文字列は捨てる」
だからです。
つまり a,b, を , で分割すると、
内部的には ["a", "b", ""] が生成されるのですが、
最後の "" が削除されて ["a", "b"] だけが返ってきます。
limit を指定すると動きが変わる(重要)
末尾の空要素も含めて全部ほしい場合は、
第二引数に limit を指定します。
String s = "a,b,";
String[] parts = s.split(",", -1); // limit に負数
System.out.println(parts.length); // 3
System.out.println(Arrays.toString(parts)); // [a, b, ""]
Javasplit(regex, limit) の limit には以下のルールがあります。
limit > 0
最大 limit - 1 回だけ分割する。末尾空要素は捨てない(場合がある)。
limit == 0
「デフォルト」と同じ扱い。末尾空要素を削る。
limit < 0
回数無制限で分割し、末尾空要素も含めて全部返す。
実務でよく使うのは、
「末尾の空要素も含めて、すべて取りたい」 → split(regex, -1)
です。
特に CSV やログ解析など、「フィールド数が固定なのに、最後の項目だけ空」というケースでは、split(regex) だけだと列数が合わなくなってバグの原因になります。
落とし穴3:空文字を含むケースと、区切り文字が連続するケース
区切り文字が続くと「空文字」が入る
例えば、カンマ区切りの値で空フィールドが途中にあるケース。
String s = "a,,c";
String[] parts = s.split(",", -1);
System.out.println(parts.length); // 3
System.out.println(Arrays.toString(parts)); // [a, "", c]
Javaa,,c の真ん中は「空文字」として扱われます。
これは仕様どおりですが、空文字をまともに考慮していないと、
null と空文字を混同してしまう
空のフィールドが飛ばされたと勘違いする
といった混乱を招きがちです。
「空文字列を split するとどうなるか」
これもよく聞かれます。
String s = "";
String[] parts1 = s.split(",", -1);
String[] parts2 = s.split(",");
Java結果はこうなります。
split(",", -1) → [""](1 要素、空文字)split(",") → [](0 要素の配列)
デフォルト( limit = 0 )だと「末尾の空要素は削る」ので、
空文字列は「末尾の空要素1つだけ」→ それも削られて要素 0 になります。
これも頭の片隅に置いておくと、「あれ?配列が空なんだけど?」というときの理解が早くなります。
落とし穴4:「簡易 CSV パーサとして使う」と地雷を踏みがち
split(",") で CSV を分解するのは危険
初心者がとてもやりがちなのが、
String line = "a,b,c";
String[] fields = line.split(",");
Javaというノリで、「CSV をすべて split で処理しようとする」ことです。
しかし CSV には、次のようなやっかいな仕様があります。
フィールド内にカンマを含めるために "a,b" のようにダブルクォートで囲む
ダブルクォート自体を含めるために "" でエスケープする
例えばこういう行です。
"山田,太郎",25,"東京都,港区"
これを split(",") すると、
ダブルクォート内のカンマまで区切りとみなされて爆散します。
CSV は一見「カンマ区切りだから簡単」と思われがちですが、
正しく処理しようとすると意外と複雑です。
実務では、素直に既存の CSV ライブラリ(OpenCSV など)を使うほうが安全です。split はあくまで「シンプルな区切りや、軽いテキスト処理」に使うもの、と捉えておくとよいです。
落とし穴5:パフォーマンスと「やりすぎ正規表現」
単純な 1 文字区切りに毎回複雑な正規表現を書かない
例えば、スペースかタブの連続で区切りたいので
String[] parts = s.split("\\s+");
Javaのように書くのは、むしろ正しい使い方です。
一方で、単に "," で区切りたいだけなのに、
String[] parts = s.split("\\,");
Javaのように、なんとなく毎回正規表現を意識して「過剰に」書いてしまうのも、
読みやすさを落とす原因になります。
さらに、超複雑な正規表現で split を繰り返すと、
実行速度に影響することもあります(特に巨大な文字列、回数の多いループの中で)。
初心者の段階では、
本当にシンプルな 1 文字区切り → 場合によっては indexOf / substring なども検討
空白や複数種類の区切り記号をまとめて扱いたい → 正規表現で split は有効
くらいの感覚で、「何でもかんでも split(regex)」にせず、
用途に合わせて手段を選ぶ意識を持つとよいです。
まとめ:split の「正しい付き合い方」を頭にインストールする
split の落とし穴を整理すると、次のようなポイントに集約されます。
引数は「正規表現」。. や | などのメタ文字は必ずエスケープが必要。
デフォルトの split(regex) は「末尾の空要素を捨てる」仕様。全部ほしいなら split(regex, -1)。
区切り文字の連続や、完全な空文字列に対する挙動(空文字が要素として入るか / 入らないか)を意識する。
CSV など複雑なフォーマットに split をそのまま使うのは危険。専用ライブラリを検討。

