我的 lex 模式无法匹配我的输入文件,如何更正它?

My lex pattern doesn't work to match my input file, how to correct it?

我有一个简单的匹配模式:head+content+tail,我有一个如下所示的 lex 文件:

$ cat b.l
%{
#include<stdio.h>
%}
%%
"12" {printf("head\n");}
"34" {printf("tail\n");}
.* {printf("content\n");}
%%

我希望遇到“12”时打印"head",遇到“34”时打印"tail",任何其他连续的字符串,打印"content"。

所以我编译并 运行 它:

lex b.l && gcc lex.yy.c -ll
$ echo '12sdaesre34'|a.out
content

我的预期是,它会打印出来

head
content
tail

但实际上它只打印 "content" 行。我有没有看错,如何改正?

谢谢!

(F)lex 总是匹配最长的可能标记。由于 .* 将匹配任何不包含换行符的序列,它会很乐意匹配 12sdaesre34。 (在 (f)lex 中,. 匹配换行符以外的任何字符。)因此 34 不再可用于匹配。

要修复它,你必须清楚你想要content匹配什么。例如,以下将匹配任何不包含数字的内容:

[^[:digit:]]+   { printf("content\n"); }

您可能希望将换行符添加到不匹配的字符列表中:

[^\n[:digit:]]+   { printf("content\n"); }

或者您可能想要匹配不包含 34 的最长序列。这比较棘手,但可以做到:

([^3]|3+[^34])+   { printf("content\n"); }

但是,这仍然会匹配初始值 12,因此它不足以解决问题。

如果您的输入始终由 12...34 形式的字符串组成,可能散布着其他内容,您可以匹配整个 12...34 序列并将其拆分为三个标记。这无疑是最简单的解决方案,因为开始和结束标记的长度已知。以下模式中的第一个匹配不以 12 开头、恰好在 12 的第一个实例之前结束的字符串,第二个匹配以 12 开头并以 12 结尾的字符串34 的第一个实例(匹配)。这两种模式都不会匹配包含不匹配 12 的输入;所以添加了第三条规则来匹配这种情况;它看起来很像第二条规则,但最后不包含 34 的匹配项。因为 (f)lex 总是匹配最长的可能标记,所以第三条规则只有在第二条规则失败时才会成功。

([^1]|1+[^12])*         { puts("content"); }
12([^3]|3+[^34])*34     { puts("head content tail"); }
12([^3]|3+[^34])*       { puts("error"); }

通常,您会希望实际捕获 content 的值以传递给调用程序。在第一条规则中,这只是 yytext,但在第二条规则中,内容由 yyleng-4 个字符组成,从 yytext+2 开始(为了删除前导和尾随分隔符)。

对于大多数用途,如果需要保留它,则必须复制匹配的令牌,因为yytext指向词法扫描器使用的内部数据结构,并且指针将在下一个时失效模式匹配。对于第一条规则,您可以使用 strcpy 创建字符串的副本,但对于第二条规则,您需要自己制作副本:

([^1]|1+[^12])*         { yylval = strcpy(yytext); ... }
12([^3]|3+[^34])*34     { yylval = malloc(yyleng-3);
                          memcpy(yylval, yytext, yyleng-4);
                          yylval[yyleng-4] = '[=14=]';
                          ...
                        }

那些假设 yylval 是类型 char* 的全局变量,并且在代码的某处你 free() 规则保存的字符串。他们还假设您在省略的代码 (...) 中用 yylval 做了一些事情,或者您向调用者 return 指示是否遇到了头和尾。