【问题标题】:Python - repetitive blocks of textPython - 重复的文本块
【发布时间】:2017-12-18 15:34:24
【问题描述】:

我是 Python 新手,但我使用 Perl 已经有一段时间了。在 Perl 中,为了将文件搜索限制为特定的文本块,我会写如下内容:

if (/start_line/ ... /end_line/) {
   do something here
}

一旦/start_line/ 正则表达式匹配,条件/start_line/ ... /end_line/ 开始为真,然后继续为真,直到/end_line/ 正则表达式匹配之后。在逐行读取输入的循环中,这将对起始行和结束行之间的所有行执行 if 块。

如何在 Python 中表达相同的条件?

【问题讨论】:

  • “将文件搜索限制为特定的文本块”非常模糊,我们不一定知道足够的 perl 来知道这个“if (/start_line/ ... /end_line/” )" 的东西真的 确实如此。此外,最佳答案可能取决于上下文(文件中的内容,您希望如何使用它等)。
  • 这在 Perl 中被称为“触发器运算符”(它是两个点 ..,而不是三个点)。谷歌“Python中的触发器运算符”了解更多信息
  • @beasy:它是要么两个点三个点,这取决于您希望它在范围末尾的行为方式。跨度>
  • @bruno: “我们不一定知道足够的 perl 来知道这个……东西的真正作用” 这很好,但这不是回避询问的理由问题只是因为有些人(大概像你)不知道答案。
  • @Borodin 我的意思是 OP 不应该问这个吗???请重新阅读,我只是要求澄清他正在尝试做什么,以便他有更多机会得到一个好的答案。

标签: python perl


【解决方案1】:

如果你尝试这样的事情会怎样?

start_line = "line 1"
end_line = "line 2"
in_block = False
line_block = []

with open("file.txt") as search:
    for line in search:
        line = line.rstrip()  # remove '\n' at end of line
        if line == start_line:
            in_block = True
        elif line == end_line:
            line_block.append(line)
            in_block = False

       if in_block:
           line_block.append(line)

【讨论】:

  • 你不应该根据疯狂的猜测发布答案。
  • @brunodesthuilliers 仅仅因为你不知道 Perl 并不意味着其他人不知道。这个答案是 Perl 的触发器运算符 ... 到 Python 的相当准确的翻译。这不是一个疯狂的猜测。
  • 第二个错误。如果一个搜索的块不存在,那么你就会崩溃,如果有几个,你只报告第一个。
【解决方案2】:

您指的是 Perl 的触发器运算符 (..)。基本上,它在遇到第一个条件时将布尔标志设置为 true,并在遇到第二个条件(包括开始行和结束行)后返回 false。这样看,实现起来相当简单。

import re

flip = False;
for line in open(filename):
    if not flip and re.match('start-text',line): flip = True
    if flip:
        print(line)
        if re.match('end-text',line): flip = False

【讨论】:

  • 由于某种原因我不能让它工作,但似乎更简单!
  • @melpomene,已修复。
  • 我还意外地将 args 翻转到了正则表达式匹配。这就是为什么它不适用于 OP。现已修复
【解决方案3】:

Perl 解决方案实现了触发器运算符,它保留了连续循环之间的状态。其他解决方案已经通过更新标志变量来实现这一点。还可以编写一个类,以便将先前匹配的状态保留在实例变量中。下面给出一个例子。这是最接近优雅 Perl 语法的方法

#Fileblock.py
import re

class Block_Extract:
    def __init__(self):
        self.state = False
    def test(self, lines, start, end):
        if not self.state:
            self.m1 = re.search(start, lines)
        self.m2 = re.search(end, lines)
        if self.m1 and not self.m2:
            self.state = True
            return self.state
        if self.m2:
            self.state = False
            return True


start = "line3"
end = "line7"
fileblock = Block_Extract()
with open("Block_Test") as fp:
    for lines in fp:
        lines = lines.rstrip()
        if fileblock.test(lines, start, end):
            print lines

$ cat Block_Test 
This is line1
This is line2
This is line3
This is line4
This is line5
This is line6
This is line7
This is line8
This is line9
This is line10
$ python Fileblock.py 
This is line3
This is line4
This is line5
This is line6
This is line7

【讨论】:

  • 不知何故我无法理解类,但这个解决方案也有效!干杯
【解决方案4】:

我最初编写了一个生成器,它从一开始就产生了行 only 匹配块(见编辑历史),但一个编辑让我 重新考虑我的第一个建议,因为预期的行为是 在每个匹配的文本块上执行 if 正文。

我的新提案又是一个生成器,当然,默认情况下 从匹配的文本块“永远”产生行,但是,使用 可选关键字参数,也可以处理匹配的块 最大次数 (count)。

接下来是概念证明

def from_beg_to_end(filename, beg, end, count=0):

    '''Yields the lines from `filename` like in `sed -n /beg/,/end/p`

    By default (for `count=0`) if the file contains multiple blocks
    all the blocks are output, for `count` greater than zero the number
    of blocks whose lines are returned is _at most_ `count.

    Example of use:

    for line in from_beg_to_end(filename, 'a', b'):
        ...```

    inside = False
    for line in open(filename):
        if not inside:
            if beg in line: inside = True
        if inside:
            yield line
            if end in line:
                count = count-1
                if count==0: return
                inside = False

使用简单的字符串匹配。修改代码应该很容易 以上支持正则表达式。

【讨论】:

    猜你喜欢
    • 2017-04-27
    • 2021-09-30
    • 2019-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多