我正在尝试创建一个简单的类来读取csv文件并将内容存储在
中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泛型的意义上):它有一个接受单个String参数的ctor 。也就是说,要在Double csv文件上创建行ArrayLists,我需要写:
StringTokenizer st = new StringTokenizer(line,",");
ArrayList<Double> curRow = new ArrayList<Double>();
while (st.hasMoreTokens()) {
curRow.add(new Double(st.nextToken());
}
传入Double.class后,我可以使用
获取String ctor Constructor ctor = c.getConstructor(new Class[] {String.class});
但这有两个问题。最重要的是,这是一个将返回类型Object的通用构造函数,然后我无法将其转换为Double。其次,我将缺少“类型”检查,因为我要求我传入类中有一个String arg构造函数。
我的问题是:如何正确实现此通用CsvReader?
谢谢, 约拿
答案 0 :(得分:7)
我不确定通用的CSV阅读器是否会如此简单易用(顺便说一句,也可以创建)。
我想到的第一个问题是:如果CSV包含三列,那么该怎么办:首先是整数,然后是字符串,最后是日期?您将如何使用通用CSV阅读器?
无论如何,假设您要创建一个CSV阅读器,其中所有列都属于同一类型。正如您所说,您无法在“接受String
作为构造函数”的类型上对类进行参数化。 Java只是不允许这样做。使用反射的解决方案是一个良好的开端。但是如果你的类没有在其构造函数中使用String
作为参数呢?
在这里,你可以选择另一种方法:一个解析器,它将获取你的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文件可以有多种类型的问题,只需稍微改进您的阅读器。您只需要一个解析器列表(每列一个),而不是解析所有列的解析器。
希望有所帮助: - )
答案 1 :(得分: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中包含超类型标记链接中显示的逻辑,以便可以获得与您的元素类对应的类对象。
答案 2 :(得分:1)
创建正确 CVS阅读器可能比您想象的更困难。例如,在您的代码示例中,在以下情况下它将无法正常工作。
“Microsoft,Inc”,1,2,3
而不是4个字段,您将得到的是基于
的5个字段StringTokenizer st = new StringTokenizer(line,",");
我的建议是,使用第三方库实现。例如
http://opencsv.sourceforge.net/
我在我的一个应用程序中使用它,我的应用程序已经运行了3年。到目前为止一切都很好。
答案 3 :(得分: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