我有~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')
也无法让它发挥作用。如果有人能够解释我做错了什么或提出一些关于我应该尝试的建议,我将非常感激。此外,如果您有任何建议使搜索更好,我也会很感激。我一直在使用一个测试文件,其中我想要找到的字符串是该行上唯一的文本,但在我的真实文件中,我需要的字符串仍然在行的开头,但后面是一堆其他文本,所以我认为我现在设置的方式也不会真正起作用。
谢谢,对不起,如果这是一个超级基本问题!
答案 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
应移出if
和else
块。你在两种情况下都这样做,所以把它放在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。
答案 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
大小,并根据需要进行尽可能多的读/写步骤来编写整个内容。