プログラマメモ2 - programmer no memo2

RFC 2234風パーサーを目指してantlrで定義 その2 2008/09/13

CSVの処理系って以外と適当につくっているところが多いのかなと。
Javaならカンマで適当にStringのsplitを使うかStringTokenizerとかでやったりしている人が多いかも。
それで改行コードがフィールドに入ってたりすると、あわてたりするわけで。Orz...

ANTLR使って、CSVをListのListにして返すように定義してみました。

CSVのヘッダーってわざわざ定義する必要がないですよね。

呼び出しコード

package csv;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;

import org.antlr.runtime.ANTLRInputStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;

public class Test2 {

public static void main(String[] args) throws IOException,
RecognitionException, URISyntaxException {
URL url = Test2.class.getResource("test.csv");
FileInputStream fileInputStream = new FileInputStream(new File(url
.toURI()));
ANTLRInputStream inputStream = new ANTLRInputStream(fileInputStream);
CsvLexer lexer = new CsvLexer(inputStream);
CommonTokenStream tokens = new CommonTokenStream(lexer);
CsvParser parser = new CsvParser(tokens);
System.out.println(parser.file());
}
}


ANTLRのグラマーなほうCsv.g
ヘッダーとして使う使わないかはあとから決定することにしてグラマーには定義しませんでした。
たぶんうまくいっていると思うんだ。
grammar Csv;

@header {
package csv;
}
@lexer::header {
package csv;
}
// file = [header CRLF] record *(CRLF record) [CRLF]
file returns[List list]
scope {
List l;
}
@init {
$file::l = new ArrayList();
}

:r=record{$file::l.add($r.list);} ((LF | CRLF) r=record{$file::l.add($r.list);})*{
// System.out.println("*** *** file!!");
// System.out.println($file::l);
$list = $file::l;
};
// header = name *(COMMA name)
header
: name (COMMA name)*;
// record = field *(COMMA field)
record returns[List list]
scope {
List f;
}
@init {
$record::f = new ArrayList();
}
: l=field{ $record::f.add($l.text); } (COMMA l=field{$record::f.add($l.text);})*
{
$list = $record::f;
};

// name = field
name: field;
// field = (escaped / non-escaped)
field
: escaped {
}
| nonescaped {
};
// escaped = DQUOTE *(TEXTDATA / COMMA / CR / LF / 2DQUOTE) DQUOTE
escaped returns [String var]
scope {
StringBuilder b;
}
@init {
$escaped::b = new StringBuilder();
}
: DQUOTE (TEXTDATA{ $escaped::b.append($TEXTDATA); }
| COMMA{ $escaped::b.append($COMMA); }
| CR{ $escaped::b.append($CR); }
| LF{ $escaped::b.append($LF); }
| d=DQUOTE{ $escaped::b.append($d); } DQUOTE)* DQUOTE {

$var = $escaped::b.toString();
};
// non-escaped = *TEXTDATA
nonescaped: TEXTDATA* {
};

// COMMA = %x2C
COMMA
: ',';
// CR = %x0D ;as per section 6.1 of RFC 2234 [2]
CR : '\r';
// DQUOTE = %x22 ;as per section 6.1 of RFC 2234 [2]
DQUOTE : '"';
// LF = %x0A ;as per section 6.1 of RFC 2234 [2]
LF : '\n';
// CRLF = CR LF ;as per section 6.1 of RFC 2234 [2]
CRLF: CR LF;
// TEXTDATA = %x20-21 / %x23-2B / %x2D-7E
TEXTDATA
: (~(COMMA| CR| LF| DQUOTE))+
;

: