13

我有大约 200 个短文本文件 (50kb),它们都有类似的格式。我想在每个包含某个字符串的文件中找到一行,然后将该行加上接下来的三行(但不是文件中的其余行)写入另一个文本文件。为了做到这一点,我正在尝试自学python,并编写了一个非常简单粗暴的小脚本来尝试一下。我使用的是 2.6.5 版本,并从 Mac 终端运行脚本:

#!/usr/bin/env python

f = open('Test.txt')

Lines=f.readlines()
searchquery = 'am\n'
i=0

while i < 500:
    if Lines[i] == searchquery:
        print Lines[i:i+3]
        i = i+1
    else:
        i = i+1
f.close()

这或多或少有效并将输出打印到屏幕上。但我想将这些行打印到一个新文件中,所以我尝试了这样的事情:

f1 = open('Test.txt')
f2 = open('Output.txt', 'a')

Lines=f1.readlines()
searchquery = 'am\n'
i=0

while i < 500:
if Lines[i] == searchquery:
    f2.write(Lines[i])
    f2.write(Lines[i+1])
    f2.write(Lines[i+2])
    i = i+1
else:
    i = i+1
f1.close()
f2.close()

但是,没有任何内容写入文件。我也试过

from __future__ import print_function
print(Lines[i], file='Output.txt')

也无法让它发挥作用。如果有人能解释我做错了什么或提供一些关于我应该尝试什么的建议,我将非常感激。此外,如果您对改进搜索有任何建议,我也将不胜感激。我一直在使用一个测试文件,其中我要查找的字符串是行中唯一的文本,但在我的真实文件中,我需要的字符串仍然在行的开头,但后面是一堆其他文本,所以我认为我现在设置的方式也不会真正起作用。

谢谢,对不起,如果这是一个超级基本的问题!

4

5 回答 5

27

正如@ajon 所指出的,我认为您的代码除了缩进之外没有任何根本性的问题。缩进固定后,它对我有用。然而,有几个改进的机会。

1)在 Python 中,迭代事物的标准方法是使用for循环。使用for循环时,您无需定义循环计数器变量并自己跟踪它们以迭代事物。相反,你写这样的东西

for line in lines:
    print line

遍历字符串列表中的所有项目并打印它们。

2)在大多数情况下,这就是您的for循环的样子。但是,在某些情况下,您确实确实想要跟踪循环计数。你的情况就是这样的情况,因为你不仅需要那一行,还需要接下来的三行,因此需要使用计数器进行索引(lst[i])。为此,enumerate()它将返回一个项目列表及其索引,然后您可以在其上循环。

for i, line in enumerate(lines):
    print i
    print line
    print lines[i+7]

如果您要像示例中那样手动跟踪循环计数器,则有两件事:

3)i = i+1应该移出ifandelse块。你在这两种情况下都这样做,所以把它放在if/else. 在您的情况下,该else块不再做任何事情,并且可以被消除:

while i < 500:
    if Lines[i] == searchquery:
        f2.write(Lines[i])
        f2.write(Lines[i+1])
        f2.write(Lines[i+2])
    i = i+1

4)现在,这将导致IndexError文件少于 500 行。您应该使用正在迭代的序列的实际长度,而不是硬编码 500 的循环计数。len(lines)会给你那个长度。但是不要使用while循环,而是使用for循环并range(len(lst))遍历从零到len(lst) - 1.

for i in range(len(lst)):
    print lst[i]

5) open()可以用作上下文管理器,负责为您关闭文件。上下文管理器是一个相当高级的概念,但如果已经为您提供了它们,则使用起来非常简单。通过做这样的事情

with open('test.txt') as f:
    f.write('foo')

f该文件将在该with块内打开并可供您访问。离开区块后,文件将自动关闭,因此您不会忘记关闭文件。

在您的情况下,您正在打开两个文件。这可以通过使用两个with语句并嵌套它们来完成

with open('one.txt') as f1:
    with open('two.txt') as f2:
        f1.write('foo')
        f2.write('bar')

或者,在 Python 2.7 / Python 3.x 中,通过在单个with语句中嵌套两个上下文管理器:

    with open('one.txt') as f1, open('two.txt', 'a') as f2:
        f1.write('foo')
        f2.write('bar')

6)根据创建文件的操作系统,行尾不同。在类 UNIX 平台上,它是\n.OS X 之前的 Mac \r,Windows 使用\r\n. 所以这Lines[i] == searchquery与 Mac 或 Windows 行结尾不匹配。file.readline()可以处理所有这三个,但是因为它保留了行尾的任何行尾,所以比较将失败。这可以通过 using 解决str.strip(),它将去除开头和结尾的所有空白字符串,并比较没有行结尾的搜索模式:

searchquery = 'am'
# ...
            if line.strip() == searchquery:
                # ...

file.read()(使用和使用读取文件str.splitlines()将是另一种选择。)

但是,由于您提到您的搜索字符串实际上出现在该行的开头,让我们这样做,使用str.startswith()

if line.startswith(searchquery):
    # ...

7) Python 的官方风格指南PEP8建议CamelCase用于类,lowercase_underscore几乎所有其他东西(变量、函数、属性、方法、模块、包)。所以而不是Lines使用lines. 与其他方面相比,这绝对是一个小问题,但仍然值得尽早做好。


因此,考虑到所有这些事情,我会这样编写您的代码:

searchquery = 'am'

with open('Test.txt') as f1:
    with open('Output.txt', 'a') as f2:
        lines = f1.readlines()
        for i, line in enumerate(lines):
            if line.startswith(searchquery):
                f2.write(line)
                f2.write(lines[i + 1])
                f2.write(lines[i + 2])

正如@TomK 指出的那样,所有这些代码都假定如果您的搜索字符串匹配,那么它后面至少有两行。如果你不能依赖这个假设,那么使用try...except@poorsod 建议的块来处理这种情况是正确的方法。

于 2012-10-06T00:40:43.140 回答
3

我认为你的问题是底部文件的标签。

您需要从 ifLines[i]到 after缩进,i=i+1例如:

while i < 500:
    if Lines[i] == searchquery:
        f2.write(Lines[i])
        f2.write(Lines[i+1])
        f2.write(Lines[i+2])
        i = i+1
    else:
        i = i+1
于 2012-10-06T00:24:49.857 回答
1

ajon 有正确的答案,但只要您正在寻找指导,您的解决方案就不会利用 Python 可以提供的高级构造。怎么样:

searchquery = 'am\n'

with open('Test.txt') as f1:
  with open(Output.txt, 'a') as f2:

    Lines = f1.readlines()

    try:
      i = Lines.index(searchquery)
      for iline in range(i, i+3):
        f2.write(Lines[iline])
    except:
      print "not in file"

即使发生异常,两个“with”语句也会在最后自动关闭文件。

一个更好的解决方案是避免一次读取整个文件(谁知道它有多大?),而是逐行处理,在文件对象上使用迭代:

  with open('Test.txt') as f1:
    with open(Output.txt, 'a') as f2:
      for line in f1:
        if line == searchquery:
          f2.write(line)
          f2.write(f1.next())
          f2.write(f1.next())

所有这些都假设在您的目标线之外至少有两条额外的线。

于 2012-10-06T01:11:44.463 回答
1

您是否尝试过使用“Output.txt”以外的其他内容来避免任何与文件系统相关的问题?

在诊断时避免任何时髦的不可预见问题的绝对路径怎么样?

这个建议只是从诊断的角度来看。还可以查看 OS X dtrace 和 dtruss。

参见:mac os X 上的 strace -feopen <command> 等价物

于 2012-10-06T01:17:55.000 回答
0

处理大数据时,逐行写入可能会很慢。您可以通过一次读/写一堆行来加速读/写操作。

from itertools import slice

f1 = open('Test.txt')
f2 = open('Output.txt', 'a')

bunch = 500
lines = list(islice(f1, bunch)) 
f2.writelines(lines)

f1.close()
f2.close()

如果您的行太长并且取决于您的系统,您可能无法将 500 行放在一个列表中。如果是这种情况,您应该减小bunch大小并根据需要设置尽可能多的读/写步骤来编写整个内容。

于 2017-10-04T16:03:46.407 回答