代码之家  ›  专栏  ›  技术社区  ›  selbie

找到一个模式,但不在C++注释中

  •  1
  • selbie  · 技术社区  · 6 年前

    我有一个regex,它在一个大型代码库中搜索用作类型或变量的特定标记的用法。假设令牌是“foo”,我想把它单独作为一个工作来查找。

    我的初始regex是:

    foo$|foo\s|foo\[|foo\*|<foo|foo>
    

    匹配项:行尾的foo、带空格的foo、foo指针、集合中的foo等…

    我想 排除 C++注释BOCK内的实例。例如在下面的实例中。

    // consume the foo and read another.
    

    我试过用否定的lookahead来修改regex,但这似乎行不通。

    (?!\/\/).*(foo$|foo\s|foo\[|foo\*|<foo|foo>)
    

    有人知道如何在正则表达式中执行此操作吗?

    更新:

    我只是想随意地过滤掉在目标模式之前可能有两个正斜线的线条。我不关心嵌套注释、C样式注释(/**/)或跨越多行的任何内容。

    2 回复  |  直到 6 年前
        1
  •  1
  •   melpomene    6 年前

    下面是一个相当全面的regex,用于满足您的需求(在Perl中测试):

    my $foo_regex = qr{
        \G
        (?>
            # // comment
            / (?: \\ \n )*+ / (?> \\ \n | [^\n] )*+
        |
            # /* comment */
            / (?: \\ \n )*+ \* (?> .*? \* (?: \\ \n )*+ / )
        |
            # 'c'
            ' (?: [^'\\\n] | \\ . )++ '
        |
            # "string"
            " (?: [^"\\\n] | \\ . )*+ "
        |
            # R"(raw string)"
            \b
            (?: (?> [LU] | u (?: \\ \n )*+ 8?+ ) (?: \\ \n )*+ )?+
            R
            (?: \\ \n )*+
            "
            (?: \\ \n )*+
            ( [^()\\\s]?+ )
            (?: \\ \n )*+
            ( [^()\\\s]?+ )
            (?: \\ \n )*+
            ( [^()\\\s]?+ )
            (?: \\ \n )*+
            ( [^()\\\s]?+ )
            (?: \\ \n )*+
            ( [^()\\\s]?+ )
            (?: \\ \n )*+
            ( [^()\\\s]?+ )
            (?: \\ \n )*+
            ( [^()\\\s]?+ )
            (?: \\ \n )*+
            ( [^()\\\s]?+ )
            (?: \\ \n )*+
            ( [^()\\\s]?+ )
            (?: \\ \n )*+
            ( [^()\\\s]?+ )
            (?: \\ \n )*+
            ( [^()\\\s]?+ )
            (?: \\ \n )*+
            ( [^()\\\s]?+ )
            (?: \\ \n )*+
            ( [^()\\\s]?+ )
            (?: \\ \n )*+
            ( [^()\\\s]?+ )
            (?: \\ \n )*+
            ( [^()\\\s]?+ )
            (?: \\ \n )*+
            ( [^()\\\s]?+ )
            (?: \\ \n )*+
            \(
            (?>
                .*?
                \)
                (?: \\ \n )*+
                \g{-16}
                (?: \\ \n )*+
                \g{-15}
                (?: \\ \n )*+
                \g{-14}
                (?: \\ \n )*+
                \g{-13}
                (?: \\ \n )*+
                \g{-12}
                (?: \\ \n )*+
                \g{-11}
                (?: \\ \n )*+
                \g{-10}
                (?: \\ \n )*+
                \g{-9}
                (?: \\ \n )*+
                \g{-8}
                (?: \\ \n )*+
                \g{-7}
                (?: \\ \n )*+
                \g{-6}
                (?: \\ \n )*+
                \g{-5}
                (?: \\ \n )*+
                \g{-4}
                (?: \\ \n )*+
                \g{-3}
                (?: \\ \n )*+
                \g{-2}
                (?: \\ \n )*+
                \g{-1}
                (?: \\ \n )*+
                "
            )
        |
            # / (not starting a comment)
            / (?! (?: \\ \n )*+ [/*] )
        |
            # identifier
            \w (?: (?: \\ \n )*+ \w )*+
        |
            # arbitrary other character
            [^/"'\w]
        )*?
        \b
        (
            f
            (?: \\ \n )*+
            o
            (?: \\ \n )*+
            o
        )
        (?!
            (?: \\ \n )*+
            \w
        )
    }xms;
    

    综合考虑的并发症概述:

    • "foo" , 'foo' , // foo , /* foo */ 不是 foo ,但分别是字符串文本、多字符常量、单行注释和块注释。
    • /* " */ , // " , " /* " , '//' 等分别是注释、注释、字符串文字和多字符常量。这意味着您不能分阶段过滤掉字符串文本、注释等;您必须同时分析它们,以避免将带引号的构造的内容误认为是另一个带引号的构造的分隔符。
    • 反斜杠换行符组合必须被忽略(就像它们在源文件中不存在一样):

        /\
        * this is a comment */
        /\
        / and so is this
        foo\
        bar  // this is a single identifier, 'foobar'
        f\
        oo  // ... but this is 'foo'
        "this is a string\\
        " <- that's not the end of the string; this is: "
      
    • 这个regex的很大一部分处理表单的原始字符串 R"delim(...)delim" 结合任意反斜杠新行对,可以散布在任何地方。幸运的是,C++指定最多16个自定义分隔符字符的上界,否则我们将不得不使用运行时代码执行/动态正则表达式生成。
    • 不处理三角图。如果要添加支持,请从更改 \\ 在正则表达式中 (?> \\ | \?\?/ ) .

    更新:对于您的简化需求(查找单词 没有先于 // 在字符串中),您可以简单地 ^(?:[^/]|/(?!/))*?\bfoo\b .

        2
  •  1
  •   Wouter van Nifterick Andrey    6 年前

    正则表达式不是实现这一点的最佳工具。

    我写了一个C到Delphi的转换器( https://github.com/WouterVanNifterick/C-To-Delphi ,这里我确实在某些任务中使用了正则表达式,但是我的结论是正则表达式并不是您所要做的工作的正确工具。 我知道,因为我已经尝试过了,并且决定放弃正则表达式,因为事情变得过于复杂,而且事情不能可靠地工作。

    您可以快速创建90%的情况下都可以工作的内容,但是如果您想要正确处理嵌套的注释或看起来像注释的字符串,解析是唯一的选项。

    你不需要一个完整的C++解析器。您需要遍历所有字符,并跟踪您是在一个/**/块、一个“”字符串块中还是在//节中,并做您需要做的事情。