2011-10-17 54 views
18

我有CSV自帶的格式:解析CSV,在某些情況下,雙引號

A1,A2,A3, 「A4,A5」,A6

只有場,將有引號

使用Java,如何輕鬆解析這個?我儘量避免使用開源CSV解析器作爲公司政策。謝謝。

+0

沒有關於輕鬆的想法,CSV有一些棘手的邊緣情況:轉義報價 - 使用多個樣式不少;以及字段值中的換行符 - 如果您必須使用它們發生的CSV行來報告錯誤,則很有趣。如果您不能使用現有的解析器並且可能不得不處理這些解析器,請編寫解析器。 (如果你不允許解析器生成器,這也是很有趣的。) – millimoose

+2

如果公司要求沒有開源庫(無論是許可證),你需要幫助/一個簡單的解析... – bestsss

+0

@Inerdia,the解析是手寫代碼的30行左右,不需要生成器。 – bestsss

回答

21

你可以使用Matcher.find與下面的正則表達式:

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

下面是一個更完整的例子:

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)); 
} 

看到它聯機工作:ideone

+0

更一般地說,在CSV文件中,只要包含一個值,它就包含在引號中分隔符,換行符和/或引號... – mousio

+0

@Mark,雙引號(「」)用於表示單個「。另外,使用regExp已經超出了過度殺傷的效果 – bestsss

+2

這不起作用,因爲它在元素之間添加了空字符串,如果csv中有空單元格,則會產生問題。 – m3th0dman

3

我遇到同樣的問題來了(但在Python中),我發現解決它的一種方法,沒有正則表達式,是: 當你得到線,檢查任何引號,如果re是引號,將該字符串拆分爲引號,並將結果數組的偶數索引結果拆分爲逗號。奇數索引的字符串應該是完整的引用值。

我沒有Java編碼器,所以把這個作爲僞...

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

另外,使用正則表達式。

3

下面是一些適合你的代碼,我希望在這裏使用代碼不會計算開源,這是。

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

下面的代碼似乎工作正常,可以在引號內處理引號。

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