String.split
(を呼び出すPattern.split
)の動作は、Java 7とJava 8で異なります。
ドキュメンテーション
ドキュメント間の比較Pattern.split
でのJava 7とJavaの8、我々は追加されている次の句を守ってください。
入力シーケンスの先頭に正の幅の一致がある場合、結果の配列の先頭に空の先行部分文字列が含まれます。ただし、最初に幅が一致しないと、そのような空の先行部分文字列は生成されません。
Java 7と比較して、同じ句がJava 8にも追加さString.split
れています。
リファレンス実装
Pattern.split
Java 7とJava 8の参照実装のコードを比較してみましょう。コードは、バージョン7u40-b43および8-b132のgrepcodeから取得されます。
Java 7
public String[] split(CharSequence input, int limit) {
int index = 0;
boolean matchLimited = limit > 0;
ArrayList<String> matchList = new ArrayList<>();
Matcher m = matcher(input);
// Add segments before each match found
while(m.find()) {
if (!matchLimited || matchList.size() < limit - 1) {
String match = input.subSequence(index, m.start()).toString();
matchList.add(match);
index = m.end();
} else if (matchList.size() == limit - 1) { // last one
String match = input.subSequence(index,
input.length()).toString();
matchList.add(match);
index = m.end();
}
}
// If no match was found, return this
if (index == 0)
return new String[] {input.toString()};
// Add remaining segment
if (!matchLimited || matchList.size() < limit)
matchList.add(input.subSequence(index, input.length()).toString());
// Construct result
int resultSize = matchList.size();
if (limit == 0)
while (resultSize > 0 && matchList.get(resultSize-1).equals(""))
resultSize--;
String[] result = new String[resultSize];
return matchList.subList(0, resultSize).toArray(result);
}
Java 8
public String[] split(CharSequence input, int limit) {
int index = 0;
boolean matchLimited = limit > 0;
ArrayList<String> matchList = new ArrayList<>();
Matcher m = matcher(input);
// Add segments before each match found
while(m.find()) {
if (!matchLimited || matchList.size() < limit - 1) {
if (index == 0 && index == m.start() && m.start() == m.end()) {
// no empty leading substring included for zero-width match
// at the beginning of the input char sequence.
continue;
}
String match = input.subSequence(index, m.start()).toString();
matchList.add(match);
index = m.end();
} else if (matchList.size() == limit - 1) { // last one
String match = input.subSequence(index,
input.length()).toString();
matchList.add(match);
index = m.end();
}
}
// If no match was found, return this
if (index == 0)
return new String[] {input.toString()};
// Add remaining segment
if (!matchLimited || matchList.size() < limit)
matchList.add(input.subSequence(index, input.length()).toString());
// Construct result
int resultSize = matchList.size();
if (limit == 0)
while (resultSize > 0 && matchList.get(resultSize-1).equals(""))
resultSize--;
String[] result = new String[resultSize];
return matchList.subList(0, resultSize).toArray(result);
}
Java 8で次のコードを追加すると、入力文字列の先頭にある長さゼロの一致が除外され、上記の動作が説明されます。
if (index == 0 && index == m.start() && m.start() == m.end()) {
// no empty leading substring included for zero-width match
// at the beginning of the input char sequence.
continue;
}
互換性の維持
Java 8以降での動作の追跡
split
バージョン間で一貫して動作し、Java 8の動作と互換性を持たせるには:
- 正規表現が長さゼロの文字列に一致できる場合は、正規表現の最後に追加
(?!\A)
し、元の正規表現を非キャプチャグループにラップします(必要な場合)。(?:...)
- 正規表現が長さ0の文字列と一致しない場合、何もする必要はありません。
- 正規表現が長さ0の文字列と一致するかどうかわからない場合は、手順1で両方の操作を実行します。
(?!\A)
文字列が文字列の先頭で終了していないことを確認します。これは、一致が文字列の先頭で空の一致であることを意味します。
Java 7以前の動作の追跡
独自のカスタム実装を指すようsplit
にすべてのインスタンスを置き換える以外に、Java 7以前との下位互換性を確保するための一般的な解決策はありませんsplit
。
s.split("(?!^)")
ようです。