在 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 更快,并且与重复搜索同一目标的可能情况相比,它只是略微优化。更好的方法是实施或找到一种标准搜索算法: