尽快通过java读取数百万行的csv文件

Joe*_*rey 15 java csv

我想读取包含数百万行的csv文件,并使用我的决策树算法的属性.我的代码如下:

String csvFile = "myfile.csv";
List<String[]> rowList = new ArrayList();
String line = "";
String cvsSplitBy = ",";
String encoding = "UTF-8";
BufferedReader br2 = null;
try {
    int counterRow = 0;
    br2 =  new BufferedReader(new InputStreamReader(new FileInputStream(csvFile), encoding));
    while ((line = br2.readLine()) != null) { 
        line=line.replaceAll(",,", ",NA,");
        String[] object = line.split(cvsSplitBy);
        rowList.add(object); 
        counterRow++;
    }
    System.out.println("counterRow is: "+counterRow);
    for(int i=1;i<rowList.size();i++){
        try{
           //this method includes many if elses only.
           ImplementDecisionTreeRulesFor2012(rowList.get(i)[0],rowList.get(i)[1],rowList.get(i)[2],rowList.get(i)[3],rowList.get(i)[4],rowList.get(i)[5],rowList.get(i)[6]); 
        }
        catch(Exception ex){
           System.out.printlnt("Exception occurred");   
        }
    }
}
catch(Exception ex){
    System.out.println("fix"+ex);
}
Run Code Online (Sandbox Code Playgroud)

当csv文件的大小不大时,它工作正常.但是,确实很大.因此我需要另一种方法来更快地读取csv.有什么建议吗?赞赏,谢谢.

lau*_*une 10

在这个片段中,我看到两个问题会让你大大减慢速度:

while ((line = br2.readLine()) != null) { 
    line=line.replaceAll(",,", ",NA,");
    String[] object = line.split(cvsSplitBy);
    rowList.add(object); 
    counterRow++;
}
Run Code Online (Sandbox Code Playgroud)

首先,rowList以默认容量开始,并且必须多次增加,总是导致旧的底层数组的副本为新的.

然而,更糟糕的是,数据过度爆炸成String []对象.只有在为该行调用ImplementDecisionTreeRulesFor2012时才需要列/单元格- 当您读取该文件并处理所有其他行时,并不是所有时间.将分组(或更好的内容,如注释所示)移动到第二行.

(即使你能负担得起内存,创建许多对象也很糟糕.)

也许最好在阅读"百万"时调用ImplementDecisionTreeRulesFor2012?它会完全避免使用rowList ArrayList.

稍后 推迟拆分可将1000万行的执行时间从1m8.262s(当程序用完堆空间时)减少到13.067s.

如果您在调用Implp ... 2012之前没有被强制读取所有行,则时间减少到4.902s.

最后写下拆分并手工更换:

String[] object = new String[7];
//...read...
    String x = line + ",";
    int iPos = 0;
    int iStr = 0; 
    int iNext = -1;
    while( (iNext = x.indexOf( ',', iPos )) != -1 && iStr < 7 ){
        if( iNext == iPos ){
            object[iStr++] = "NA";
        } else {
             object[iStr++] = x.substring( iPos, iNext );
        }
        iPos = iNext + 1;
    }
    // add more "NA" if rows can have less than 7 cells
Run Code Online (Sandbox Code Playgroud)

将时间缩短到1.983秒.这比原始代码快30倍,原始代码无论如何都会遇到OutOfMemory.


Jer*_*kes 7

只需使用uniVocity-parsers的CSV解析器,而不是尝试构建自定义解析器.您的实现可能不会快速或灵活,无法处理所有极端情况.

它具有极高的内存效率,您可以在不到一秒的时间内解析一百万行.此链接具有许多java CSV库和univocity-parsers的性能比较.

这是一个如何使用它的简单示例:

CsvParserSettings settings = new CsvParserSettings(); // you'll find many options here, check the tutorial.
CsvParser parser = new CsvParser(settings);

// parses all rows in one go (you should probably use a RowProcessor or iterate row by row if there are many rows)
List<String[]> allRows = parser.parseAll(new File("/path/to/your.csv"));
Run Code Online (Sandbox Code Playgroud)

但是,它将所有内容加载到内存中.要流式传输所有行,您可以执行以下操作:

String[] row;
parser.beginParsing(csvFile)
while ((row = parser.parseNext()) != null) {
    //process row here.
}
Run Code Online (Sandbox Code Playgroud)

更快的方法是使用RowProcessor,它还提供了更大的灵活性:

settings.setRowProcessor(myChosenRowProcessor);
CsvParser parser = new CsvParser(settings);
parser.parse(csvFile);
Run Code Online (Sandbox Code Playgroud)

最后,它有内置例程,使用解析器执行一些常见任务(迭代java bean,dump ResultSets等)

这应该涵盖基础知识,检查文档以找到适合您案例的最佳方法.

披露:我是这个图书馆的作者.它是开源和免费的(Apache V2.0许可证).

  • 我尝试使用它,但加载 csv 文件需要花费大量时间。 (2认同)