2012-10-06 110 views
9

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

#!/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') 

并且无法让它工作。如果任何人都可以解释我做错了什么,或者提供一些关于我应该尝试的建议,我会非常感激。此外,如果您有任何改善搜索的建议,我也会很感激。我一直在使用一个测试文件,其中我想查找的字符串是行中唯一的文本,但在我的真实文件中,我需要的字符串仍然在行首,但后面跟着一堆其他文本,所以我认为我现在设置的东西的方式也不会有效。

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

+2

在第二个例子中,它看起来像你的循环体没有缩进..是复制/粘贴错误还是你实际上有什么? – Collin

+2

您可能应该查看'enumerate'函数和'for iterable'构造。 –

+0

@Collin你是对的问题是缩进。我可能看了两个小时的代码,从未注意到!谢谢! – Andreanna

回答

17

正如@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)现在,这将导致IndexError比500线较短的文件。而不是硬编码循环计数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') 

该文件将被打开,并访问您的fwith块内。离开程序块后,文件将自动关闭,所以最终不会忘记关闭文件。

在你的情况下,你打开两个文件。这可以通过只使用两个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使用之前的Macs \r,而Windows使用\r\n。因此,Lines[i] == searchquery将不匹配Mac或Windows行尾。 file.readline()可以处理所有这三个,但因为它保留了行结束处的所有行结束符,所以比较将失败。

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

(阅读使用file.read()文件和:这是通过使用str.strip(),这将剥离开头和末尾的所有空格的字符串,比较搜索模式没有结束,以该行解决使用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块来处理这种情况是正确的。

+2

哇,这非常有帮助。感谢您的详细解释! – Andreanna

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 
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()) 

所有这些都假定您的目标线以外至少有两条附加线。

+0

“所有这些都假定您的目标线以外至少有两条附加线。” - 值得指出的是,处理这种情况的方法是使用['try ... except'块](http://docs.python.org/reference/compound_stmts.html#the-try-statement) :'try:f2.write(f1.next());除了StopIteration:pass'或类似之外。 –

+0

你应该永远不使用裸'except:'语句,总是列出你想明确捕捉的异常(在这种情况下是'IndexError')。使用“except:”可能会导致你无法想象的事情,并且需要处理与你期望的情况不同的事情。在这种情况下,应该提出异常,导致代码停止而不是继续并可能导致损坏。 –

1

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

如何在诊断这种情况时避免任何时髦的无法预料的问题的绝对路径呢?

此建议仅从诊断角度出发。另外检查OS X dtrace和dtruss。

见:大数据时Equivalent of strace -feopen <command> on mac os X

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行放入列表中。如果是这样的话,你应该减小尺寸,并根据需要尽可能多的读/写步骤来编写整个东西。

相关问题