在 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)*;

这增加了前导逗号的可能性(实际上是缺少第一个值)。

还有一条建议:不要在语法中使用操作代码来收集值。相反,创建一个解析侦听器并将其分配给解析器,解析器的方法在解析期间被触发以完成所有后台工作。它使语法更清晰,并允许独立于实际目标语言使用它。