使用python将特定行从一个文件写入另一个文件

时间:2012-10-06 00:18:12

标签: python

我有~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')

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

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

5 个答案:

答案 0 :(得分:23)

正如@ajon指出的那样,除了缩进之外,我认为你的代码没有任何根本性的错误。随着缩进修复它对我有用。然而,有几个改进的机会。

1)在Python中,迭代事物的标准方法是使用for loop。使用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应移出ifelse块。你在两种情况下都这样做,所以把它放在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)现在,这将导致文件短于500行的IndexError。您应该使用要迭代的序列的实际长度,而不是将循环计数硬编码为500。 len(lines)会给你那个长度。但是,不要使用while循环,而是使用for循环和range(len(lst))迭代范围从零到len(lst) - 1的列表。

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

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

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使用\r之前的Mac,Windows使用\r\n。因此Lines[i] == searchquery与Mac或Windows行结尾不匹配。 file.readline()可以处理所有三个,但因为它保留了行尾的任何行结尾,所以比较将失败。这是通过使用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指出的那样,所有这些代码都假设如果你的搜索字符串匹配,那么它后面至少有两行。如果你不能依赖这个假设,那么使用像@poorsod这样的try...except块来处理这种情况是正确的方法。

答案 1 :(得分:2)

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

您需要在Lines[i]之后缩进,直到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

答案 2 :(得分: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())

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

答案 3 :(得分:1)

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

在诊断时避免任何时髦无法预料的问题的绝对路径如何。

这个建议只是从诊断的角度出发。另请查看OS X dtrace和dtruss。

请参阅:Equivalent of strace -feopen < command > on mac os X

答案 4 :(得分: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大小,并根据需要进行尽可能多的读/写步骤来编写整个内容。