在flex中打印匹配单词的行

时间:2018-09-23 19:31:16

标签: c flex-lexer lex

我正在尝试创建一种具有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;
}

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快,并且与重复搜索相同目标相比,它仅稍作优化。最好是实现或找到一种标准的搜索算法: