[解決済み] Java 8のsplitで、結果配列の先頭にある空の文字列が削除されることがあるのはなぜですか?
質問
Java8以前 のように空文字列で分割した場合
String[] tokens = "abc".split("");
でマークされた箇所を分割します。
|
|a|b|c|
空白のため
""
が各文字の前後に存在するからです。その結果、最初にこのような配列が生成されます。
["", "a", "b", "c", ""]
となり、後に
は末尾の空文字列を削除します。
(に明示的に負の値を与えなかったため)。
limit
引数に負の値を与えなかったため)、最終的に
["", "a", "b", "c"]
Java 8では の分割メカニズムが変更されたようです。今、私たちが
"abc".split("")
を取得します。
["a", "b", "c"]
の代わりに
["", "a", "b", "c"]
.
私が最初に推測したのは、もしかしたら今 をリードする と同じように、空文字列も削除されるようになったのでしょう。 末尾の と同様に削除されます。
しかし、この理論は失敗します。
"abc".split("a")
戻る
["", "bc"]
を返すので、先頭の空文字列は削除されません。
ここで何が起こっているのか、誰か説明してください。どのように
split
のルールはJava 8でどのように変更されましたか?
どのように解決するのですか?
の動作は
String.split
(これは
Pattern.split
を呼び出す) は、Java 7 と Java 8 の間で変更されます。
ドキュメンテーション
のドキュメントを比較すると
Pattern.split
で
Java 7
と
Java 8
を追加すると、以下のような句が追加されることが確認できます。
入力シーケンスの先頭に正の幅のマッチがある場合、結果の配列の先頭に空の先頭部分文字列が含まれます。しかし、冒頭のゼロ幅のマッチはそのような空の先頭部分文字列を生成することはありません。
同じ節が
String.split
で
ジャバ8
と比較して
Java 7
.
リファレンス実装
のコードを比較してみましょう。
Pattern.split
のコードを比較してみましょう。コードは、バージョン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);
}
Java8で以下のコードを追加すると、入力文字列の先頭の長さ0のマッチが除外され、上記の動作が説明されます。
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;
}
互換性を維持する
Java8以上での動作に準ずる
を作るには
split
の挙動をバージョン間で一貫させ、Java 8での挙動と互換性を持たせる。
-
もしあなたの正規表現が
は
が長さゼロの文字列にマッチする場合、単に
(?!\A)
で の後に を追加し、捕捉しないグループで元の正規表現を囲みます。(?:...)
(で囲みます(必要な場合)。 - もしあなたの正規表現が はできません。 が長さゼロの文字列にマッチする場合は、何もする必要はありません。
- 正規表現が長さ0の文字列にマッチするかどうかわからない場合は、手順1の両方の操作を行います。
(?!\A)
は文字列の先頭で終わらないことをチェックします。これは、文字列の先頭で空のマッチであることを意味します。
Java7以前での以下の動作
を作るための一般的な解決策はありません。
split
のインスタンスをすべて置き換えるというような、Java 7 以前との後方互換性のある解決策はありません。
split
のインスタンスをすべて置き換えて、独自のカスタム実装を指すようにすることです。
関連
-
XMLファイル操作時のjava.util.NoSuchElementExceptionを解決する方法。
-
型に解決できない エラー解決
-
セミコロン期待値エラー解決
-
XXX型を囲むインスタンスがJavaでアクセスできない
-
ecplise プロンプトが表示されます。"選択したものは起動できません。" "最近の起動はありません。"
-
Spring Bootは、Tomcatの組み込みのmaxPostSizeの値を設定します。
-
[解決済み] Bashで文字列をデリミターで分割するには?
-
[解決済み] Javaにおける例外処理によるパフォーマンスへの影響とは?
-
[解決済み] Java 文字列の分割で空の値を削除
-
[解決済み】array[idx++]+="a "は、Java 8ではidxを1回増やすが、Java 9と10では2回増やすのはなぜか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
Java Notes 005_この行に複数のマーカーがある - キーを変数に解決できない - シンタックスエラー、ins
-
配列定数は初期化子でのみ使用可能です。
-
Javaがエラーで実行される、選択が起動できない、最近起動したものがない
-
Java Runtime Environmentを継続するためのメモリが不足しています。
-
Java の double データ型における 0.0 と -0.0 の問題
-
java 365*1000*60*60*24 計算問題
-
htmlとwordの相互変換の実装(画像あり)
-
[解決済み] javaで文字列から文字列配列への変換
-
[解決済み] Javaで文字列を分割する方法
-
[解決済み] 文字列を文字列の配列に分割する