TSVの読込み

昔作成して公開している CSVの読み書き、
 GitHub - yipuran/yipuran-csv: Java CSV read and write
これは、TSV ファイルで使用することを目的にしていなかった。
長いこと開発をしていて、なかなかTSVファイルを対象にすることが少なかった。
この yipuran-csv の中の、Csvprocess と同じ機能で、TSV読込みは、継承をすることで
次のように用意できる。
 Csvprocess の説明は、→ こちらのWiki
継承して、TSV用に用意するポイントは、

  • 内部で使用する org.yipuran.csv4j.CSVReader のインスタンス生成で、デリミタをカンマ区切りからタブ区切りにすること

後は、Csvprocess と同じメソッドの仕様で使える。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.yipuran.csv.BOMfunction;
import org.yipuran.csv.Csvprocess;
import org.yipuran.csv4j.CSVReader;
import org.yipuran.csv4j.ParseException;
import org.yipuran.csv4j.ProcessingException;
/**
 * Tsvprocess : Csvprocessを継承したTSV読込クラス
 */
public class Tsvprocess extends Csvprocess{
    private boolean blankIsNull = false;
    private static final char DELIMITER = '\t';
    public Tsvprocess(){}
    
    public Tsvprocess(boolean blankIsNull) {
        this.blankIsNull = blankIsNull;
    }
    /**
     * ヘッダ有りTSV読込み実行.
     * @param inReader InputStreamReader
     * @param header ヘッダ行 Consumer
     * @param processor コンテンツ行BiConsumer、TSV行読込みカウント(1始まり)とTSV文字列のList
     * @throws IOException
     * @throws ProcessingException
     */
    @Override
    public void read(InputStreamReader inReader, Consumer<List<String>> header, BiConsumer<Integer, List<String>> processor) 
    throws IOException, ProcessingException{
        CSVReader reader = new CSVReader(new BufferedReader(inReader),
                                         Charset.forName(inReader.getEncoding()), 
                                         DELIMITER,
                                         getComment(),
                                         blankIsNull);
        try{
            int lineCount = 0;
            while(true){
                try{
                    List<String> fields = reader.readLine();
                    if (fields.size()==0) break;
                    if (isHasHeader() && lineCount==0){
                        String rep = fields.get(0);
                        if (BOMfunction.match(rep)) {
                            fields.remove(0);
                            fields.add(0, BOMfunction.chop(rep));
                        }
                        header.accept(fields);
                    }else{
                        processor.accept(lineCount, fields);
                    }
                }catch(Exception e){
                    throw new ProcessingException(e, reader.getLineNumber());
                }
                lineCount++;
            }
        }finally{
            reader.close();
        }
    }
    /**
     * ヘッダ無しTSV読込み実行.
     * @param inReader InputStreamReader
     * @param processor BiConsumer 行のindexとTSV文字列のList
     * @throws IOException
     * @throws ProcessingException
     * @throws ParseException
     */
    @Override
    public void readNoheader(InputStreamReader inReader, BiConsumer<Integer, List<String>> processor)
    throws IOException, ProcessingException, ParseException{
        CSVReader reader = new CSVReader(new BufferedReader(inReader),
                                         Charset.forName(inReader.getEncoding()),
                                         DELIMITER,
                                         getComment(),
                                         blankIsNull);
        try{
            int lineIndex = 0;
            while(true){
                try{
                    List<String> fields = reader.readLine();
                    if (fields.size()==0) break;
                    if (lineIndex==0){
                        String rep = fields.get(0);
                        if (BOMfunction.match(rep)) {
                            fields.remove(0);
                            fields.add(0, BOMfunction.chop(rep));
                        }
                    }
                    processor.accept(lineIndex, fields);
                }catch(Exception e){
                    throw new ProcessingException(e, reader.getLineNumber());
                }
                lineIndex++;
            }
        }finally{
            reader.close();
        }
    }

    /**
     * ヘッダ有りTSV読込み実行(Map形式読込み).
     * <PRE>
     * ヘッダ行列をキーとして読込み結果をMapで実行
     * </PRE>
     * @param inReader InputStreamReader
     * @param processor コンテンツ行BiConsumer、TSV行読込みカウント(1始まり)とヘッダのキーに対するコンテンツ行の値のMap
     * @throws IOException
     * @throws ProcessingException
     */
    @Override
    public void read(InputStreamReader inReader, BiConsumer<Integer, Map<String, String>> processor)
    throws IOException, ProcessingException{
        CSVReader reader = new CSVReader(new BufferedReader(inReader),
                                         Charset.forName(inReader.getEncoding()),
                                         DELIMITER,
                                         getComment(),
                                         blankIsNull);
        try{
            Map<Integer, String> headerMap = new HashMap<>();
            int lineCount = 0;
            while(true){
                try{
                    List<String> fields = reader.readLine();
                    if (fields.size()==0) break;
                    if (isHasHeader() && lineCount==0){
                        String rep = fields.get(0);
                        if (BOMfunction.match(rep)) {
                            fields.remove(0);
                            fields.add(0, BOMfunction.chop(rep));
                        }
                        int i = 0;
                        for(String key:fields){
                            headerMap.put(i, key);
                            i++;
                        }
                    }else{
                        processor.accept(lineCount,
                            Stream.iterate(0, i->i+1).limit(fields.size())
                            .collect(HashMap<String, String>::new, 
                                     (r, t)->r.put(headerMap.get(t), fields.get(t)), (r, t)->{})
                        );
                    }
                }catch(Exception e){
                    throw new ProcessingException(e, reader.getLineNumber());
                }
                lineCount++;
            }
        }finally{
            reader.close();
        }
    }
}

この Tsvprocess を使用するサンプルは、、、
文字コードUTF-8 で1行目はヘッダ行で読込み
↓ サンプル無いの in変数は、InputStream

Tsvprocess process = new Tsvprocess();
process.read(in, StandardCharsets.UTF_8
, h->{
    // h=List<String> ヘッダ行
}, (i, p)->{
    // i=TSV行番号:1始まり、 p=1行の List<String>
});

文字コードUTF-8 でヘッダ無しで読込み
↓ サンプル無いの in変数は、InputStream

Tsvprocess process = new Tsvprocess();
process.readNoheader(in, StandardCharsets.UTF_8, (n, p)->{
    // n=行番号(0始まり、インデックスとしてのカウント) p=1行の List<String>
});

文字コードUTF-8 で1行目はヘッダ行で、マップのキーとして読み込む

Tsvprocess process = new Tsvprocess(true);
process.read(in, StandardCharsets.UTF_8, (n, m)->{
    // n=行番号(1始まり、ヘッダを除くため) m=1行の Map<String, String>
    m.entrySet().stream().forEach(e->{
        // e.getKey()=ヘッダキー e.getValue()=値
    });
});