You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 22:48:11