RFC 4180に準拠したCSVデータを読み込みたい!

もしかしたらどっか(apacheとか…)にありそうだけれども勉強ついでに。
非常に泥臭い感じに仕上がっております。全力。

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class CsvReader {

    private BufferedReader bufferedReader;

    public CsvReader(String filePath) throws IOException {
        bufferedReader = new BufferedReader(new InputStreamReader(
                new FileInputStream(filePath), System
                        .getProperty("file.encoding")));
    }

    public String[] readLineColumns() throws IOException {
        List lineColumns = new ArrayList();
        // カラム文字列
        StringBuilder element = null;
        // クォート開始フラグ
        boolean isQuotBegan = false;
        // クォート出現カウンタ
        int cntQuot = 0;

        while (true) {
            // 文字コード読込み
            int code = bufferedReader.read();
            // 現在文字コードがファイル終端の場合
            if (code < 0) {
                if (element != null) {
                    // 返却リストにカラム文字列を追加
                    lineColumns.add(element.toString());
                }
                // レコード終了のためループ終了
                break;
            }
            // クォート開始判定
            if (!isQuotBegan && code == 0x22) {
                // RFC4180準拠であれば、クォートで開始されていないカラム内に、
                // クォートが存在することはありえない
                isQuotBegan = true;
                continue;
            }
            // 文字変換
            char nowChar = (char) code;
            if (element == null) {
                element = new StringBuilder();
            }
            if (isQuotBegan) {
                // 要素がクォーテーションで開始されている場合
                // カンマ、クォーテーション、改行が存在し
                // クォーテーションはクォーテーションでエスケープされている
                if (nowChar == 0x0d && cntQuot == 1) {
                    // 現在文字コードが CR で、
                    // かつ、クォート出現済の場合
                    // 閉じクォートと判断し、カラム文字列確定
                    // 改行コード判定のため、マーク設定
                    bufferedReader.mark(1);
                    // 次文字取得
                    char next = (char) bufferedReader.read();
                    if (next != 0x0a) {
                        // 次文字が LF でない場合
                        // CR でレコードが終了しているとみなし、
                        // マーク位置(次文字取得以前)にリセット
                        bufferedReader.reset();
                    }
                    // 返却リストにカラム文字列を追加
                    lineColumns.add(element.toString());
                    // レコード終了のためループ終了
                    break;
                } else if (nowChar == 0x0a && cntQuot == 1) {
                    // 現在文字コードが LF で、
                    // かつ、クォート出現済の場合
                    // 閉じクォートと判断し、カラム文字列確定
                    // 返却リストにカラム文字列を追加
                    lineColumns.add(element.toString());
                    // レコード終了のためループ終了
                    break;
                } else if (nowChar == 0x2c && cntQuot == 1) {
                    // 現在文字コードがカンマで、
                    // かつ、クォート出現済の場合
                    // 閉じクォートと判断し、カラム文字列確定
                    // 返却リストにカラム文字列を追加
                    lineColumns.add(element.toString());
                    // 現状のカラム文字列削除
                    element.delete(0, element.length());
                    // クォート開始フラグ・カウンタ初期化
                    isQuotBegan = false;
                    cntQuot = 0;
                } else if (nowChar == 0x22) {
                    // 現在文字コードがクォートの場合
                    if (cntQuot == 1) {
                        // エスケープ用のクォートが存在した場合
                        // 現在文字をカラム文字列に追加し、
                        // カウンタクリア
                        element.append(nowChar);
                        cntQuot = 0;
                    } else {
                        // カウントアップ
                        cntQuot++;
                    }
                } else {
                    // 上記条件に当てはまらない場合
                    // 現在文字をカラム文字列に追加
                    element.append(nowChar);
                }
            } else {
                // 要素がクォーテーションで開始されていない場合
                // 要素内にカンマ、クォーテーション、改行は存在しない
                if (nowChar == 0x0d) {
                    // 現在文字コードが CR の場合
                    // 改行コード判定のため、マーク設定
                    bufferedReader.mark(1);
                    // 次文字取得
                    char next = (char) bufferedReader.read();
                    if (next != 0x0a) {
                        // 次文字が LF でない場合
                        // 改行(CR)でレコードが終了しているとみなし、
                        // マーク位置(次文字取得以前)にリセット
                        bufferedReader.reset();
                    }
                    // 返却リストにカラム文字列を追加
                    lineColumns.add(element.toString());
                    // レコード終了のためループ終了
                    break;
                } else if (nowChar == 0x0a) {
                    // 現在文字コードが LF の場合
                    // 返却リストにカラム文字列を追加
                    lineColumns.add(element.toString());
                    // レコード終了のためループ終了
                    break;
                } else if (nowChar == 0x2c) {
                    // 現在文字コードがカンマの場合
                    // 返却リストにカラム文字列を追加
                    lineColumns.add(element.toString());
                    // 現状のカラム文字列削除
                    element.delete(0, element.length());
                } else {
                    // 上記条件に当てはまらない場合
                    // 現在文字をカラム文字列に追加
                    element.append(nowChar);
                }
            }
        }
        if (lineColumns.isEmpty()) {
            return null;
        }
        return lineColumns.toArray(new String[0]);
    }
}

たぶん大丈夫なはず…
再帰したい!と思ってましたが結局普通のループで。しかもwhile(true)…
分岐も多いし、行ったり来たりやし。なんか不満だけれども。
すぐ忘れるのでメモ代わり。すごい閃いたら塗り替えます。

花神

司馬遼太郎 新潮文庫

体調崩したりダラけたりしてましたが遂に情報処理を生業とする歯車が基本的に備え(略)多の艱難辛苦を乗(略)というわけで合格です!ぅわーい!キタ!午前600午後705で午前の点数ギリギリ。オペレーションタイトロープ、まさにフラメンコサヴァイヴァーだった訳ですよ。
やーこれで課長やら部長やらの無言のプレッシャーから逃れ、同期からの生暖かい眼差しも避ける事が出来る訳です。と思ったのも束の間次はオラクルマスターとかいうデータを何やらする技術がまさに今、課長の中でアツいらしいですよ?


ぐぐぐ…べんきようキライ。

いろは歌

いろはにほへと ちりぬるを わかよたれそ つねならむ ゐのおくやま けふこえて あさきゆめみし ゑひもせす

色は匂へど 散りぬるを 我が世誰ぞ 常ならむ 有為の奥山 今日越えて 浅き夢見じ 酔ひもせず

花は鮮やかだが散ってしまう この世で誰が一定不変であろうか 因と縁によって生じる有為の世を今日越えて はかない夢など見ない 酔っているわけでもないのに