C 风格注释中的 Python 正则表达式阅读

CRS*_*CRS 2 c python regex

我试图在 ac 文件中找到 c 风格的注释,但如果引号中碰巧有 // ,我就会遇到麻烦。这是文件:

/*My function
is great.*/
int j = 0//hello world
void foo(){
    //tricky example
    cout << "This // is // not a comment\n";
}
Run Code Online (Sandbox Code Playgroud)

它将与那个 cout 匹配。这是我到目前为止所拥有的(我已经可以匹配 /**/ 评论了)

fp = open(s)

p = re.compile(r'//(.+)')
txt = p.findall(fp.read())
print (txt)
Run Code Online (Sandbox Code Playgroud)

Cas*_*yte 7

第一步是确定哪些情况///*不得解释为注释子字符串的开头。例如,当它们位于字符串内(引号之间)时。为了避免引号(或其他东西)之间的内容,诀窍是将它们放在一个捕获组中并在替换模式中插入一个反向引用:

图案:

(
    "(?:[^"\\]|\\[\s\S])*"
  |
    '(?:[^'\\]|\\[\s\S])*'
)
|
//.*
|
/\*(?:[^*]|\*(?!/))*\*/
Run Code Online (Sandbox Code Playgroud)

替代品:

\1
Run Code Online (Sandbox Code Playgroud)

在线演示

由于引用的部分首先搜索,因此每次您找到//or 时/*...*/,您都可以确定您不在字符串内。

请注意,该模式是自愿的低效(由于(A|B)*子模式)以使其更易于理解。为了提高效率,您可以像这样重写它:

("(?=((?:[^"\\]+|\\[\s\S])*))\2"|'(?=((?:[^'\\]+|\\[\s\S])*))\3')|//.*|/\*(?=((?:[^*]+|\*(?!/))*))\4\*/
Run Code Online (Sandbox Code Playgroud)

(?=(something+))\1只是一种模拟原子组的方法 (?>something+)

在线演示

因此,如果您只想查找注释(而不是删除它们),最方便的是将模式的注释部分放在捕获组中并测试它是否不为空。以下模式已被修改(在 Jonathan Leffler 评论之后)来处理??/被预处理器解释为反斜杠字符的三合字母(我假设代码不是为-trigraphs选项编写的)并处理反斜杠后跟换行符允许在多行上格式化一行:

fp = open(s)

p = re.compile(r'''(?x)
(?=["'/])      # trick to make it faster, a kind of anchor
(?:
    "(?=((?:[^"\\?]+|\?(?!\?/)|(?:\?\?/|\\)[\s\S])*))\1" # double quotes string
  |
    '(?=((?:[^'\\?]+|\?(?!\?/)|(?:\?\?/|\\)[\s\S])*))\2' # single quotes string
  |
    (
        /(?:(?:\?\?/|\\)\n)*/(?:.*(?:\?\?|\\)/\n)*.* # single line comment
      |
        /(?:(?:\?\?/|\\)\n)*\*                       # multiline comment
        (?=((?:[^*]+|\*+(?!(?:(?:\?\?/|\\)\n)*/))*))\4
        \*(?:(?:\?\?/|\\)\n)*/             
    )
)
''')

for m in p.findall(fp.read()):
    if (m[2]):    
        print m[2]
Run Code Online (Sandbox Code Playgroud)

这些更改不会影响模式效率,因为正则表达式引擎的主要工作是查找以引号或斜线开头的位置。通过在模式开始处存在前瞻(?=["'/]),允许内部优化快速找到第一个字符,可以简化此任务。

另一种优化是使用模拟原子组,将回溯减少到最小,并允许在重复组内使用贪婪量词。

注意:C 中可能没有heredoc 语法!