2011-10-17 28 views
18

tengo csv que viene con formato:Analizar CSV con comillas dobles en algunos casos

a1, a2, a3, "a4, a5", a6

Sólo campo con, tendrá cotizaciones

Usando Java, ¿cómo analizar esto fácilmente? Intento evitar el uso del analizador de código abierto CSV como política de la compañía. Gracias.

+0

No hay idea de que sea fácil, CSV tiene algunos casos de bordes fiddly: comillas de escape: utilizando varios estilos no menos; y nuevas líneas en los valores de campo: diversión si tiene que informar errores con la línea CSV en la que se produjeron. Si no puede usar un analizador existente y podría tener que lidiar con esto, escriba un analizador. (Lo cual también es divertido de hacer si no tiene permitido un generador de analizador.) – millimoose

+2

si la compañía no solicita libs de código abierto {independientemente de la licencia) y necesita ayuda con un análisis simple ... – bestsss

+0

@Inerdia, the El análisis es alrededor de 30 líneas de código escrito a mano, sin necesidad de generador. – bestsss

Respuesta

21

Usted podría utilizar Matcher.find con la siguiente expresión regular:

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

Aquí hay un ejemplo más 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)); 
} 

ver su funcionamiento en línea: ideone

+0

De manera más general, en un archivo CSV se incluye un valor entre comillas en cuanto contiene el separador, una línea nueva y/o comillas ... – mousio

+0

@Mark, las comillas dobles ("") se utilizan para representar a un solo ". Además, usar regExp está más allá de 0kLL – bestsss

+2

Esto no funciona bien porque agrega cadena vacía entre los elementos y esto crea un problema si hay celdas vacías en el csv. – m3th0dman

3

me encontré con este mismo problema (pero en Python), una manera que encontré para resolverlo, sin expresiones regulares, fue: Cuando consigues la línea, busca cualquier cotización, si re son comillas, divida la cadena entre comillas y divida los resultados indizados par de la matriz resultante en comas. Las cadenas indexadas impares deben ser los valores cotizados completos.

No soy un codificador de Java, por lo que toman esto como pseudocódigo ...

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(',') 
    } 

otra alternativa es utilizar una expresión regular.

3

Aquí hay un código para usted, espero que usar código fuera de aquí no cuente código abierto, que es.

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

El siguiente código parece funcionar bien y puede manejar las comillas entre comillas.

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; 
} 
Cuestiones relacionadas