次のような文字列があります:
foo,bar,c;qual="baz,blurb",d;junk="quux,syzygy"
カンマで分割したいのですが、引用符内のカンマは無視する必要があります。どうすればいいでしょうか? 正規表現のアプローチは失敗しているようです。引用符を見つけたら手動でスキャンして別のモードに入ることはできると思いますが、既存のライブラリを使用できれば便利です。(編集: 既に JDK の一部になっているライブラリ、または Apache Commons などのよく使用されるライブラリの一部になっているライブラリを意味していると思います。)
上記の文字列は次のように分割されます。
foo
bar
c;qual="baz,blurb"
d;junk="quux,syzygy"
注:これは CSV ファイルではなく、より大きな全体的な構造を持つファイルに含まれる単一の文字列です。
ベストアンサー1
試す:
public class Main {
public static void main(String[] args) {
String line = "foo,bar,c;qual=\"baz,blurb\",d;junk=\"quux,syzygy\"";
String[] tokens = line.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", -1);
for(String t : tokens) {
System.out.println("> "+t);
}
}
}
出力:
> foo
> bar
> c;qual="baz,blurb"
> d;junk="quux,syzygy"
つまり、コンマの前に引用符が 0 個または偶数個ある場合にのみ、コンマで分割します。
あるいは、もう少し目に優しい方法:
public class Main {
public static void main(String[] args) {
String line = "foo,bar,c;qual=\"baz,blurb\",d;junk=\"quux,syzygy\"";
String otherThanQuote = " [^\"] ";
String quotedString = String.format(" \" %s* \" ", otherThanQuote);
String regex = String.format("(?x) "+ // enable comments, ignore white spaces
", "+ // match a comma
"(?= "+ // start positive look ahead
" (?: "+ // start non-capturing group 1
" %s* "+ // match 'otherThanQuote' zero or more times
" %s "+ // match 'quotedString'
" )* "+ // end group 1 and repeat it zero or more times
" %s* "+ // match 'otherThanQuote'
" $ "+ // match the end of the string
") ", // stop positive look ahead
otherThanQuote, quotedString, otherThanQuote);
String[] tokens = line.split(regex, -1);
for(String t : tokens) {
System.out.println("> "+t);
}
}
}
最初の例と同じ結果になります。
編集
コメントで@MikeFHayが言及したように:
私はGuava の Splitterを使うことを好みます。これは、より合理的なデフォルトを持っているからです ( によって空の一致がトリミングされることに関する上記の議論を参照してください)
String#split()
。そこで、私は次のようにしました:Splitter.on(Pattern.compile(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)"))