apache.commons.csvでinvalid char between encapsulated token and delimiter

 だいぶ前に作ったapache.commons.csvとapache.poiを使ったCSVファイルからエクセル出力処理で、

java.io.IOException: (line 3) invalid char between encapsulated token and delimiter

 が発生していました。原因は、ダブルクォーテーションで括っているデータ項目の中にダブルクォーテーションが混ざっていたからなのですが、データを出力する側で対処するのが出来そうにないので、CSVファイルを読み込む所で何とかならんものかと検討。
 
 CSVファイルを読む箇所は下記のようにInputStreamが渡され、CSVRecordリストを取得して色々やるという内容です。

    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でテンポラリファイルにコピーしてから読み込むようにしました。

    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();
//以下、略