Experimentar:
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);
}
}
}
Resultado:
> foo
> bar
> c;qual="baz,blurb"
> d;junk="quux,syzygy"
Em outras palavras: divida na vírgula apenas se ela tiver zero ou um número par de aspas à frente .
Ou, um pouco mais amigável para os olhos:
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 produz o mesmo que o primeiro exemplo.
EDITAR
Como mencionado por @MikeFHay nos comentários:
Prefiro usar o Guava's Splitter , pois possui padrões mais saudáveis (veja a discussão acima sobre partidas vazias sendo cortadas por String#split()
, então fiz:
Splitter.on(Pattern.compile(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)"))
String line = "equals: =,\"quote: \"\"\",\"comma: ,\""
tudo o que você precisa fazer é retirar as aspas duplas estranhas personagens.-1
ao param método split:line.split(regex, -1)
. Veja: docs.oracle.com/javase/6/docs/api/java/lang/…Splitter.on(Pattern.compile(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)"))
.findAllIn("(?s)(?:\".*?\"|[^\",]*)*")
em combinação com uma etapa de pós-processamento para pular o primeiro campo (sempre vazio) após cada campo não vazio.Embora eu goste de expressões regulares em geral, para esse tipo de tokenização dependente do estado, acredito que um analisador simples (que neste caso é muito mais simples do que essa palavra possa parecer) é provavelmente uma solução mais limpa, principalmente no que diz respeito à manutenção , por exemplo:
Se você não se preocupa em preservar as vírgulas entre aspas, pode simplificar essa abordagem (sem manipulação do índice inicial, sem o caso especial de último caractere ) substituindo suas vírgulas entre aspas por outra coisa e depois dividindo em vírgulas:
fonte
http://sourceforge.net/projects/javacsv/
https://github.com/pupi1985/JavaCSV-Reloaded (bifurcação da biblioteca anterior que permitirá que a saída gerada tenha terminadores de linha do Windows
\r\n
quando não estiver executando o Windows)http://opencsv.sourceforge.net/
API CSV para Java
Você pode recomendar uma biblioteca Java para ler (e possivelmente gravar) arquivos CSV?
Java lib ou aplicativo para converter CSV para arquivo XML?
fonte
Eu não recomendaria uma resposta regex de Bart, acho a solução de análise melhor neste caso específico (como Fabian propôs). Eu tentei a solução regex e a própria implementação de análise, descobri que:
Minha solução e teste abaixo.
É claro que você pode alterar a opção para else-ifs neste trecho se não se sentir à vontade com a feiura. Observe a falta de interrupção após o interruptor com separador. Em vez disso, o StringBuilder foi escolhido como StringBuffer por design para aumentar a velocidade, onde a segurança do thread é irrelevante.
fonte
-1
ao método de divisão na resposta de Bart, você pegará cadeias vazias (incluindo cadeias vazias após a última vírgula):line.split(regex, -1)
Tente um lookaround como
(?!\"),(?!\")
. Isso deve corresponder ao,
que não está cercado"
.fonte
(?<!"),(?!")
, mas ainda não vai funcionar. Dada a sequênciaone,two,"three,four"
, ela corresponde corretamente à vírgulaone,two
, mas também corresponde à vírgula"three,four"
e falha ao corresponder umatwo,"three
.Você está naquela área de fronteira irritante onde regexps quase não funciona (como apontado por Bart, escapar das aspas dificultaria a vida), e ainda assim um analisador completo parece um exagero.
Se você provavelmente precisar de maior complexidade em breve, procurarei uma biblioteca de analisadores. Por exemplo, este
fonte
Fiquei impaciente e optei por não esperar por respostas ... para referência, não parece tão difícil fazer algo assim (que funciona para o meu aplicativo, não preciso me preocupar com aspas escapadas, como as coisas entre aspas está limitado a algumas formas restritas):
(exercício para o leitor: estenda o tratamento de aspas escapadas, procurando também por barras invertidas.)
fonte
A abordagem mais simples é não combinar delimitadores, ou seja, vírgulas, com uma lógica adicional complexa para corresponder ao que realmente é pretendido (os dados que podem ser citados), apenas para excluir delimitadores falsos, mas, em primeiro lugar, corresponder aos dados pretendidos.
O padrão consiste em duas alternativas, uma string entre aspas (
"[^"]*"
ou".*?"
) ou tudo até a próxima vírgula ([^,]+
). Para suportar células vazias, precisamos permitir que o item não citado fique vazio e consumir a próxima vírgula, se houver, e usar a\\G
âncora:O padrão também contém dois grupos de captura para obter o conteúdo da sequência de caracteres citada ou o conteúdo simples.
Em seguida, com o Java 9, podemos obter uma matriz como
enquanto as versões Java mais antigas precisam de um loop como
A adição dos itens a uma
List
ou a uma matriz é deixada como um imposto especial de consumo para o leitor.No Java 8, você pode usar a
results()
implementação desta resposta , para fazê-lo como a solução Java 9.Para conteúdo misto com sequências incorporadas, como na pergunta, você pode simplesmente usar
Mas então, as cadeias são mantidas em sua forma citada.
fonte
Em vez de usar lookahead e outras regex loucas, basta puxar as aspas primeiro. Ou seja, para cada agrupamento de cotações, substitua esse agrupamento por
__IDENTIFIER_1
ou algum outro indicador e mapeie esse agrupamento para um mapa de sequência, sequência.Depois de dividir por vírgula, substitua todos os identificadores mapeados pelos valores da sequência original.
fonte
que tal um one-liner usando String.split ()?
fonte
Eu faria algo assim:
fonte