我正在尝试创建一种具有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;
}
答案 0 :(得分:0)
这对于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);
最后的奇怪之处是处理未正确用换行符终止的输入。最后一种模式将用换行符(即使没有换行符)打印该行,而换行符(如果有一个换行符)将重新开始起始条件。
要稍微提高速度,您可以在每次调用yyleng
时记住yymore()
的先前值,这样yyleng - prev_yyleng
将是令牌“此部分”的长度。 (Flex扫描仪知道此值,但不提供任何接口供您查找它,这有点烦人。但这没什么大不了的。)然后而不是检查到目前为止的整行是否足够长为了使比较成为可能,您可以检查匹配的最后一个单词是否恰好是正确的长度,这种情况的出现频率会降低,从而减少对strcmp
的调用。
总而言之,这不是一个好的策略。您可能会发现strstr
比flex快,并且与重复搜索相同目标相比,它仅稍作优化。最好是实现或找到一种标准的搜索算法: