无法解析非结构化文本:ANTLR语法解析问题求助
问题分析与解决方案
错误根源
- 词法规则优先级冲突:ANTLR词法分析器遵循「最长匹配+先定义优先」原则,原语法中
DTEXT会匹配几乎所有非换行内容,但SDATA/CDATA定义在DTEXT之前,导致部分行被错误拆分;同时注释行未优先匹配,会被DTEXT误识别。 - 关键字与数据边界模糊:数据行中包含的
sdata/cdata不应被视为关键字,但原语法未区分「语句开头的关键字」和「数据中的关键字」。 - 换行处理逻辑错误:原
NL被直接跳过,导致解析器无法正确识别行边界,出现sdata:\r\n这类错误输入。
修改后的词法分析器(testlexer.g4)
lexer grammar testlexer; // 默认模式:处理注释、关键字、冒号 COMMENT: '*' .*? '\r'? '\n' -> channel(HIDDEN); SDATA: S D A T A -> pushMode(DATA_MODE); CDATA: C D A T A -> pushMode(DATA_MODE); COLON: ':'; WS: [ \t\f]+ -> skip; // 数据模式:处理数据行,直到遇到下一个关键字或注释 mode DATA_MODE; DTEXT: ~[\r\n*]+ '\r'? '\n'; // 排除注释开头的* DATA_COMMENT: '*' .*? '\r'? '\n' -> channel(HIDDEN); DATA_WS: [ \t\f]+ -> skip; // 遇到关键字时切回默认模式 DATA_SDATA: S D A T A -> type(SDATA), popMode; DATA_CDATA: C D A T A -> type(CDATA), popMode; // 字符碎片定义 fragment A:('a'|'A'); fragment B:('b'|'B'); fragment C:('c'|'C'); fragment D:('d'|'D'); fragment E:('e'|'E'); fragment F:('f'|'F'); fragment G:('g'|'G'); fragment H:('h'|'H'); fragment I:('i'|'I'); fragment J:('j'|'J'); fragment K:('k'|'K'); fragment L:('l'|'L'); fragment M:('m'|'M'); fragment N:('n'|'N'); fragment O:('o'|'O'); fragment P:('p'|'P'); fragment Q:('q'|'Q'); fragment R:('r'|'R'); fragment S:('s'|'S'); fragment T:('t'|'T'); fragment U:('u'|'U'); fragment V:('v'|'V'); fragment W:('w'|'W'); fragment X:('x'|'X'); fragment Y:('y'|'Y'); fragment Z:('z'|'Z');
修改后的解析器语法(testparser.g4)
parser grammar testparser; options { tokenVocab=testlexer;} prog : stmt* EOF; stmt : sdata_stmt | cdata_stmt; // 允许关键字与冒号之间有空格,支持冒号后直接跟同行数据或换行数据 sdata_stmt: SDATA WS? COLON WS? (dtext | WS)*; cdata_stmt: CDATA WS? COLON WS? (dtext | WS)*; dtext: DTEXT;
核心调整说明
- 词法模式隔离:
- 默认模式仅识别注释、关键字和冒号,遇到关键字后切换到
DATA_MODE。 DATA_MODE下只处理数据行和注释,数据行中出现的sdata/cdata会被重新标记为关键字并切回默认模式,避免误识别。
- 默认模式仅识别注释、关键字和冒号,遇到关键字后切换到
- 注释优先匹配:将
COMMENT放在词法规则最前面,确保注释行不会被其他规则误匹配。 - 灵活语句结构:解析器允许关键字与冒号之间有空格,支持冒号后直接跟同一行数据,或换行后的多行数据。
- 保留行边界:不再跳过换行符,而是将换行包含在
DTEXT中,确保解析器能正确识别每一行数据。
内容的提问来源于stack exchange,提问作者sedu
相关产品推荐
相关产品推荐

