在 flex 中打印匹配单词的行
Print line of matched word in flex
我正在尝试创建一个带有 flex 的扫描仪,它的行为有点像 grep
。
基本上,我想做的是:给定一个词(常规文本,而不是正则表达式),在输入中找到包含该文本匹配项的任何行,然后打印包含该词的行。
我一直遇到的问题是我不知道如何最好地打印该行。我可以在 搜索到的单词后打印所有内容,但我不知道如何正确存储整行的内容。
我尝试使用 yyseek()
,但是当我编译时,我收到消息说 yyseek
是一个未定义的符号。
使用 yymore()
存储文本适用于行中匹配词之后的任何内容。
这是我目前的代码:
%option yylineno
%option noyywrap
%{
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *search_str = NULL;
char *curr_line = NULL;
%}
%x found
letter [a-zA-Z]
word {letter}+
line (.*)\n
%%
<INITIAL,found>{word} {
/* If a word matches the string that we are looking for, use the 'found'
* condition, which will cause the line to be dumped at the end.
*/
yymore();
if (strcmp(search_str, yytext) == 0) {
BEGIN(found);
}
}
<found>{line} {
yymore();
ECHO;
BEGIN(INITIAL);
}
. { }
\n {}
%%
int main(int argc, char *argv[])
{
if (argc > 1) {
unsigned int str_len = sizeof(argv[1]);
search_str = malloc(str_len + 1);
strcpy(search_str, argv[1]);
yylex();
free(search_str);
return 0;
}
printf("usage: ./a.out [search word]\n");
return 1;
}
这真的不是 flex 的好用例。我也不太清楚它会做你想做的事。 (因为我实际上并不知道你想要什么,所以我对此可能是错误的。但请注意以下几点:
Target line grep night grep -w night Your code
------------------- ---------- ------------- ---------
a night to remember Yes Yes Yes
a knight to forget Yes No No
night23 Yes No Yes
无论如何,您使用 yymore
的直觉是正确的。你只需要早点开始,这样整行都保留在令牌中。小麻烦是,当你需要检查一个单词时,你不能从yytext
开始检查;它包含到目前为止的整行。您必须检查最后 strlen(search_str)
个字符。以下代码确保它只执行一次计算,因为它需要完整扫描 search_str
。另请注意,它确保它不会超出 yytext
.
的开头
实际上,以下代码将文本分为三种标记:单词、非单词和换行符。只有换行符无法调用 yymore()
,因此当换行符规则触发时,yytext
包含整行。正如在您的代码中一样,一旦在一行中找到匹配项,该行的其余部分就会简单地添加到匹配项中。
(注意:我重写这个没有宏,宏一般被过度使用。我看不出有任何理由认为 {letter}
比 [[:alpha:]]
更具可读性,后者更有优势任何知道 flex 的人都清楚,而无需搜索您的特定定义。)
%x FOUND
%%
/* Indented lines before the first rule are put at the top of yylex */
int match_length = strlen(search_str);
[^[:alpha:]\n]+ { yymore(); }
[[:alpha:]]+ { yymore();
if (yyleng >= match_length
&& 0 == strcmp(yytext + yyleng - match_length,
search_str))
BEGIN(FOUND);
}
<INITIAL,FOUND>\n BEGIN(INITIAL);
<FOUND>.* printf("%s\n", yytext);
最后的奇怪之处在于处理未正确以换行符终止的输入。最后一个模式将打印带有换行符的行(即使没有换行符),换行符(如果有的话)将重新启动开始条件。
为了稍微提速,你可以在每次调用yymore()
时记住yyleng
的前一个值,这样yyleng - prev_yyleng
就是"this part"的长度的令牌。 (flex scanner 知道这个值但是不提供任何接口让你找到它,这有点烦人。但这不是什么大问题。)然后而不是检查到这一点的整个行是否足够长以使比较成为可能,您可以检查匹配的最后一个单词是否恰好是正确的长度,这种情况不太常见,因此需要更少的调用 strcmp
.
总而言之,这不是一个好策略。您可能会发现 strstr
比 flex 更快,并且与重复搜索同一目标的可能情况相比,它只是略微优化。更好的方法是实施或找到一种标准搜索算法:
我正在尝试创建一个带有 flex 的扫描仪,它的行为有点像 grep
。
基本上,我想做的是:给定一个词(常规文本,而不是正则表达式),在输入中找到包含该文本匹配项的任何行,然后打印包含该词的行。
我一直遇到的问题是我不知道如何最好地打印该行。我可以在 搜索到的单词后打印所有内容,但我不知道如何正确存储整行的内容。
我尝试使用 yyseek()
,但是当我编译时,我收到消息说 yyseek
是一个未定义的符号。
使用 yymore()
存储文本适用于行中匹配词之后的任何内容。
这是我目前的代码:
%option yylineno
%option noyywrap
%{
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *search_str = NULL;
char *curr_line = NULL;
%}
%x found
letter [a-zA-Z]
word {letter}+
line (.*)\n
%%
<INITIAL,found>{word} {
/* If a word matches the string that we are looking for, use the 'found'
* condition, which will cause the line to be dumped at the end.
*/
yymore();
if (strcmp(search_str, yytext) == 0) {
BEGIN(found);
}
}
<found>{line} {
yymore();
ECHO;
BEGIN(INITIAL);
}
. { }
\n {}
%%
int main(int argc, char *argv[])
{
if (argc > 1) {
unsigned int str_len = sizeof(argv[1]);
search_str = malloc(str_len + 1);
strcpy(search_str, argv[1]);
yylex();
free(search_str);
return 0;
}
printf("usage: ./a.out [search word]\n");
return 1;
}
这真的不是 flex 的好用例。我也不太清楚它会做你想做的事。 (因为我实际上并不知道你想要什么,所以我对此可能是错误的。但请注意以下几点:
Target line grep night grep -w night Your code
------------------- ---------- ------------- ---------
a night to remember Yes Yes Yes
a knight to forget Yes No No
night23 Yes No Yes
无论如何,您使用 yymore
的直觉是正确的。你只需要早点开始,这样整行都保留在令牌中。小麻烦是,当你需要检查一个单词时,你不能从yytext
开始检查;它包含到目前为止的整行。您必须检查最后 strlen(search_str)
个字符。以下代码确保它只执行一次计算,因为它需要完整扫描 search_str
。另请注意,它确保它不会超出 yytext
.
实际上,以下代码将文本分为三种标记:单词、非单词和换行符。只有换行符无法调用 yymore()
,因此当换行符规则触发时,yytext
包含整行。正如在您的代码中一样,一旦在一行中找到匹配项,该行的其余部分就会简单地添加到匹配项中。
(注意:我重写这个没有宏,宏一般被过度使用。我看不出有任何理由认为 {letter}
比 [[:alpha:]]
更具可读性,后者更有优势任何知道 flex 的人都清楚,而无需搜索您的特定定义。)
%x FOUND
%%
/* Indented lines before the first rule are put at the top of yylex */
int match_length = strlen(search_str);
[^[:alpha:]\n]+ { yymore(); }
[[:alpha:]]+ { yymore();
if (yyleng >= match_length
&& 0 == strcmp(yytext + yyleng - match_length,
search_str))
BEGIN(FOUND);
}
<INITIAL,FOUND>\n BEGIN(INITIAL);
<FOUND>.* printf("%s\n", yytext);
最后的奇怪之处在于处理未正确以换行符终止的输入。最后一个模式将打印带有换行符的行(即使没有换行符),换行符(如果有的话)将重新启动开始条件。
为了稍微提速,你可以在每次调用yymore()
时记住yyleng
的前一个值,这样yyleng - prev_yyleng
就是"this part"的长度的令牌。 (flex scanner 知道这个值但是不提供任何接口让你找到它,这有点烦人。但这不是什么大问题。)然后而不是检查到这一点的整个行是否足够长以使比较成为可能,您可以检查匹配的最后一个单词是否恰好是正确的长度,这种情况不太常见,因此需要更少的调用 strcmp
.
总而言之,这不是一个好策略。您可能会发现 strstr
比 flex 更快,并且与重复搜索同一目标的可能情况相比,它只是略微优化。更好的方法是实施或找到一种标准搜索算法: