c - 如何在 LEX/FLEX 中匹配模式时只选择前 N 个字符并丢弃剩余的字符
问题描述
我需要编写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 动作更有效,你需要制作一个副本。printf
但是,如果您只想打印最多 20 个字符的令牌,则可以在格式字符串中使用长度限制:
[a-z_][a-z0-9_]* { printf("%.20s is an identifier.\n", yytext); }
您还需要更改第二条规则,因为.*
它将匹配当前行的末尾。除非标识符正好位于行尾,.*
否则将产生更长的匹配并且不会使用标识符规则。(F)lex 总是选择最长的匹配;它只优先考虑规则顺序,以防两个或多个规则都产生相同的最长匹配。
如果您确实想要返回字符串值,则需要复制最多 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] = `\0`;
笔记
您需要检查
strndup
or返回的值,malloc
以确保它不为 NULL。NULL 表示内存不足错误。您还需要在yylval
某处声明;如果您使用 yacc/bison 进行解析,这将是自动的,但您需要告诉 yacc/bison 这yylval
是 achar*
而不是 defaultint
。free
当您不再需要它时,不要忘记分配的字符串。yyleng
使这稍微更有效率,但如果您在其他一些编码环境中,您可以使用它strnlen
来计算有界字符串长度:leng = strnlen(yytext, 20); yylval = malloc(leng + 1); memcpy(yylval, yytext, leng); yylval[leng] = `\0`;
不要使用
strlen
然后测试。strlen
不管它有多长,都必须数到字符串的末尾,而且你不在乎精确的长计数是多少。strnlen
达到限制时停止计数,从而避免了额外的工作。它不太可能对扫描仪产生重大影响,但对于大获全胜的情况来说,这是一个好习惯。
推荐阅读
- oracle - 表 ESPUMA.ORDER_DETAILS 正在变异,触发器/函数可能看不到它
- javascript - 挂载和卸载的过渡
- c# - 返回列表中具有角色 id 的所有用户
- java - 尝试在空对象引用上调用虚拟方法“void android.widget.ImageView.setImageResource(int)”
- django - Django 轻松减少字段值的方法
- azure-devops-rest-api - 无法使用 API 创建 VSTS 安全组
- polymer - 启动聚合物 3.0。无法呈现自定义元素。检查是否启用了 JavaScript
- jenkins-pipeline - 需要通过 git sha 来结帐到 Jenkins 多分支管道
- installation - 如何在 Ubuntu 16.04 上安装 Plesk
- c# - 从通用数据类型填充 SelectListItem