【问题标题】:Python Regex - non-greedy match does not workPython Regex - 非贪婪匹配不起作用
【发布时间】:2016-03-19 13:35:12
【问题描述】:

我有一个平面文件,其中有一个 C++ 函数名及其部分声明,如下所示:

virtual void NameSpace1::NameSpace2::ClassName1::function_name1(int arg1) const
void function_name2
void NameSpace2::NameSpace4::ClassName2::function_name3
function_name4

我正在尝试使用这一行单独提取函数名称:

fn_name = re.match(":(.*?)\(?", lines)

我可以理解为什么 function_name2function_name4 不匹配(因为没有前导 :。但我看到即使对于 function_name1function_name3,它也不会进行非贪婪匹配.fn_name.group()的输出是

:NameSpace2::ClassName1::function_name1

我有三个问题:

  1. 我希望只从第 1 行提取字符串“function_name1”,但非贪婪匹配似乎不起作用。为什么?
  2. 为什么没有提取第 3 行?
  3. 如何使用单个正则表达式从所有行中获取函数名称?

请帮忙。

【问题讨论】:

  • 您知道re.match 从字符串的开头开始匹配吗?您的所有字符串都没有以冒号开头。你试过re.search吗?
  • 懒惰匹配也不会影响正则表达式开始匹配的位置——它只会影响匹配的结束

标签: python regex python-2.7 non-greedy


【解决方案1】:

这很好用,至少在你的例子中:

^(?:\w+ +)*(?:\w+::)*(\w+)

即,在 Python 代码中:

import re

function_name = re.compile(r'^(?:\w+ +)*(?:\w+::)*(\w+)', re.MULTILINE)
matches = function_name.findall(your_txt)

# -> ['function_name1', 'function_name2', 'function_name3', 'function_name4']

外卖:如果你能用贪心匹配来做,那就用贪心匹配来做。


请注意,\w 对于 C 标识符不正确,但写下与这些标识符匹配的技术上正确的字符类不在问题之列。查找并使用正确的字符集而不是 \w

【讨论】:

    【解决方案2】:

    1) 始终使用r" " 字符串作为正则表达式。

    2)

    我正在尝试使用这一行单独提取函数名称:

    fn_name = re.match(":(.*?)\(?", lines)
    

    fn_name.group() 的输出是

    :NameSpace2::ClassName1::function_name1
    

    我没看到:

    import re
    
    line = "virtual void NameSpace1::NameSpace2::ClassName1::function_name1(int arg1) const"
    
    fn_name = re.search(r":(.*?)\(?", line)
    print(fn_name.group())
    
    --output:--
    :
    

    无论如何,如果您想了解非贪婪是如何工作的,请查看以下代码:

    import re
    
    line = "N----1----2"
    
    greedy_pattern = r"""
        N
        .*
        \d
    """
    
    match_obj = re.search(greedy_pattern, line, flags=re.X)
    print(match_obj.group())
    
    non_greedy_pattern = r"""
        N
        .*?
        \d
    """
    match_obj = re.search(non_greedy_pattern, line, flags=re.X)
    print(match_obj.group())
    
    --output:--
    N----1----2
    N----1
    

    非贪婪版本要求匹配.*的所有字符,直到遇到的第一个数字,而贪婪版本将尝试找到.*后跟一个数字的最长匹配项。

    3) 警告!没有正则表达式区域!

    func_names = [
    "virtual void NameSpace1::NameSpace2::ClassName1::function_name1(int arg1) const",
    "void function_name2",
    "void NameSpace2::NameSpace4::ClassName2::function_name3",
    "function_name4",
    ]
    
    for func_name in func_names:
        name = func_name.rsplit("::", 1)[-1]
    
        pieces = name.rsplit(" ", 1)
    
        if pieces[-1] == "const":
            name = pieces[-2]
        else:
            name = pieces[-1]
    
        name = name.split('(', 1)[0]
        print(name)
    
    --output:--
    function_name1
    function_name2
    function_name3
    function_name4
    

    【讨论】:

      【解决方案3】:
      1. 我希望只从第 1 行提取字符串“function_name1”,但非贪婪匹配似乎不起作用。为什么?

      这是您的正则表达式":(.*?)\(?"的结果

      我认为你的正则表达式是“太懒了”。它将仅匹配:,因为(.*?) 代表匹配任何字符“尽可能少”,然后正则表达式引擎选择匹配零字符。如您所料,直到\(? 才会匹配,因为? 仅表示“可选”

      1. 为什么没有提取第 3 行?

      因为我已经测试了你的正则表达式。不仅是第三行,它根本不起作用。

      1. 如何使用单个正则表达式从所有行中获取函数名称?

      你可以从这个最小的例子开始

      (?:\:\:|void\s+)(\w+)(?:\(|$)|(function_name4)
      

      (?:\:\:|void\s+) 代表以您的函数名称开头的任何内容,(?:\(|$) 代表您的函数名称后面的任何内容。

      请注意,function_name4 假设由于缺少模式而被显式声明。

      见:DEMO

      【讨论】:

        【解决方案4】:

        在尝试从“N foo bar N----1----2”中捕获“N----1”时,我曾经被类似的东西难住了。添加前导 .* 可以得到所需的结果。

        import re
        line = "N foo bar N----1----2"
        match_obj = re.search(r'(N.*?\d)', line)
        print(match_obj.group(1)) 
        
        match_obj = re.search(r'.*(N.*?\d)', line)
        print(match_obj.group(1))
        
        --output:--
        N foo bar N----1
        N----1
        

        【讨论】:

          猜你喜欢
          • 2017-05-07
          • 1970-01-01
          • 1970-01-01
          • 2012-06-27
          • 2018-10-22
          • 1970-01-01
          • 2017-10-16
          • 2015-02-11
          • 1970-01-01
          相关资源
          最近更新 更多