如何匹配行开始?

How to match the line begin?

我正在用 lex 编写 cat(1) 实用程序。 当我考虑如何实现选项 -n 时,即为每一行编号。 但我必须这样写:

^. {
printf("%8d  ", ++lino);
ECHO;
}

我知道行尾(EOL)可以匹配使用锚点$\n,所以我想知道是否有类似的东西可以匹配行首(BOL)锚点,所以我不必使用 ECHO;

(我同意 Joachim Pileborg 的评论,即 lex 而不是 实现 cat 的工具。这个答案的其余部分本着解释一下 lex 的精神。)

  1. 如果输入中有空行,所提供的 lex 程序将无法运行,因为 ^. 不匹配空行。 (在 lex 中,. 不匹配换行符。)因此,合理最小的 (f)lex 输入文件将是:

    %options noyywrap noinput nounput
    %%
      int lino = 0;
    ^(.|\n)    { printf("%8d   %c", ++lino, *yytext); }
    

    这里我只是打印出printf中匹配的token,相当于使用ECHO。所以它并不是真的 "eliminate" ECHO.

  2. (f)lex 规则必须至少匹配一个字符。因此,一个模式不可能仅由 $ 组成,就像一个模式仅由 ^(这是一个 BOL 锚点)组成一样。从这个意义上说,您问题的答案很简单 "no".

  3. 一个更容易理解(并且可能更有效)的解决方案是实际匹配每一行。这个解决方案 never 使用 ECHO,甚至在默认规则中也不使用,所以我告诉 flex 不要生成默认规则:

    %options noyywrap noinput nounput nodefault
    %%
      int lino = 0;
    .*\n?    { printf("%8d   %s", ++lino, yytext); }
    

    这不是很完美,因为它会截断包含 NUL 字符的行。 (也就是说,printf 将有效地截断该行;该行将被正确解析。)要修复它,有必要使用 fwrite 而不是 printf:

    %options noyywrap noinput nounput nodefault
    %%
      int lino = 0;
    .*\n?    { printf("%8d   %s", ++lino);
               fwrite(yytext, 1, yyleng, yyout); }
    

    换行符是可选的 (\n?),以防文件的最后一行没有以换行符结尾。因为 (f)lex 模式从不匹配零个字符,所以该规则实际上等同于更精确但更笨重的正则表达式 .*\n|.+.