だいぶ前に作ったapache.commons.csvとapache.poiを使ったCSVファイルからエクセル出力処理で、
java.io.IOException: (line 3) invalid char between encapsulated token and delimiter
が発生していました。原因は、ダブルクォーテーションで括っているデータ項目の中にダブルクォーテーションが混ざっていたからなのですが、データを出力する側で対処するのが出来そうにないので、CSVファイルを読み込む所で何とかならんものかと検討。
CSVファイルを読む箇所は下記のようにInputStreamが渡され、CSVRecordリストを取得して色々やるという内容です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public static int parseCSV(InputStream input){ BufferedReader in = new BufferedReader(new InputStreamReader(input)); CSVParser parser = CSVFormat .EXCEL .withIgnoreEmptyLines(true) .withIgnoreSurroundingSpaces(false) .withRecordSeparator(System.getProperty("line.separator")) .withDelimiter(',') .withEscape('\\') .withQuote('"') .parse(in); List<CSVRecord> recordList = parser.getRecords(); //以下、略 |
殆どの場合で問題は発生しないので、出来れば失敗した場合のみリトライ出来ればいいかなと、データ内に混ざりこんだダブルクォーテーションをエスケープするようにしてみます。正規表現での変換を3段階かますことで、一部ケース(データ内にカンマとダブルクォーテーションが連続した場合)は問題ですが、今回はまあこの位で。。
次に失敗した後のフォローとなるので、読込が進んでしまっているInputStreamを何とかしなければなりません。結局、IOUtilsでテンポラリファイルにコピーしてから読み込むようにしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
public static int parseCSV(InputStream input){ // BufferedReader in = new BufferedReader(new InputStreamReader(input)); File fstream = File.createTempFile("csv",".txt"); FileOutputStream out = new FileOutputStream(fstream); IOUtils.copy(input, out); out.close(); List<CSVRecord> recordList; try(BufferedInputStream bf = new BufferedInputStream(new FileInputStream(fstream.getAbsoluteFile())); BufferedReader in = new BufferedReader(new InputStreamReader(bf));) { CSVParser parser = CSVFormat .EXCEL .withIgnoreEmptyLines(true) .withIgnoreSurroundingSpaces(false) .withRecordSeparator(System.getProperty("line.separator")) .withDelimiter(',') .withEscape('\\') .withQuote('"') .parse(in); recordList = parser.getRecords(); } catch(Exception e){ logger.error("retry parse tmpfile:" + fstream.getAbsoluteFile(),e ); BufferedInputStream bf = new BufferedInputStream(new FileInputStream(fstream.getAbsoluteFile())); BufferedReader in = new BufferedReader(new InputStreamReader(bf,"UTF-8")); StringBuilder sb = new StringBuilder(); String line; while ((line = in.readLine()) != null) { line.replaceAll("\\\\", ""); line = line.replaceAll("(?!(^|,|\",))\"(?!$|\")","\\\\\""); line = line.replaceAll(",\\\\\"",",\""); sb.append(line+System.getProperty("line.separator")); } CSVParser parser = CSVFormat .EXCEL .withIgnoreEmptyLines(true) .withIgnoreSurroundingSpaces(false) .withRecordSeparator(System.getProperty("line.separator")) .withDelimiter(',') .withEscape('\\') .withQuote('"') .parse(new StringReader(sb.toString())); recordList = parser.getRecords(); bf.close(); in.close(); } fstream.deleteOnExit(); //以下、略 |