如何用ANTLR解析含嵌套字符串的逗号分隔字符串?
CSV解析语法问题修正方案
首先看你当前语法的核心问题:
- 规则结构冲突:你的
STRING词法规则已经包含了前后的D_QUOTE,但csv_string语法规则又要求整个CSV被一对D_QUOTE包裹,这意味着只有输入是双层双引号包裹的内容时才能匹配csv_string,但你的示例输入是单层双引号包裹的内容,自然会报错缺少开头的D_QUOTE,同时单独的"foo"会被优先匹配为STRING,而非csv_string里的ID。 - 词法规则优先级:ANTLR的词法规则遵循「先定义优先、最长匹配」原则,你的
STRING规则会优先匹配带双引号的内容,这本身是合理的,但和csv_string的结构要求冲突。
修正后的基础语法(适用于反斜杠转义的字符串)
如果你的字符串用反斜杠转义双引号(比如\"snafu\"),可以用以下语法:
grammar snafu; // 忽略转义换行和空白字符 ESC_NEWLINE : '\\' '\r'? '\n' -> skip; WS : [ \t]+ -> skip; // 定义基础符号 D_QUOTE : '"'; COMMA : ','; // 词法规则顺序:不带引号的标识符、数字在前,带引号的字符串在后(不冲突) ID : (LETTER|'_') (LETTER|DIGIT|'_')*; INTEGER : '0' | ('-'? NON_ZERO_DIGIT DIGIT*); FLOAT : '-'? ( '0' '.' DIGIT+ | NON_ZERO_DIGIT DIGIT* '.' DIGIT+ ); STRING : D_QUOTE ( ~["\\] | ESCAPE_SEQUENCE )* D_QUOTE; // 片段规则(仅用于辅助定义,不会生成词法Token) fragment LETTER : [a-zA-Z]; fragment DIGIT : [0-9]; fragment NON_ZERO_DIGIT : [1-9]; fragment ESCAPE_SEQUENCE : '\\' ( [nrtbf\\"] | [0-7]{3} ); // 语法规则:CSV行由多个逗号分隔的值组成 csv_line : csv_value (COMMA csv_value)*; // 每个值可以是数字、标识符或带引号的字符串 csv_value : number | ID | STRING; number : INTEGER | FLOAT;
进阶:用词法模式处理CSV标准的双引号转义
如果遵循CSV标准(用两个双引号表示一个实际双引号,比如"He said ""hello"""),推荐使用词法模式(pushMode/popMode)来避免歧义,语法如下:
grammar snafu; // 全局模式下的规则 ESC_NEWLINE : '\\' '\r'? '\n' -> skip; WS : [ \t]+ -> skip; COMMA : ','; ID : (LETTER|'_') (LETTER|DIGIT|'_')*; INTEGER : '0' | ('-'? NON_ZERO_DIGIT DIGIT*); FLOAT : '-'? ( '0' '.' DIGIT+ | NON_ZERO_DIGIT DIGIT* '.' DIGIT+ ); // 遇到双引号时切换到字符串内部模式 D_QUOTE : '"' -> pushMode(STRING_MODE); // 字符串内部模式:仅处理字符串内的内容 mode STRING_MODE; // 两个连续双引号表示一个实际双引号,复用D_QUOTE的Token类型 STRING_D_QUOTE : '"' '"' -> type(D_QUOTE); // 遇到单个双引号时退出字符串模式 STRING_END : '"' -> popMode; // 匹配字符串内的普通内容(除双引号外的所有字符) STRING_CONTENT : ~["]+; // 片段规则 fragment LETTER : [a-zA-Z]; fragment DIGIT : [0-9]; fragment NON_ZERO_DIGIT : [1-9]; // 语法规则 csv_line : csv_value (COMMA csv_value)*; csv_value : number | ID | string; number : INTEGER | FLOAT; // 字符串由开头双引号、内容(普通内容或转义双引号)、结尾双引号组成 string : D_QUOTE (STRING_CONTENT | STRING_D_QUOTE)* STRING_END;
关于词法模式的必要性
如果你的字符串内部需要包含逗号、双引号等特殊字符,或者遵循CSV标准的转义规则,使用词法模式是更清晰的选择——它能让词法分析器在进入字符串时专注处理内部内容,避免和外部的逗号、标识符等规则产生歧义。如果只是简单的字符串(不含特殊字符),基础语法就足够。
内容的提问来源于stack exchange,提问作者David Maffitt
相关产品推荐
相关产品推荐

