我正在尝试创建一个简单的类来读取csv文件并将内容存储在
ArrayList<ArrayList<T>>. 我正在创建一个泛型类CsvReader,这样我就可以处理不同类型的数据:整型、双精度型、字符串。比方说,如果我有一个包含doubles的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泛型的技术意义),它有一个接受单个字符串参数的ctor。也就是说,要在双csv文件上创建行ArrayLists,我需要这样写:
StringTokenizer st = new StringTokenizer(line,",");
ArrayList<Double> curRow = new ArrayList<Double>();
while (st.hasMoreTokens()) {
curRow.add(new Double(st.nextToken());
}传入Double.class后,我可以使用以下命令获取它的字符串ctor
Constructor ctor = c.getConstructor(new Class[] {String.class});但这有两个问题。最重要的是,这是一个通用构造函数,它将返回一个类型对象,然后我不能将其向下转换为双精度类型。其次,如果我要求传入的类必须有一个String arg构造函数,那么我就会错过"type“检查。
我的问题是:我如何正确地实现这个通用的CsvReader?
谢谢你,乔纳
发布于 2010-09-20 02:54:52
我不确定一个通用的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文件可以有多种类型的问题,只需改进一下您的读者即可。您只需要一个解析器列表(每列一个),而不是一个解析所有列的解析器。
希望这能有所帮助:-)
发布于 2010-09-20 03:03:54
如果你正在尝试做真正的工作,我建议你忘记这一点,使用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);
}
};
...不是很完美,但很合理。
编辑:事实证明你可以随心所欲,尽管它看起来有点老土。参见Super Type Tokens。它基本上包括在CsvReader中的超级类型令牌链接中显示的逻辑,以使对应于element类的类对象可用。
发布于 2010-09-20 17:33:01
创建一个正确的阅读器可能比你想象的要困难得多。例如,在您的代码示例中,它在以下情况下将无法正常工作。
“微软公司”,1,2,3
您将获得的不是4个字段,而是5个基于
StringTokenizer st = new StringTokenizer(line,",");我的建议是,使用第三方库实现。例如
http://opencsv.sourceforge.net/
我在我的一个应用程序中使用它,我的应用程序已经运行了3年。到目前一切尚好。
https://stackoverflow.com/questions/3746922
复制相似问题