【问题标题】:Python equivalent for 'grep -C N'?'grep -C N'的Python等效项?
【发布时间】:2015-12-02 14:28:54
【问题描述】:

所以现在我正在文件中寻找一些东西。我得到一个value 变量,它是一个相当长的字符串,带有换行符等。然后,我使用 re.findall(regex, value) 来查找正则表达式。正则表达式相当简单——类似于“abc de.*”。

现在,我不仅要捕获正则表达式的所有内容,还要捕获上下文(就像 grep-C 标志)。

所以,假设我将 value 转储到文件并在其上运行 grep,我会做的是 grep -C N 'abc de .*' valueinfile

如何在 Python 中实现相同的功能?我需要使用 Unicode 正则表达式/文本的答案。

【问题讨论】:

  • 添加示例文本以及您想从中提取什么?
  • @SIslam 文本无关紧要。我想要grep -C 的功能。我没有示例文本,我可以想出它,但恕我直言,这不是必需的,因为该工具定义了功能。
  • @IgnacioVazquez-Abrams 链接呢?
  • 使用双端队列来存储最后的 n 行(一旦存在很多行,每添加一个新行就弹出左列)。当您的正则表达式找到匹配项时,返回堆栈中的前 n 行,然后迭代 n 更多行并返回这些行。这使您不必在任何行上迭代两次(DRY),并且只在内存中存储最少的数据。

标签: python regex unicode


【解决方案1】:

我的方法是将文本块拆分为行列表。接下来,遍历每一行并查看是否有匹配项。如果匹配,则收集上下文行(发生在当前行之前和之后的行)并返回它。这是我的代码:

import re

def grep(pattern, block, context_lines=0):
    lines = block.splitlines()
    for line_number, line in enumerate(lines):
        if re.match(pattern, line):
            lines_with_context = lines[line_number - context_lines:line_number + context_lines + 1]
            yield '\n'.join(lines_with_context)

# Try it out
text_block = """One
Two
Three
abc defg
four
five
six
abc defoobar
seven
eight
abc de"""

pattern = 'abc de.*'

for line in grep(pattern, text_block, context_lines=2):
    print line
    print '---'

输出:

Two
Three
abc defg
four
five
---
five
six
abc defoobar
seven
eight
---
seven
eight
abc de
---

【讨论】:

    【解决方案2】:

    As recommended by Ignacio Vazquez-Abrams,使用 a deque 存储最后 n 行。一旦出现那么多行,就为添加的每个新行弹出左键。当您的正则表达式找到匹配项时,返回堆栈中之前的 n 行,然后再迭代 n 行并返回这些行。

    这使您不必在任何行上迭代两次(DRY),并且只在内存中存储最少的数据。您还提到了对 Unicode 的需求,因此处理文件编码并将 Unicode 标志添加到 RegEx 搜索很重要。此外,另一个答案使用 re.match() 而不是 re.search() ,因此可能会产生意想不到的后果。

    下面是一个例子。此示例仅对文件中的每一行进行一次迭代,这意味着不会再次查看也包含命中的上下文行。这可能是理想的行为,也可能不是理想的行为,但可以很容易地进行调整,以突出显示或以其他方式标记具有前一次命中上下文中其他命中的行。

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    import codecs
    import re
    from collections import deque
    
    def grep(pattern, input_file, context=0, case_sensitivity=True, file_encoding='utf-8'):
        stack = deque()
        hits = []
        lines_remaining = None
    
        with codecs.open(input_file, mode='rb', encoding=file_encoding) as f:
            for line in f:
                # append next line to stack
                stack.append(line)
    
                # keep adding context after hit found (without popping off previous lines of context)
                if lines_remaining and lines_remaining > 0:
                    continue  # go to next line in file
                elif lines_remaining and lines_remaining == 0:
                    hits.append(stack)
                    lines_remaining = None
                    stack = deque()
    
                # if stack exceeds needed context, pop leftmost line off stack 
                # (but include current line with possible search hit if applicable)
                if len(stack) > context+1:
                    last_line_removed = stack.popleft()
    
                # search line for pattern
                if case_sensitivity:
                    search_object = re.search(pattern, line, re.UNICODE)
                else:
                    search_object = re.search(pattern, line, re.IGNORECASE|re.UNICODE)
    
                if search_object:
                    lines_remaining = context
    
        # in case there is not enough lines left in the file to provide trailing context
        if lines_remaining and len(stack) > 0:
            hits.append(stack)
    
        # return list of deques containing hits with context
        return hits  # you'll probably want to format the output, this is just an example
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-05-28
      • 1970-01-01
      • 1970-01-01
      • 2011-05-26
      • 1970-01-01
      • 2012-04-22
      • 1970-01-01
      • 2017-08-27
      相关资源
      最近更新 更多