Help us understand the problem. What is going on with this article?

【Java】CrLfのみでreadLineする厳密改行ストリームリーダを実装した。

More than 3 years have passed since last update.

ハッキリ言って、百万回実装された猫だと思うけど。
ぼくも所用あって実装した事があるので、せっかくだからGitHubに上げてみた。

■概要:

◇背景

要件としては物凄くありがちな「CSVパーサを実装する必要が出て来た」事による。
ぼく個人としてはCSVと言うファイル形式は色々と面倒事が多いので好きじゃないので、
システム間IFとしてはXMLなりJSONなり、他の構造化文字列形式を採用して欲しいのだが、
残念ながら諸事情あって何が何でもCSVと言う事に。

◇独自実装する前に探してみた

要するに欲しいのはCSVパーサであって、出来ればそんな泥臭いものは自分でガリガリ書きたいモノではない。
という事で、使えそうなライブラリを探してみたのだが、どれもいまいちピンと来ない。

主に以下の点がネックになった。

  • CSVのエスケープ実装が弱い。
  • 一旦ファイルの全量をメモリに乗っけてしまう。
    (恐らく、内部的にScannerを使ってるとか、正規表現を使っているとかで、その辺の事情だと思う)

どうにかして実装済みのライブラリで済ませたい(自分で泥臭いコードは書きたくない)ので、結構頑張って探したんだけど、
要件を全てクリア出来るいいモノは結局見付からなかった。

◇実装要件

  • CSVに含まれるデータには改行(LF line-feed : \n)が含まれ、これをきちんと処理する必要がある。
  • レコードの区切りはCrLf、データ内改行はLfのみ、と言うのは仕様として確定している。
  • CSVファイルのデータ量はそこそこ大きく、全量メモリに展開するのはNG。ストリーム処理が出来る事が必須。

で、まぁテキストファイルを一行ずつ読み込んでって、
普通にインクォート処理しながらCSVのトークンパースしてくだけじゃん。
泥臭いけどまぁ楽勝楽勝。

って思ってたんですが、残念なことに BufferedReader#readLine() が肝心の CrLf と Lf を区別してくれない と言う事を知らず。

トークンパーサはすぐに出来たのに、肝心の行読み込みが上手く行かず、結局そっちを自作する必要がでた、という。

■実装コード:

コアロジック部分のみ抜粋。
関連する他クラスとかは省略してるので、全量はGitHubの方を見て下さい。
(コメントも全部削除してコードだけ乗っけてるので、コメント必要な方もそっち推奨)

CrLfReader#next
    public String next()
    {
        this.sb.setLength( 0 );

        if ( this.build() )
        {
            return this.sb.toString();
        }
        else
        {
            return null;
        }
    }
CrLfReader#build
    private boolean build()
    {
        if ( this.end )
        {
            return false;
        }
        else
        {
            this.readline( false );
            return true;
        }
    }
CrLfReader.Buffer
    private class Buffer
    {
        private final char[] temp;

        private int size;
        private int index;
        private boolean eof;

        public Buffer(int size)
        {
            this.temp = new char[max( size, MIN_SIZE )];
            this.size = 0;
            this.index = this.temp.length;
            this.eof = false;
        }

        public boolean fill()
        {
            if ( !eof )
            {
                size = reader.read( temp );
                index = 0;
                eof = -1 == size;
            }

            return !eof;
        }


        public boolean seekable()
        {
            return index < size;
        }

        public char seek()
        {
            return temp[index++];
        }
    }
CrLfReader#readline
    private static final char CR = '\r';
    private static final char LF = '\n';

    private void readline(boolean cr)
    {
        while ( this.buffer.seekable() )
        {
            final char c = this.buffer.seek();

            if ( cr )
            {
                if ( LF == c ) return;

                this.sb.append( CR );
            }

            cr = CR == c;

            if ( !cr )
            {
                this.sb.append( c );
            }
        }


        if ( this.buffer.fill() )
        {
            this.readline( cr );
        }
        else
        {
            if ( cr ) sb.append( CR );

            this.end = true;
        }
    }

■GitHub

repository URL
https://github.com/sugaryo/sharp4j

class FQN
sharp4j.util.io.CrLfReader

余談

C#だったら yield return 使って更に便利なユーティリティに出来るんだけど、
JavaだったらStreamAPIを使う事になるのかな?

ちょっとその辺の勉強がてら、拡張してみたいな。

sugaryo
缶コーヒーを原料にしてプログラムを吐き出すお仕事をしています。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした