2010-09-19 46 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