Dividir por comas fuera de comillas
Mi programa lee una línea de un archivo. Esta línea contiene texto separado por comas como:
123,test,444,"don't split, this",more test,1
Me gustaría que el resultado de una división fuera este:
123
test
444
"don't split, this"
more test
1
Si uso el String.split(",")
, obtendría esto:
123
test
444
"don't split
this"
more test
1
En otras palabras: la coma en la subcadena "don't split, this"
no es un separador. Como lidiar con esto?
Puedes probar esta expresión regular:
str.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)");
Esto divide la cadena ,
seguida de un número par de comillas dobles. En otras palabras, se divide por coma fuera de las comillas dobles. Esto funcionará siempre que tenga comillas equilibradas en su cadena.
Explicación:
, // Split on comma
(?= // Followed by
(?: // Start a non-capture group
[^"]* // 0 or more non-quote characters
" // 1 quote
[^"]* // 0 or more non-quote characters
" // 1 quote
)* // 0 or more repetition of non-capture group (multiple of 2 quotes will be even)
[^"]* // Finally 0 or more non-quotes
$ // Till the end (This is necessary, else every comma will satisfy the condition)
)
Incluso puedes escribir así en tu código, usando (?x)
el modificador con tu expresión regular. El modificador ignora los espacios en blanco en su expresión regular, por lo que resulta más fácil leer una expresión regular dividida en varias líneas como esta:
String[] arr = str.split("(?x) " +
", " + // Split on comma
"(?= " + // Followed by
" (?: " + // Start a non-capture group
" [^\"]* " + // 0 or more non-quote characters
" \" " + // 1 quote
" [^\"]* " + // 0 or more non-quote characters
" \" " + // 1 quote
" )* " + // 0 or more repetition of non-capture group (multiple of 2 quotes will be even)
" [^\"]* " + // Finally 0 or more non-quotes
" $ " + // Till the end (This is necessary, else every comma will satisfy the condition)
") " // End look-ahead
);
¿Por qué dividir cuando puedes igualar?
Resucitando esta pregunta porque, por alguna razón, no se mencionó la solución fácil. Aquí está nuestra expresión regular bellamente compacta:
"[^"]*"|[^,]+
Esto coincidirá con todos los fragmentos deseados ( ver demostración ).
Explicación
- Con
"[^"]*"
, emparejamos completo"double-quoted strings"
- o
|
- hacemos coincidir
[^,]+
cualquier carácter que no sea una coma.
Un posible refinamiento es mejorar el lado de la cadena de la alternancia para permitir que las cadenas entre comillas incluyan comillas de escape.
Sobre la base de la respuesta de @ zx81 , porque la idea de hacer coincidir es realmente buena, agregué results
una llamada a Java 9, que devuelve un archivo Stream
. Como OP quería usar split
, lo recopilé String[]
, al igual split
que lo hace.
Tenga cuidado si tiene espacios después de los separadores de coma ( a, b, "c,d"
). Entonces necesitas cambiar el patrón.
demostración de jshell
$ jshell
-> String so = "123,test,444,\"don't split, this\",more test,1";
| Added variable so of type String with initial value "123,test,444,"don't split, this",more test,1"
-> Pattern.compile("\"[^\"]*\"|[^,]+").matcher(so).results();
| Expression value is: java.util.stream.ReferencePipeline$Head@2038ae61
| assigned to temporary variable $68 of type java.util.stream.Stream<MatchResult>
-> $68.map(MatchResult::group).toArray(String[]::new);
| Expression value is: [Ljava.lang.String;@6b09bb57
| assigned to temporary variable $69 of type String[]
-> Arrays.stream($69).forEach(System.out::println);
123
test
444
"don't split, this"
more test
1
Código
String so = "123,test,444,\"don't split, this\",more test,1";
Pattern.compile("\"[^\"]*\"|[^,]+")
.matcher(so)
.results()
.map(MatchResult::group)
.toArray(String[]::new);
Explicación
- Coincidencias de expresiones regulares
[^"]
: una cita, cualquier cosa menos una cita, una cita. - Coincidencias de expresiones regulares
[^"]*
: una cotización, cualquier cosa menos una cotización 0 (o más) veces, una cotización. - Esa expresión regular debe ir primero para "ganar"; de lo contrario, coincidirá con cualquier cosa que no sea una coma 1 o más veces , es decir:
[^,]+
- "ganaría". results()
requiere Java 9 o superior.- Devuelve
Stream<MatchResult>
, que asigno mediantegroup()
llamada y recopilación a una matriz de cadenas. La llamada sin parámetrostoArray()
regresaríaObject[]
.