Tratar:
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);
}
}
}
Salida:
> foo
> bar
> c;qual="baz,blurb"
> d;junk="quux,syzygy"
En otras palabras: dividir en la coma solo si esa coma tiene cero, o un número par de comillas delante de ella .
O, un poco más amigable para los ojos:
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);
}
}
}
que produce lo mismo que el primer ejemplo.
EDITAR
Como mencionó @MikeFHay en los comentarios:
Prefiero usar Guava's Splitter , ya que tiene valores predeterminados más sanos (vea la discusión anterior sobre los partidos vacíos que se recortan String#split()
, así que lo hice:
Splitter.on(Pattern.compile(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)"))
String line = "equals: =,\"quote: \"\"\",\"comma: ,\""
todo lo que necesita hacer es quitar la comilla doble extraña caracteres.-1
al método parámetro de división:line.split(regex, -1)
. Ver: docs.oracle.com/javase/6/docs/api/java/lang/…Splitter.on(Pattern.compile(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)"))
.findAllIn("(?s)(?:\".*?\"|[^\",]*)*")
en combinación con un paso de postprocesamiento para omitir el primer campo (siempre vacío) que sigue a cada campo no vacío.Si bien me gustan las expresiones regulares en general, para este tipo de tokenización dependiente del estado, creo que un analizador simple (que en este caso es mucho más simple de lo que esa palabra podría hacer sonar) es probablemente una solución más limpia, en particular con respecto a la mantenibilidad , p.ej:
Si no le importa preservar las comas dentro de las comillas, puede simplificar este enfoque (sin manejo del índice de inicio, sin el caso especial del último carácter ) al reemplazar sus comas entre comillas por otra cosa y luego dividirlas entre comas:
fuente
http://sourceforge.net/projects/javacsv/
https://github.com/pupi1985/JavaCSV-Reloaded (bifurcación de la biblioteca anterior que permitirá que la salida generada tenga terminadores de línea de Windows
\r\n
cuando no se ejecuta Windows)http://opencsv.sourceforge.net/
API CSV para Java
¿Me puede recomendar una biblioteca Java para leer (y posiblemente escribir) archivos CSV?
¿Java lib o aplicación para convertir CSV a un archivo XML?
fuente
No recomendaría una respuesta de regex de Bart, creo que la solución de análisis es mejor en este caso particular (como propuso Fabian). He intentado la solución regex y la implementación de análisis propio, he encontrado que:
Mi solución y prueba a continuación.
Por supuesto, puede cambiar el cambio a else-ifs en este fragmento si se siente incómodo con su fealdad. Tenga en cuenta entonces la falta de descanso después del interruptor con separador. StringBuilder fue elegido en lugar de StringBuffer por diseño para aumentar la velocidad, donde la seguridad del hilo es irrelevante.
fuente
-1
método de división en la respuesta de Bart, capturará cadenas vacías (incluidas las cadenas vacías después de la última coma):line.split(regex, -1)
Prueba un lookaround como
(?!\"),(?!\")
. Esto debería coincidir con los,
que no están rodeados"
.fuente
(?<!"),(?!")
, pero todavía no funcionará. Dada la cadenaone,two,"three,four"
, coincide correctamente con la comaone,two
, pero también coincide con la coma"three,four"
y no coincide con unatwo,"three
.Estás en esa zona límite molesta donde las expresiones regulares casi no funcionan (como ha sido señalado por Bart, escapar de las citas haría la vida difícil), y sin embargo, un analizador completo parece excesivo.
Si es probable que necesite una mayor complejidad en el corto plazo, iría a buscar una biblioteca de analizador. Por ejemplo este
fuente
Estaba impaciente y decidí no esperar respuestas ... para referencia, no parece tan difícil hacer algo como esto (que funciona para mi aplicación, no necesito preocuparme por las comillas escapadas, como las cosas entre comillas se limita a algunas formas restringidas):
(ejercicio para el lector: extienda el manejo de las comillas escapadas buscando también barras invertidas).
fuente
El enfoque más simple es no hacer coincidir delimitadores, es decir, comas, con una lógica adicional compleja para que coincida con lo que realmente se pretende (los datos que podrían ser comillas), solo para excluir delimitadores falsos, sino que coinciden con los datos previstos en primer lugar.
El patrón consta de dos alternativas, una cadena entre comillas (
"[^"]*"
o".*?"
) o todo hasta la siguiente coma ([^,]+
). Para admitir celdas vacías, debemos permitir que el elemento no entrecomillado esté vacío y consumir la siguiente coma, si la hay, y usar el\\G
ancla:El patrón también contiene dos grupos de captura para obtener, el contenido de la cadena citada o el contenido sin formato.
Luego, con Java 9, podemos obtener una matriz como
mientras que las versiones anteriores de Java necesitan un bucle como
Agregar los elementos a una
List
matriz se deja como un impuesto especial al lector.Para Java 8, puede usar la
results()
implementación de esta respuesta , para hacerlo como la solución Java 9.Para contenido mixto con cadenas incrustadas, como en la pregunta, simplemente puede usar
Pero entonces, las cadenas se mantienen en su forma citada.
fuente
En lugar de usar lookahead y otro regex loco, solo saque las comillas primero. Es decir, para cada agrupación de cotizaciones, reemplace esa agrupación con
__IDENTIFIER_1
o algún otro indicador, y asigne esa agrupación a un mapa de cadena, cadena.Después de dividir en coma, reemplace todos los identificadores asignados con los valores de cadena originales.
fuente
¿Qué pasa con una línea usando String.split ()?
fuente
Haría algo como esto:
fuente