2010-09-19 63 views
0

我試圖創建一個簡單的類來讀取csv文件和內容存儲在一個創建一個通用CsvReader

ArrayList<ArrayList<T>>. 

我創建一個通用類CsvReader,這樣我可以處理不同的數據類型:int,double,String。如果我有,說,雙打的csv文件,我想象我會用我的課是這樣的:

//possible method 1 
CsvReader<Double> reader = new CsvReader<Double>(); 
ArrayList<ArrayList<Double>> contents = reader.getContents(); 

//possible method 2 
CsvReader reader = new CsvReader(Double.class); 
ArrayList<ArrayList<Double>> contents = reader.getContents(); 

但方法1不工作,因爲類型擦除阻止你寫像

rowArrayList.add(new T(columnStringValue)); 
代碼

但我甚至無法在Double.class解決方案中傳遞。問題是,真正發生的事情是我需要我的類「參數化」(在這個詞的一般意義上,而不是技術上的java泛型意義上),它具有以下屬性:它具有接受單個字符串參數的類型。也就是說,創建,比如說,一個雙csv文件行的ArrayList,我需要寫:

StringTokenizer st = new StringTokenizer(line,","); 
ArrayList<Double> curRow = new ArrayList<Double>(); 
while (st.hasMoreTokens()) { 
curRow.add(new Double(st.nextToken()); 
} 

在Double.class已經過去了,我可以用得到它的字符串構造函數

Constructor ctor = c.getConstructor(new Class[] {String.class}); 

但這有兩個問題。最重要的是,這是一個普通的構造函數,它將返回一個Object類型,然後我不能將其轉換爲Double。其次,我會缺少「類型」檢查的事實,我要求我的類傳入一個字符串參數構造函數。

我的問題是:我該如何正確地實現這個通用CsvReader?

感謝, 約拿

回答

7

我不知道一個通​​用的CSV讀者將這個簡單的使用(和創建,順便說一句)。

我想到的第一個問題是:如果CSV包含三列:首先是整數,然後是字符串,最後是日期?你將如何使用你的通用CSV閱讀器?

無論如何,讓我們假設您想創建一個CSV閱讀器,其中所有列都是相同類型的。正如你所說的,你不能對類型「」接受String作爲構造函數「的類進行參數化。 Java只是不允許。使用反射的解決方案是一個好的開始。但是如果你的班級沒有在其構造函數之一中使用String作爲參數呢?

在這裏,您可以選擇一個解析器,它將採用您的字符串並返回正確類型的對象。創建一個通用的接口,使您要抓取的類型一些實現:

public interface Parser<T> { 

    T parse(String value); 

} 

,然後執行:

public class StringParser implements Parser<String> { 

    public String parse(String value) { 
     return value; 
    } 

} 

然後,CSV讀者可以採取Parser作爲它的一個參數。然後,它可以使用此解析器將每個String轉換爲Java對象。

有了這個解決方案,你可以擺脫不那麼漂亮的反射你在哪裏使用。你可以轉換爲任何類型,你只需要實現一個Parser

你的讀者會是這樣的:

public CSVReader<T> { 

    Parser<T> parser; 

    List<T> getValues() { 
     // ... 
    } 

} 

現在,回到在那裏一個CSV文件可以有多個類型,只是提高你的讀者有點問題。您只需要一個解析器列表(每列一個),而不是解析所有列的解析器列表。

。希望:-)

+0

謝謝!我認爲這將工作得很好。 – Jonah 2010-09-19 18:58:58

+0

@Jonah,如果它適合你,你應該接受答案;) – 2010-09-19 19:55:38

+0

Colin,我該怎麼做?我沒有看到任何「接受」按鈕.... – Jonah 2010-09-19 20:08:49

1

幫助,如果你正在嘗試做的實際工作中,我建議你忘了,並使用Scanner

如果您正在試驗:我會讓CsvReader一個抽象類:

public abstract class CsvReader<T> { 
... 
    // This is what you use in the rest of CsvReader 
    // to create your objects from the strings in the CSV 
    protected abstract T parse(String s); 
... 
} 

而且,它還將被用作:

CsvReader<Double> = new CsvReader<Double>() { 
    @Override protected Double parse(String s) { 
     return Double.valueOf(s); 
    } 
}; 
... 

不是完美的,但合理的。


編輯:事實證明,你可以有你的方式,雖然看起來有點hackish。請參閱Super Type Tokens。它基本上涉及包括在CsvReader中的Super Type Tokens鏈接中顯示的邏輯,以具有與你的元素類相對應的類對象。

+0

+1爲java.util.Scanner類我不知道,需要它近9000次。 – Ither 2010-09-19 19:15:31

+0

是的。至少我會擺脫'StringTokenizer'並使用'String.split()'代替。 – gpeche 2010-09-19 19:54:22

+0

你們是否應該使用Scanner來實現分解文本數據並將其放入ArrayLists的代碼?另外,出於好奇,String.split()優於StringTokenizer的優點是什麼? – Jonah 2010-09-19 20:25:07

1

創建正確 CVS閱讀器可能比您想象的更困難。例如,在您的代碼示例中,在以下情況下它將無法正常工作。

「微軟,公司」,1,2,3

取而代之的4場,你會得到5場的基礎上

StringTokenizer st = new StringTokenizer(line,","); 

我的建議是什麼,使用第三方庫實現。例如

http://opencsv.sourceforge.net/

我用它在我的應用程序之一,我的應用程序已經運行了3年。到現在爲止還挺好。

+0

感謝您的鏈接 – Jonah 2010-09-21 14:01:38

0

我曾經需要閱讀存儲在CSV文件單元格中的字符串列表,並開始搜索Java解決方案。我發現大多數開源CSV閱讀器對我的目的來說是不必要的複雜。 (請參閱https://agiletribe.wordpress.com/2012/11/23/the-only-class-you-need-for-csv-files/進行全面審查)。 最後我發現MKYong的代碼非常有效。爲了讀取整個CSV或TSV文件的目的,我必須調整它,並將其作爲列表列表返回。內部列表中的每個元素表示CSV的一個單元格。代碼以及信譽到MKYong可以在: https://github.com/ramanraja/CsvReader