如何只选择前 N 个字符并在 LEX/FLEX 中匹配模式时删除剩余字符
How to pick only first N number of characters and drop remaining while matching pattern in LEX/FLEX
我需要编写 Lex/Flex
代码来识别 Identifiers
。
这里,Identifiers
定义为 -
标识符-它可以有字母(小写)数字组合,以字母或下划线开头,只选择前 20 个字符,其余字符删除。
我的问题是如何只选择前 20 个字符并丢弃剩余的字符。
示例输入:
_sdfasdfjh89234792jashdf
89ajshdf
所需输出:
_sdfasdfjh89234792ja is an Identifier
89ajshdf is a normal string
经过多次尝试,我想出了以下解决方案,但这不是所需的输出。
我得到的输出:
_sdfasdfjh89234792jashdf is an Identifier
89ajshdf is a normal string
我的解决方案代码:
%{
%}
%%
([a-z]|_)[_a-z0-9]{1,19} {printf("%s is an identifier\n",yytext);}
.* {printf("%s is normal string\n",yytext);} /* we will use ctrl+d to exit*/
%%
int yywrap(){}
int main(){
yylex();
return 0;
}
问题陈述要求您识别标识符,然后使用每个标识符的前 20 个字符。这与接受最多 20 个字符作为标识符标记完全不同,这是您的代码试图做的,因为在扫描 20 个字符后,标识符的其余部分仍在输入流中,下一次扫描将选择它作为第二个标记,这是不希望的。所以你需要摆脱有界重复运算符 {1,19}
.
在 yytext
中获得令牌后,您需要在操作中将其截断。这是简单的 C 字符串操作。这里唯一有用的 (f)lex 特性是它将全局 yyleng
设置为令牌的长度(在 yytext
中)。
yytext
是一个内部临时缓冲区,所以如果您希望它的内容在 (f)lex 操作之后仍然存在,您需要制作一个副本。但是如果您只想打印出最多 20 个字符的令牌,您可以在 printf
格式字符串中使用长度限制:
[a-z_][a-z0-9_]* { printf("%.20s is an identifier.\n", yytext); }
您还需要更改第二个规则,因为 .*
将匹配到当前行的末尾。除非标识符恰好位于行尾,否则 .*
将产生更长的匹配并且不会使用标识符规则。 (F)lex 总是选择最长的匹配;它只优先考虑规则顺序,以防两个或多个规则都产生相同的最长匹配。
如果您确实想要 return 字符串值,您可能希望复制最多 20 个字符。最简单的方法是使用 strndup
函数:
yylval = strndup(yytext, 20); /* This is a Posix function, so it's not in all C libraries. */
如果您没有strndup
,则必须自己制作副本,在这种情况下yyleng
就派上用场了:
if (yyleng > 20) yyleng = 20;
yylval = malloc(yyleng + 1);
memcpy(yylval, yytext, yyleng);
yylval[yyleng] = `[=12=]`;
备注
您需要检查 strndup
或 malloc
编辑的值 return 以确保它不为 NULL。 NULL 表示内存不足错误。您还需要在某处声明 yylval
;如果您使用 yacc/bison 进行解析,那将是自动的,但是您需要告诉 yacc/bison yylval
是 char*
而不是默认的 int
.当您不再需要时,不要忘记 free
分配的字符串。
yyleng
使这稍微更有效率,但如果您在其他编码环境中,您可以只使用 strnlen
来计算有界字符串长度:
leng = strnlen(yytext, 20);
yylval = malloc(leng + 1);
memcpy(yylval, yytext, leng);
yylval[leng] = `[=13=]`;
不要使用strlen
然后测试。 strlen
无论多长都要数到字符串末尾,你不关心精确的长数是多少。 strnlen
在达到限制时停止计数,这避免了额外的工作。它不太可能对扫描仪产生重大影响,但在大获全胜的情况下养成这种习惯是个好习惯。
我需要编写 Lex/Flex
代码来识别 Identifiers
。
这里,Identifiers
定义为 -
标识符-它可以有字母(小写)数字组合,以字母或下划线开头,只选择前 20 个字符,其余字符删除。
我的问题是如何只选择前 20 个字符并丢弃剩余的字符。 示例输入:
_sdfasdfjh89234792jashdf
89ajshdf
所需输出:
_sdfasdfjh89234792ja is an Identifier
89ajshdf is a normal string
经过多次尝试,我想出了以下解决方案,但这不是所需的输出。 我得到的输出:
_sdfasdfjh89234792jashdf is an Identifier
89ajshdf is a normal string
我的解决方案代码:
%{
%}
%%
([a-z]|_)[_a-z0-9]{1,19} {printf("%s is an identifier\n",yytext);}
.* {printf("%s is normal string\n",yytext);} /* we will use ctrl+d to exit*/
%%
int yywrap(){}
int main(){
yylex();
return 0;
}
问题陈述要求您识别标识符,然后使用每个标识符的前 20 个字符。这与接受最多 20 个字符作为标识符标记完全不同,这是您的代码试图做的,因为在扫描 20 个字符后,标识符的其余部分仍在输入流中,下一次扫描将选择它作为第二个标记,这是不希望的。所以你需要摆脱有界重复运算符 {1,19}
.
在 yytext
中获得令牌后,您需要在操作中将其截断。这是简单的 C 字符串操作。这里唯一有用的 (f)lex 特性是它将全局 yyleng
设置为令牌的长度(在 yytext
中)。
yytext
是一个内部临时缓冲区,所以如果您希望它的内容在 (f)lex 操作之后仍然存在,您需要制作一个副本。但是如果您只想打印出最多 20 个字符的令牌,您可以在 printf
格式字符串中使用长度限制:
[a-z_][a-z0-9_]* { printf("%.20s is an identifier.\n", yytext); }
您还需要更改第二个规则,因为 .*
将匹配到当前行的末尾。除非标识符恰好位于行尾,否则 .*
将产生更长的匹配并且不会使用标识符规则。 (F)lex 总是选择最长的匹配;它只优先考虑规则顺序,以防两个或多个规则都产生相同的最长匹配。
如果您确实想要 return 字符串值,您可能希望复制最多 20 个字符。最简单的方法是使用 strndup
函数:
yylval = strndup(yytext, 20); /* This is a Posix function, so it's not in all C libraries. */
如果您没有strndup
,则必须自己制作副本,在这种情况下yyleng
就派上用场了:
if (yyleng > 20) yyleng = 20;
yylval = malloc(yyleng + 1);
memcpy(yylval, yytext, yyleng);
yylval[yyleng] = `[=12=]`;
备注
您需要检查
strndup
或malloc
编辑的值 return 以确保它不为 NULL。 NULL 表示内存不足错误。您还需要在某处声明yylval
;如果您使用 yacc/bison 进行解析,那将是自动的,但是您需要告诉 yacc/bisonyylval
是char*
而不是默认的int
.当您不再需要时,不要忘记free
分配的字符串。yyleng
使这稍微更有效率,但如果您在其他编码环境中,您可以只使用strnlen
来计算有界字符串长度:leng = strnlen(yytext, 20); yylval = malloc(leng + 1); memcpy(yylval, yytext, leng); yylval[leng] = `[=13=]`;
不要使用
strlen
然后测试。strlen
无论多长都要数到字符串末尾,你不关心精确的长数是多少。strnlen
在达到限制时停止计数,这避免了额外的工作。它不太可能对扫描仪产生重大影响,但在大获全胜的情况下养成这种习惯是个好习惯。