2017-11-13 16 views
0

10万行以上のCSVファイルを解析しようとしていますが、パフォーマンス上の問題はファイルの最後まで到達さえせず、「スレッドのメイン」java.lang .OutOfMemoryError:GCオーバーヘッドの上限を超えました "遅いCSV行の解析と分割

改善や改善はありますか?

public static List<String[]> readCSV(String filePath) throws IOException{ 
    List<String[]> csvLine= new ArrayList<String[]>(); 
    CSVReader reader = new CSVReader(new FileReader(filePath), '\n'); 
    String[] row; 

    while((row = reader.readNext()) != null){ 
     csvLine.add(removeWhiteSpace(row[0].toString().split(","))); 
    } 

    reader.close(); 
    return csvLine; 
} 

private static String[] removeWhiteSpace(String[] split) { 
    for(int index =0; index < split.length;index++){ 
     split[index] = split[index].trim(); 
    } 
    return split; 
} 
+0

100,000行のデータセット全体をメモリにロードしようとしています。ヒープサイズをデータセットの予想サイズよりも大きくするか、プログラムを変更してすべてのデータを一度にロードしないようにします。 –

+0

プログラムにcsvファイル全体を保存しないでください。なぜCSVを読んでいるのですか?あなたはデータを読み込むことで何をするつもりですか? –

+0

@JimGarrisonもし私のヒープサイズかプログラムが問題だったのか分かりませんでした。ありがとう – Moley

答えて

1

まず、すべての行がリストに追加されているので、あなたはメモリが不足しています。

2番目は非常に遅いString.split()を使用しています。

この形式(デリミタ、引用符などのエスケープを処理する必要があります)の周りに多くのエッジケースがあるため、独自の解析コードを記述することでCSVを処理しないでください。

解決策は、univocity-parsersのようなライブラリを使用することです。あなたは1秒未満で100万行を読むことができるはずです。パースする

、ちょうどこの操作を行います。

public static IterableResult<String[], ParsingContext> readCSV(String filePath) { 
    File file = new File(filePath); 

    //configure the parser here. By default all values are trimmed 
    CsvParserSettings parserSettings = new CsvParserSettings(); 

    //create the parser 
    CsvParser parser = new CsvParser(parserSettings); 

    //create an iterable over rows. This will not load everything into memory. 
    IterableResult<String[], ParsingContext> rows = parser.iterate(file); 

    return rows; 
} 

は今、あなたは、このようにあなたの方法を使用することができます。

public static void main(String... args) { 
    IterableResult<String[], ParsingContext> rows = readCSV("c:/path/to/input.csv"); 

    try { 
     for (String[] row : rows) { 
      //process the rows however you want 
     } 
    } finally { 
     //the parser closes itself but in case any errors processing the rows (outside of the control of the iterator), close the parser. 
     rows.getContext().stop(); 
    } 
} 

これは、あなたがパーサーを使用する方法のほんの一例ですが、そこにありますそれを使用する多くの異なる方法。

今書き込みのために、あなたがこれを行うことができます:

public static void main(String... args) { 
    //this is your output file 
    File output = new File("c:/path/to/output.csv"); 

    //configure the writer if you need to 
    CsvWriterSettings settings = new CsvWriterSettings(); 

    //create the writer. Here we write to a file 
    CsvWriter writer = new CsvWriter(output, settings); 

    //get the row iterator 
    IterableResult<String[], ParsingContext> rows = readCSV("c:/temp"); 

    try { 
     //do whatever you need to the rows here 
     for (String[] row : rows) { 
      //then write it each one to the output. 
      writer.writeRow(row); 
     } 
    } finally { 
     //cleanup 
     rows.getContext().stop(); 
     writer.close(); 
    } 
} 

をあなたが望むすべては、データを読み、それを修正し、戻って別のファイルにそれを書くことであるならば、あなたはちょうどこの操作を行うことができます。

public static void main(String... args) throws IOException { 
    CsvParserSettings parserSettings = new CsvParserSettings(); 
    parserSettings.setProcessor(new AbstractRowProcessor() { 
     @Override 
     public void rowProcessed(String[] row, ParsingContext context) { 
      //modify the row data here. 
     } 
    }); 

    CsvWriterSettings writerSettings = new CsvWriterSettings(); 
    CsvRoutines routines = new CsvRoutines(parserSettings, writerSettings); 

    FileReader input = new FileReader("c:/path/to/input.csv"); 
    FileWriter output = new FileWriter("c:/path/to/output.csv"); 

    routines.parseAndWrite(input, output); 
} 

これが役に立ちます。

免責事項:私はこの本の著者です。オープンソースで無料(Apache 2.0ライセンス)です。

+0

ちょっと質問がありましたら、私は素早い質問をしています(素晴らしいbtwライブラリを使用してください) 私はCSV1の1行を解析し、それをCSV2の各行と比較しようとしています。 CSV1の次の行を繰り返してください。 これは、データをオブジェクトに取り込み、同じことを行うよりもコストがかかりますか? - CSV1は100万行になります – Moley

+1

最も簡単なことは、 '-Xms8G -Xmx8G'を使ってアプリケーションを実行し、メモリ。次に、両方のリストをメモリにソートし、比較を順次実行します。データが大きすぎてメモリに収まらない場合は、データベースを使用してデータを格納できます。そこから出る方法がない場合は、ファイルベースのアプローチにしてください。 –

-1

このような大きなファイルをメモリに保存しようとしましたか? あなたがやりたいことに応じて、処理された新しいファイルを書き込むか、その行をdbaに入れてください。 これは、最初に実装しています

FileInputStream inputStream = null; 
Scanner sc = null; 
try { 
    inputStream = new FileInputStream(path); 
    sc = new Scanner(inputStream, "UTF-8"); 
    while (sc.hasNextLine()) { 
     String line = sc.nextLine(); 
     // System.out.println(line); 
    } 
    // note that Scanner suppresses exceptions 
    if (sc.ioException() != null) { 
     throw sc.ioException(); 
    } 
} finally { 
    if (inputStream != null) { 
     inputStream.close(); 
    } 
    if (sc != null) { 
     sc.close(); 
    } 
} 
+0

これは新しいファイルの作成を実装する方法をどのように示していますか? –