2011-10-17 18 views
18

私はフォーマットが付属していCSV持っている:引用符を持つことになり、解析CSVは

のみフィールドと

A6

A1、A2、A3、 "A4、A5" を、

Javaを使用すると、これを簡単に解析する方法は?私は会社のポリシーとしてオープンソースのCSVパーサーを使用しないようにしています。ありがとう。

+0

簡単にはわかりませんが、CSVにはいくつかの偽のエッジケースがあります。エスケープされた引用符 - いくつかのスタイルを使用します。フィールド値の改行 - 発生したCSV行でエラーを報告する必要がある場合は面白いです。既存のパーサーを使用できず、これらを処理する必要がある場合は、パーサーを作成します。 (パーザジェネレータが許可されていないと、楽しいことです) – millimoose

+2

会社がオープンソースのlibs {ライセンスに関係なく}を要求していて、簡単な構文解析で助けが必要な場合... – bestsss

+0

@Inerdia、解析は、手書きコードの約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

@マーク、二重引用符( "")を使用して1つの表現を表します。さらに、regExpを使用すると余計になります – bestsss

+2

これは要素間に空の文字列を追加し、 csvに空のセルがあると問題が発生します。 – m3th0dman

3

私はこの同じ問題に出くわしました(でもPythonで)、正規表現なしでそれを解決する方法の1つが見つかりました: あなたが行を取得したら、 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; 
} 
関連する問題