【问题标题】:Multiline regex match retrieving line numbers and matches多行正则表达式匹配检索行号和匹配
【发布时间】:2021-05-04 15:35:48
【问题描述】:

我正在尝试遍历文件中的所有行以匹配可以匹配的模式;

  1. 出现在文件中的任意位置
  2. 在同一个文件中多次出现
  3. 在同一行多次出现
  4. 对于一个正则表达式模式,我正在搜索的字符串可能分布在多行中

一个示例输入是;

new File()
new
File()
there is a new File()
new
    
    
    
File()
there is not a matching pattern here File() new
new File() test new File() occurs twice on this line

示例输出是;

new File() Found on line 1  
new File() Found on lines 2 & 3 
new File() Found on line 4 
new File() Found on lines 5 & 9 
new File() Found on line 11
new File() Found on line 11 
6 occurrences of new File() pattern in test.txt (Filename)

正则表达式模式看起来像;

pattern = r'new\s+File\s*\({1}\s*\){1}'

查看文档here,我可以看到 match、findall 和 finditer 都在字符串的开头返回匹配项,但我没有看到使用搜索函数的方法,该函数查看正则表达式的任何位置我们要搜索的字符串是多行的(上面我的要求中的第四个)。

足够简单,可以匹配每行不止一次出现的正则表达式;

示例输入:

line = "new File() new File()"

代码:

i = 0
matches = []
while i < len(line):
    while line:
        matchObj = re.search(r"new\s+File\s*\({1}\s*\){1}", line, re.MULTILINE | re.DOTALL)
        if matchObj:
            line = line[matchObj.end():]
            matches.append(matchObj.group())

print(matches)

打印以下匹配项 - 目前不包括行号等:

['new File()', 'new File()']

有没有办法用 Python 的正则表达式来做我正在寻找的事情?

【问题讨论】:

    标签: python python-3.x regex


    【解决方案1】:

    您可以首先在文本中找到所有\n 字符及其各自的位置/字符索引。由于每个\n...嗯...开始一个新行,因此此列表中每个值的索引指示找到的\n 字符终止的行号。然后搜索所有出现的模式并使用上述列表查找匹配的开始/结束位置...

    import re
    import bisect
    
    text = """new 
    File()
    aa new File()
    new
    File()
    there is a new File() and new
    File() again
    new
        
        
        
    File()
    there is not a matching pattern here File() new
    new File() test new File() occurs twice on this line
    """
    
    # character indices of all \n characters in text
    nl = [m.start() for m in re.finditer("\n", text, re.MULTILINE|re.DOTALL)]
    
    matches = list(re.finditer(r"(new\s+File\(\))", text, re.MULTILINE|re.DOTALL))
    match_count = 0
    for m in matches:
        match_count += 1
        r = range(bisect.bisect(nl, m.start()-1), bisect.bisect(nl, m.end()-1)+1)
        print(re.sub(r"\s+", " ", m.group(1), re.DOTALL), "found on line(s)", *r)
    print(f"{match_count} occurrences of new File() found in file....")
    

    输出:

    new File() found on line(s) 0 1
    new File() found on line(s) 2
    new File() found on line(s) 3 4
    new File() found on line(s) 5
    new File() found on line(s) 5 6
    new File() found on line(s) 7 8 9 10 11
    new File() found on line(s) 13
    new File() found on line(s) 13
    8 occurrences of new File() found in file....
    

    【讨论】:

    • 请注意 re.MULTILINE|re.DOTALL 在这里是多余的,因为没有 .^$ 模式可以使用这些选项修改其行为。
    【解决方案2】:

    可以先统计匹配前的换行数,再统计匹配值中的换行数,合并行号: 见Python demo

    import re
    s='new File()\nnew\nFile()\nthere is a new File()\nnew\n \n \n \nFile()\nthere is not a matching pattern here File() new\nnew File() test new File() occurs twice on this line'
    pattern = r'new\s+File\s*\(\s*\)'
    for m in re.finditer(pattern, s):
        linenums = [s[:m.start()].count('\n') + 1]
        for _ in range(m.group().count('\n')):
            linenums.append(linenums[-1] + 1)
        print('{} Found on line {}'.format(re.sub(r'\s+', ' ', m.group()), ", ".join(map(str,linenums))))
    

    请参阅online Python demo

    输出:

    new File() Found on line 1
    new File() Found on line 2, 3
    new File() Found on line 4
    new File() Found on line 5, 6, 7, 8, 9
    new File() Found on line 11
    new File() Found on line 11
    

    【讨论】:

      猜你喜欢
      • 2017-07-26
      • 1970-01-01
      • 1970-01-01
      • 2021-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多