如何在RE/flex词法分析器中匹配对称引号包裹的任意内容?
解决RE/flex匹配对称单引号包裹双引号字符串并支持无效UTF8的问题
需要用RE/flex实现匹配如下语法的字符串:
- 合法示例:
"foo"、''"bar"''(对称数量的单引号包裹双引号,内部是任意内容) - 非法示例:
''"baz"'(单引号数量不对称)
RE/flex不支持前瞻、后顾和反向引用,原实现的问题是只能匹配有效UTF8内容,无法处理无效UTF8序列,尝试过的三种方案均有缺陷:
- 使用
skip()会消耗内容无法保留 - 用
.*?"匹配合法字符串有效,但未终止的字符串会导致lexer卡住 - 逐个字符用
.匹配支持无效UTF8但速度过慢
核心思路
利用RE/flex的字节级字符类匹配特性,用[^\x22]*替代原有的[^"]*:
[^\x22]按字节匹配任意非双引号(0x22)的字节,包括无效UTF8序列- 批量匹配非双引号字节,保证效率,同时支持无效UTF8
- 保留原有的对称分隔符长度校验逻辑,确保单引号数量对称
修改后的Lex代码
%x STRING %% '*\x22 { textLen = 0uz; quoteLen = size(); // 记录开头分隔符总长度:n个单引号 + 1个双引号 start(STRING); } <STRING> { \x22'* { // 校验结尾分隔符长度是否和开头一致(保证单引号数量对称) if (size() != quoteLen) goto MORE_TEXT; // 截断匹配结果,仅保留字符串内容部分 matcher().less(textLen + quoteLen); start(INITIAL); res = std::string{matcher().begin(), textLen}; return TokenKind::STR; } [^\x22]* { MORE_TEXT: textLen += size(); // 累积字符串内容长度 matcher().more(); // 继续匹配后续内容 } <<EOF>> { std::cerr << "Lexical error: Unterminated 'STRING'\n"; return TokenKind::ERR; } } %%
方案优势
- 支持无效UTF8:
[^\x22]*按字节匹配,捕获所有非双引号字节,包括无效UTF8序列 - 高效匹配:批量匹配连续非双引号字节,速度和原
[^"]*相当,远快于逐个字符匹配 - 避免卡住:未终止的字符串会触发
<<EOF>>规则,返回明确的 lexical error,不会无限扫描 - 保留内容:通过
textLen累积内容长度,最终提取完整字符串内容,无需skip()
关键细节说明
'*\x22:匹配开头的n个单引号+1个双引号,用size()记录分隔符总长度(n+1)\x22'*:匹配结尾的1个双引号+m个单引号,通过size() == quoteLen确保m == n(单引号数量对称)[^\x22]*:批量匹配任意非双引号字节,包括无效UTF8,解决原代码仅支持有效UTF8的问题matcher().more():将当前匹配的内容累积到后续匹配中,实现跨token的内容拼接
内容的提问来源于stack exchange,提问作者Sourav Kannantha B
相关产品推荐
相关产品推荐

