2013-03-04 57 views
4

我有,有一些報價問題的CSV文件:Java的CSV解析器使用轉義引號

"Albanese Confectionery","157137","ALBANESE BULK ASST. MINI WILD FRUIT WORMS 2" 4/5LB",9,90,0,0,0,.53,"21",50137,"3441851137","5 lb",1,4,4,$6.7,$6.7,$26.8 

SuperCSV是這些水果蟲窒息(雙關語意)。我知道2"應該可能是2"",但事實並非如此。 LibreOffice實際上對此進行了正確的解析(讓我感到驚訝)。我想的只是寫我自己的小解析器,但其他行有這樣的字符串內的逗號:

"Albanese Confectionery","157230","ALBANESE BULK JET FIGHTERS,ASSORTED 4/5 B",9,90,0,0,0,.53,"21",50230,"3441851230","5 lb",1,4,4,$6.7,$6.7,$26.8 

有誰知道一個Java庫,將處理瘋狂的事情是這樣的?或者我應該嘗試所有可用的?還是我最好自己砍掉這個?

+1

我會說你自己破解了這個,一般來說,當一個字符串應該結束時,實際上是不可能的,但是如果沒有至少知道該行應該看起來怎麼樣,那就不可能。LibreOffice的可能正確地分析這一點,可是你知道這只是服用猜測出現這種情況是適合你的輸入。 – millimoose 2013-03-04 21:14:09

回答

6

正確的解決方案是找到生成數據的人,並用鍵盤在頭部擊敗他們,直到他們解決問題爲止。

一旦你完成了這條路線,你可以嘗試一些其他的CSV解析器在市場上,我已經使用OpenCSV成功在過去。

即使OpenCSV不能解決開箱即用的問題,該代碼相當容易閱讀,並且可以在Apache許可證下使用,因此可能會修改算法以處理您不可靠的數據,並且可能比從頭開始容易。

+0

+1。發電機需要產生輸出,使用標準CSV公約之一正確逃脫報價:CSV使用'「的一些變種」'和其他人使用'\「'這不是合理的,要求人們只是假設一個雙引號後面沒有通過記錄或行分隔符是帶引號的,因爲這使你的CSV方言既容易產生歧義和不完整的。 – 2013-03-04 21:15:20

+0

我試圖OpenCSV以及和它沒有工作。所以我想一個正則表達式來找出流氓的報價和他們翻倍。 – Hut8 2013-03-04 21:28:14

+1

@ bowenl2'「'([,$]?!)(也許 - 不記得,如果'$'是特內裏'[]'我只是不使用正則表達式和代碼編寫了這一點。)如果該惡意引號後面跟着逗號,意味着該字段內容的一部分,這也不起作用。 (?!):| – millimoose 2013-03-04 21:43:47

1

即使我自己在這裏也很驚訝,但我想我會自己破解它。我的意思是,你只需要通過分開引號/逗號來讀取行並生成標記,無論你想要什麼。這樣你就可以按照它的方式調整邏輯。這不是很難。該文件似乎被打破,以至於通過一些現有的解決方案似乎更多的工作。

但有一點 - 如果LibreOffice已經正確解析它,你能不能從那裏保存文件,從而生成一個更合理的文件。但是,如果您認爲LibreOffice可能正在猜測,請自行編寫標記器。

1

+1對於「窒息果蟲」的雙關語 - 我在我的咖啡讀數:)

如果你真的不能拿到CSV固定的,那麼你可以只提供自己的標記生成器(差點窒息Super CSV非常靈活!)。

通常情況下,您會自己編寫readColumns()實現,但擴展默認Tokenizer並覆蓋readLine()方法可更快地攔截String(並修復未轉義的引號),然後對其進行標記。

我在這裏做了一個假設,任何不在分隔符旁邊或行尾或行尾的引號都應該被轉義。它遠非完美,但它適用於您的示例輸入。你可以實現這一點,但你喜歡 - 這是早晨對我來說,使用正則表達式:)

這樣你不必修改超級CSV(它只是插入),所以你得到所有其他功能,如單元處理器和bean映射也是如此。

package org.supercsv; 
import java.io.IOException; 
import java.io.Reader; 
import org.supercsv.io.Tokenizer; 
import org.supercsv.prefs.CsvPreference; 

public class FruitWormTokenizer extends Tokenizer { 

    public FruitWormTokenizer(Reader reader, CsvPreference preferences) { 
    super(reader, preferences); 
    } 

    @Override 
    protected String readLine() throws IOException { 
    final String line = super.readLine(); 
    if (line == null) { 
     return null; 
    } 

    final char quote = (char) getPreferences().getQuoteChar(); 
    final char delimiter = (char) getPreferences().getDelimiterChar(); 

    // escape all quotes not next to a delimiter (or start/end of line) 
    final StringBuilder b = new StringBuilder(line); 
    for (int i = b.length() - 1; i >= 0; i--) { 
     if (quote == b.charAt(i)) { 
     final boolean validCharBefore = i - 1 < 0 
      || b.charAt(i - 1) == delimiter; 
     final boolean validCharAfter = i + 1 == b.length() 
      || b.charAt(i + 1) == delimiter; 
     if (!(validCharBefore || validCharAfter)) { 
      // escape that quote! 
      b.insert(i, quote); 
     } 
     } 
    } 
    return b.toString(); 
    } 
} 

您可以將此Tokenizer提供給您的CsvReader的構造函數。