2011-10-17 11 views
18

ho csv che viene fornito con il formato:Parse CSV con doppio apice in alcuni casi

a1, a2, a3, "A4, A5", A6

Solo campo con, avrà citazioni

Utilizzo di Java, come analizzare facilmente questo? Cerco di evitare l'utilizzo del parser CSV open source come criterio aziendale. Grazie.

+0

Nessuna idea su facilmente, CSV ha alcuni casi estremi: citazioni sfuggite - utilizzando diversi stili non meno; e newline nei valori dei campi: divertente se devi segnalare errori con la linea CSV in cui si sono verificati. Se non puoi usare un parser esistente e potresti doverli gestire, scrivi un parser. (Che è anche divertente da fare se non ti è consentito un generatore di parser.) – millimoose

+2

se la compagnia non richiede nessuna licenza open source {indipendentemente dalla licenza) e hai bisogno di aiuto con un semplice parse ... – bestsss

+0

@Inerdia, il l'analisi è di circa 30 righe di codice scritto a mano, senza bisogno di generatore. – bestsss

risposta

21

Si potrebbe usare Matcher.find con la seguente espressione regolare:

 
\s*("[^"]*"|[^,]*)\s* 

Ecco un esempio più completo:

String s = "a1, a2, a3, \"a4,a5\", a6"; 
Pattern pattern = Pattern.compile("\\s*(\"[^\"]*\"|[^,]*)\\s*"); 
Matcher matcher = pattern.matcher(s); 
while (matcher.find()) { 
    System.out.println(matcher.group(1)); 
} 

vederlo lavorare on-line: ideone

+0

Più in generale, in un file CSV un valore è racchiuso tra virgolette non appena contiene il separatore, una nuova riga e/o virgolette ... – mousio

+0

@Mark, le virgolette doppie ("") viene utilizzato per rappresentare un singolo. "Inoltre, l'uso di regExp non è eccessivo. – bestsss

+2

Questo non funziona bene perché aggiunge una stringa vuota tra gli elementi e questo crea un problema se ci sono celle vuote nel csv – m3th0dman

3

mi sono imbattuto in questo stesso problema (ma in Python), un modo che ho trovato per risolverlo, senza espressioni regolari, era: Quando si ottiene la linea, controllare eventuali virgolette, se il re sono le virgolette, dividere la stringa tra virgolette e dividere i risultati di indicizzazione pari dell'array risultante su virgole. Le stringhe indicizzate dispari dovrebbero essere i valori quotati completi.

Io non sono un programmatore Java, in modo da prendere questo come pseudocodice ...

line = String[]; 
    if ('"' in row){ 
     vals = row.split('"'); 
     for (int i =0; i<vals.length();i+=2){ 
      line+=vals[i].split(','); 
     } 
     for (int j=1; j<vals.length();j+=2){ 
      line+=vals[j]; 
     } 
    } 
    else{ 
     line = row.split(',') 
    } 

In alternativa, utilizzare una regex.

3

Ecco un codice per te, spero che l'utilizzo del codice non contenga l'open source, che è.

package bestsss.util; 

import java.io.BufferedReader; 
import java.io.IOException; 
import java.util.ArrayList; 
import java.util.List; 

public class SplitCSVLine { 
    public static String[] splitCSV(BufferedReader reader) throws IOException{ 
     return splitCSV(reader, null, ',', '"'); 
    } 

    /** 
    * 
    * @param reader - some line enabled reader, we lazy 
    * @param expectedColumns - convenient int[1] to return the expected 
    * @param separator - the C(omma) SV (or alternative like semi-colon) 
    * @param quote - double quote char ('"') or alternative 
    * @return String[] containing the field 
    * @throws IOException 
    */ 
    public static String[] splitCSV(BufferedReader reader, int[] expectedColumns, char separator, char quote) throws IOException{  
     final List<String> tokens = new ArrayList<String>(expectedColumns==null?8:expectedColumns[0]); 
     final StringBuilder sb = new StringBuilder(24); 

     for(boolean quoted=false;;sb.append('\n')) {//lazy, we do not preserve the original new line, but meh 
      final String line = reader.readLine(); 
      if (line==null) 
       break; 
      for (int i = 0, len= line.length(); i < len; i++) { 
       final char c = line.charAt(i); 
       if (c == quote) { 
        if(quoted && i<len-1 && line.charAt(i+1) == quote){//2xdouble quote in quoted 
         sb.append(c); 
         i++;//skip it 
        }else{ 
         if (quoted){ 
          //next symbol must be either separator or eol according to RFC 4180 
          if (i==len-1 || line.charAt(i+1) == separator){ 
           quoted = false; 
           continue; 
          } 
         } else{//not quoted 
          if (sb.length()==0){//at the very start 
           quoted=true; 
           continue; 
          } 
         } 
         //if fall here, bogus, just add the quote and move on; or throw exception if you like to 
         /* 
         5. Each field may or may not be enclosed in double quotes (however 
          some programs, such as Microsoft Excel, do not use double quotes 
          at all). If fields are not enclosed with double quotes, then 
          double quotes may not appear inside the fields. 
         */ 
         sb.append(c);     
        } 
       } else if (c == separator && !quoted) { 
        tokens.add(sb.toString()); 
        sb.setLength(0); 
       } else { 
        sb.append(c); 
       } 
      } 
      if (!quoted) 
       break;  
     } 
     tokens.add(sb.toString());//add last 
     if (expectedColumns !=null) 
      expectedColumns[0] = tokens.size(); 
     return tokens.toArray(new String[tokens.size()]); 
    } 
    public static void main(String[] args) throws Throwable{ 
     java.io.StringReader r = new java.io.StringReader("222,\"\"\"zzzz\", abc\"\" , 111 ,\"1\n2\n3\n\""); 
     System.out.println(java.util.Arrays.toString(splitCSV(new BufferedReader(r)))); 
    } 
} 
1

Il codice seguente sembra funzionare correttamente e può gestire le virgolette tra virgolette.

final static Pattern quote = Pattern.compile("^\\s*\"((?:[^\"]|(?:\"\"))*?)\"\\s*,"); 

public static List<String> parseCsv(String line) throws Exception 
{  
    List<String> list = new ArrayList<String>(); 
    line += ","; 

    for (int x = 0; x < line.length(); x++) 
    { 
     String s = line.substring(x); 
     if (s.trim().startsWith("\"")) 
     { 
      Matcher m = quote.matcher(s); 
      if (!m.find()) 
       throw new Exception("CSV is malformed"); 
      list.add(m.group(1).replace("\"\"", "\"")); 
      x += m.end() - 1; 
     } 
     else 
     { 
      int y = s.indexOf(","); 
      if (y == -1) 
       throw new Exception("CSV is malformed"); 
      list.add(s.substring(0, y)); 
      x += y; 
     } 
    } 
    return list; 
} 
Problemi correlati