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

And*_*nna 10 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()
Run Code Online (Sandbox Code Playgroud)

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

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()
Run Code Online (Sandbox Code Playgroud)

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

from __future__ import print_function
print(Lines[i], file='Output.txt')
Run Code Online (Sandbox Code Playgroud)

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

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

Luk*_*raf 24

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

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

for line in lines:
    print line
Run Code Online (Sandbox Code Playgroud)

迭代字符串列表中的所有项目并打印它们.

2)在大多数情况下,这就是你的for循环的样子.但是,在某些情况下,您确实希望跟踪循环计数.您的情况就是这种情况,因为您不仅需要一行而且需要接下来的三行,因此需要使用计数器进行索引(lst[i]).对于有enumerate(),它会返回一个项目列表它们的指数超过你,然后可以循环.

for i, line in enumerate(lines):
    print i
    print line
    print lines[i+7]
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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

for i in range(len(lst)):
    print lst[i]
Run Code Online (Sandbox Code Playgroud)

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

with open('test.txt') as f:
    f.write('foo')
Run Code Online (Sandbox Code Playgroud)

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

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

with open('one.txt') as f1:
    with open('two.txt') as f2:
        f1.write('foo')
        f2.write('bar')
Run Code Online (Sandbox Code Playgroud)

或者,在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')
Run Code Online (Sandbox Code Playgroud)

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

searchquery = 'am'
# ...
            if line.strip() == searchquery:
                # ...
Run Code Online (Sandbox Code Playgroud)

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

但是,既然你提到你的搜索字符串实际出现在行的开头,那么让我们这样做,使用str.startswith():

if line.startswith(searchquery):
    # ...
Run Code Online (Sandbox Code Playgroud)

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])
Run Code Online (Sandbox Code Playgroud)

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