在 C# 中使用 ANTLR 解析 csv
Parsing csv using ANTLR in c#
我在 ANTLR 中创建了以下语法来解析 csv 文件。
grammar CSV;
file returns [List<List<string>> data]
@init {$data = new List<List<string>>();}
: (row {$data.Add($row.list);})+ EOF
;
row returns [List<string> list]
@init {$list = new List<string>();}
: a=value {
$list.Add($a.val);
}
(Comma b=value {
$list.Add($b.val);}
)*
(LineBreak | EOF)
;
value returns [string val]
: SimpleValue {$val = $SimpleValue.text;}
| QuotedValue
{
System.Console.WriteLine($val);
$val = $QuotedValue.text;
$val = $val.Substring(1, $val.Length-1);
$val = $val.Replace("\"\"", "\"");
}
;
Comma :
( ' '* ',' ' '*);
LineBreak :
'\r'? '\n';
SimpleValue
: ~[,\r\n"]+
;
QuotedValue
: '"' ('""' | ~'"')* '"'
;
上面的语法正在解析下面的csv文件没有错误。
a,b
1,2
3,4
但是当我解析以下 csv 文件时,它抛出以下错误
a,b
,2
3,4
line 2:0 extraneous input ',' expecting {<EOF>, SimpleValue, QuotedValue}
谁能指导我如何解决这个问题?
主程序
public List<List<string>> Parse()
{
string csvData = string.Empty;
if (string.IsNullOrEmpty(_path))
throw new ArgumentException("Path can not be empty");
try
{
csvData = File.ReadAllText(_path);
}
catch (Exception)
{
throw new FileNotFoundException(string.Format("{0} not found", _path));
}
// create an instance of the lexer
CSVLexer lexer = new CSVLexer(new AntlrInputStream(csvData));
// wrap a token-stream around the lexer
CommonTokenStream tokens = new CommonTokenStream(lexer);
// create the parser
CSVParser parser = new CSVParser(tokens);
// invoke the entry point of our grammar
_results = parser.file().data;
return _results;
}
更新
根据 Mike Lischke 的回答,我更新了如下的行规则。现在我没有收到任何错误
row returns [List<string> list]
@init {$list = new List<string>();}
: Comma? a=value {
$list.Add($a.val);
}
(Comma b=value {
$list.Add($b.val);
}
)*
(LineBreak | EOF)
;
显然您的 row
规则不够灵活,无法处理缺失值。你应该改用这样的东西:
row: Comma? value (Comma value)*;
这增加了前导逗号的可能性(实际上是缺少第一个值)。
还有一条建议:不要在语法中使用操作代码来收集值。相反,创建一个解析侦听器并将其分配给解析器,解析器的方法在解析期间被触发以完成所有后台工作。它使语法更清晰,并允许独立于实际目标语言使用它。
我在 ANTLR 中创建了以下语法来解析 csv 文件。
grammar CSV;
file returns [List<List<string>> data]
@init {$data = new List<List<string>>();}
: (row {$data.Add($row.list);})+ EOF
;
row returns [List<string> list]
@init {$list = new List<string>();}
: a=value {
$list.Add($a.val);
}
(Comma b=value {
$list.Add($b.val);}
)*
(LineBreak | EOF)
;
value returns [string val]
: SimpleValue {$val = $SimpleValue.text;}
| QuotedValue
{
System.Console.WriteLine($val);
$val = $QuotedValue.text;
$val = $val.Substring(1, $val.Length-1);
$val = $val.Replace("\"\"", "\"");
}
;
Comma :
( ' '* ',' ' '*);
LineBreak :
'\r'? '\n';
SimpleValue
: ~[,\r\n"]+
;
QuotedValue
: '"' ('""' | ~'"')* '"'
;
上面的语法正在解析下面的csv文件没有错误。
a,b
1,2
3,4
但是当我解析以下 csv 文件时,它抛出以下错误
a,b
,2
3,4
line 2:0 extraneous input ',' expecting {<EOF>, SimpleValue, QuotedValue}
谁能指导我如何解决这个问题?
主程序
public List<List<string>> Parse()
{
string csvData = string.Empty;
if (string.IsNullOrEmpty(_path))
throw new ArgumentException("Path can not be empty");
try
{
csvData = File.ReadAllText(_path);
}
catch (Exception)
{
throw new FileNotFoundException(string.Format("{0} not found", _path));
}
// create an instance of the lexer
CSVLexer lexer = new CSVLexer(new AntlrInputStream(csvData));
// wrap a token-stream around the lexer
CommonTokenStream tokens = new CommonTokenStream(lexer);
// create the parser
CSVParser parser = new CSVParser(tokens);
// invoke the entry point of our grammar
_results = parser.file().data;
return _results;
}
更新
根据 Mike Lischke 的回答,我更新了如下的行规则。现在我没有收到任何错误
row returns [List<string> list]
@init {$list = new List<string>();}
: Comma? a=value {
$list.Add($a.val);
}
(Comma b=value {
$list.Add($b.val);
}
)*
(LineBreak | EOF)
;
显然您的 row
规则不够灵活,无法处理缺失值。你应该改用这样的东西:
row: Comma? value (Comma value)*;
这增加了前导逗号的可能性(实际上是缺少第一个值)。
还有一条建议:不要在语法中使用操作代码来收集值。相反,创建一个解析侦听器并将其分配给解析器,解析器的方法在解析期间被触发以完成所有后台工作。它使语法更清晰,并允许独立于实际目标语言使用它。