Parsing 我的莱克斯模式没有';无法匹配输入文件,如何更正?

Parsing 我的莱克斯模式没有';无法匹配输入文件,如何更正?,parsing,printing,match,lex,Parsing,Printing,Match,Lex,我有一个简单的模式来匹配:head+content+tail,我有一个lex文件,如下所示: $ cat b.l %{ #include<stdio.h> %} %% "12" {printf("head\n");} "34" {printf("tail\n");} .* {printf("content\n");} %% 我的期望是,它会打印出来 head content tail 但实际上它只打印“内容”行。我做错了什么,怎么纠正 谢谢 (F)lex始终匹配最长的令牌。由于*

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

$ cat b.l
%{
#include<stdio.h>
%}
%%
"12" {printf("head\n");}
"34" {printf("tail\n");}
.* {printf("content\n");}
%%
我的期望是,它会打印出来

head
content
tail
但实际上它只打印“内容”行。我做错了什么,怎么纠正

谢谢

(F)lex始终匹配最长的令牌。由于
*
将匹配任何不包含换行符的序列,因此它将很高兴地匹配
12sdaesre34
。(在(f)lex中,
匹配除换行符以外的任何字符。)因此,
34
不再可用于匹配

要解决这个问题,您必须清楚您希望
内容
匹配什么。例如,以下内容将匹配任何不包含数字的内容:

[^[:digit:]]+   { printf("content\n"); }
您可能希望将换行符添加到不匹配的字符列表中:

[^\n[:digit:]]+   { printf("content\n"); }
或者,您可能希望匹配不包含
34
的最长序列。这很棘手,但可以做到:

([^3]|3+[^34])+   { printf("content\n"); }
但是,这仍然会匹配初始
12
,因此它不足以解决问题

如果您的输入总是由形式为
12…34
的字符串组成,并且可能夹杂着其他内容,那么您可以匹配整个
12…34
序列,并将其拆分为三个标记。这无疑是最简单的解决方案,因为开始和结束标记的长度是已知的。以下模式中的第一个匹配的字符串不以
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"); }
通常,您希望实际捕获
内容的值
以传递给调用程序。在第一条规则中,这只是
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] = '\0';
                          ...
                        }
这些假设是
yylval
是一个类型为
char*
的全局变量,并且在代码中的某个地方您可以
free()
规则保存的字符串。它们还假设您使用省略的代码(
)中的
yylval
执行某些操作,或者您返回给调用方,并指示是否遇到了头部和尾部。

(F)lex始终匹配最长的令牌。由于
*
将匹配任何不包含换行符的序列,因此它将很高兴地匹配
12sdaesre34
。(在(f)lex中,
匹配除换行符以外的任何字符。)因此,
34
不再可用于匹配

要解决这个问题,您必须清楚您希望
内容
匹配什么。例如,以下内容将匹配任何不包含数字的内容:

[^[:digit:]]+   { printf("content\n"); }
您可能希望将换行符添加到不匹配的字符列表中:

[^\n[:digit:]]+   { printf("content\n"); }
或者,您可能希望匹配不包含
34
的最长序列。这很棘手,但可以做到:

([^3]|3+[^34])+   { printf("content\n"); }
但是,这仍然会匹配初始
12
,因此它不足以解决问题

如果您的输入总是由形式为
12…34
的字符串组成,并且可能夹杂着其他内容,那么您可以匹配整个
12…34
序列,并将其拆分为三个标记。这无疑是最简单的解决方案,因为开始和结束标记的长度是已知的。以下模式中的第一个匹配的字符串不以
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"); }
通常,您希望实际捕获
内容的值
以传递给调用程序。在第一条规则中,这只是
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] = '\0';
                          ...
                        }
这些假设是
yylval
是一个类型为
char*
的全局变量,并且在代码中的某个地方您可以
free()
规则保存的字符串。它们还假设您使用省略的代码(
)中的
yylval
执行某些操作,或者您重新