在Python中搜索并替换文件中的一行

在Python中搜索并替换文件中的一行,python,file,Python,File,我想循环一个文本文件的内容,在某些行上进行搜索和替换,然后将结果写回文件。我可以先将整个文件加载到内存中,然后将其写回,但这可能不是最好的方法 在下面的代码中,最好的方法是什么 f = open(file) for line in f: if line.contains('foo'): newline = line.replace('foo', 'bar') # how to write this newline back to the file 创建一

我想循环一个文本文件的内容,在某些行上进行搜索和替换,然后将结果写回文件。我可以先将整个文件加载到内存中,然后将其写回,但这可能不是最好的方法

在下面的代码中,最好的方法是什么

f = open(file)
for line in f:
    if line.contains('foo'):
        newline = line.replace('foo', 'bar')
        # how to write this newline back to the file

创建一个新文件,将旧文件中的行复制到新文件中,并在将行写入新文件之前进行替换。

我想类似的操作应该可以做到这一点。它基本上将内容写入新文件,并用新文件替换旧文件:

from tempfile import mkstemp
from shutil import move, copymode
from os import fdopen, remove

def replace(file_path, pattern, subst):
    #Create temp file
    fh, abs_path = mkstemp()
    with fdopen(fh,'w') as new_file:
        with open(file_path) as old_file:
            for line in old_file:
                new_file.write(line.replace(pattern, subst))
    #Copy the file permissions from the old file to the new file
    copymode(file_path, abs_path)
    #Remove original file
    remove(file_path)
    #Move new file
    move(abs_path, file_path)

正如lassevk所建议的,边写边写新文件,下面是一些示例代码:

fin = open("a.txt")
fout = open("b.txt", "wt")
for line in fin:
    fout.write( line.replace('foo', 'bar') )
fin.close()
fout.close()

最短的方法可能是使用。例如,以下内容将行号添加到文件中:

import fileinput

for line in fileinput.input("test.txt", inplace=True):
    print('{} {}'.format(fileinput.filelineno(), line), end='') # for Python 3
    # print "%d: %s" % (fileinput.filelineno(), line), # for Python 2
这里发生的是:

  • 原始文件将移动到备份文件
  • 标准输出被重定向到循环中的原始文件
  • 因此,任何
    print
    语句都会写回原始文件
  • fileinput
    具有更多的功能。例如,它可以用来自动操作
    sys.args[1://code>中的所有文件,而不必显式地迭代它们。从Python3.2开始,它还提供了一个方便的上下文管理器,可在
    with
    语句中使用


    虽然
    fileinput
    非常适合一次性脚本,但我会小心在实际代码中使用它,因为它不太可读或熟悉。在实际(生产)代码中,只需再花费几行代码就可以使流程显式化,从而使代码可读

    有两种选择:

  • 该文件不太大,您可以将其完全读取到内存中。然后关闭文件,在写入模式下重新打开,并将修改后的内容写回
  • 文件太大,无法存储在内存中;您可以将它移到一个临时文件中,然后打开该文件,逐行读取它,然后写回原始文件。请注意,这需要两倍的存储空间

  • 下面是另一个经过测试的示例,它将匹配搜索和替换模式:

    import fileinput
    import sys
    
    def replaceAll(file,searchExp,replaceExp):
        for line in fileinput.input(file, inplace=1):
            if searchExp in line:
                line = line.replace(searchExp,replaceExp)
            sys.stdout.write(line)
    
    示例用法:

    replaceAll("/fooBar.txt","Hello\sWorld!$","Goodbye\sWorld.")
    
    这应该有效:(就地编辑)


    如果删除下面类似位置的缩进,它将在多行中搜索和替换。 例如,见下文

    def replace(file, pattern, subst):
        #Create temp file
        fh, abs_path = mkstemp()
        print fh, abs_path
        new_file = open(abs_path,'w')
        old_file = open(file)
        for line in old_file:
            new_file.write(line.replace(pattern, subst))
        #close temp file
        new_file.close()
        close(fh)
        old_file.close()
        #Remove original file
        remove(file)
        #Move new file
        move(abs_path, file)
    

    根据Thomas Watnedal的回答。 但是,这并不能准确回答原始问题的逐行部分。该功能仍然可以在线对线的基础上进行替换

    此实现在不使用临时文件的情况下替换文件内容,因此文件权限保持不变

    此外,re.sub而不是replace,只允许正则表达式替换而不是纯文本替换

    将文件作为单个字符串而不是逐行读取允许多行匹配和替换

    import re
    
    def replace(file, pattern, subst):
        # Read contents from file as a single string
        file_handle = open(file, 'r')
        file_string = file_handle.read()
        file_handle.close()
    
        # Use RE package to allow for replacement (also allowing for (multiline) REGEX)
        file_string = (re.sub(pattern, subst, file_string))
    
        # Write contents to file.
        # Using mode 'w' truncates the file.
        file_handle = open(file, 'w')
        file_handle.write(file_string)
        file_handle.close()
    

    一种更具python风格的方法是使用上下文管理器,如下面的代码:

    from tempfile import mkstemp
    from shutil import move
    from os import remove
    
    def replace(source_file_path, pattern, substring):
        fh, target_file_path = mkstemp()
        with open(target_file_path, 'w') as target_file:
            with open(source_file_path, 'r') as source_file:
                for line in source_file:
                    target_file.write(line.replace(pattern, substring))
        remove(source_file_path)
        move(target_file_path, source_file_path)
    

    您可以找到完整的代码段。

    如果您想要一个用其他文本替换任何文本的通用函数,这可能是最好的方法,特别是如果您是regex的粉丝:

    import re
    def replace( filePath, text, subs, flags=0 ):
        with open( filePath, "r+" ) as file:
            fileContents = file.read()
            textPattern = re.compile( re.escape( text ), flags )
            fileContents = textPattern.sub( subs, fileContents )
            file.seek( 0 )
            file.truncate()
            file.write( fileContents )
    

    使用hamishmcn的答案作为模板,我能够在文件中搜索与我的正则表达式匹配的行,并用空字符串替换它

    import re 
    
    fin = open("in.txt", 'r') # in file
    fout = open("out.txt", 'w') # out file
    for line in fin:
        p = re.compile('[-][0-9]*[.][0-9]*[,]|[-][0-9]*[,]') # pattern
        newline = p.sub('',line) # replace matching strings with empty string
        print newline
        fout.write(newline)
    fin.close()
    fout.close()
    

    扩展@Kiran的答案,我同意它更简洁、更通俗,这增加了支持UTF-8读写的编解码器:

    import codecs 
    
    from tempfile import mkstemp
    from shutil import move
    from os import remove
    
    
    def replace(source_file_path, pattern, substring):
        fh, target_file_path = mkstemp()
    
        with codecs.open(target_file_path, 'w', 'utf-8') as target_file:
            with codecs.open(source_file_path, 'r', 'utf-8') as source_file:
                for line in source_file:
                    target_file.write(line.replace(pattern, substring))
        remove(source_file_path)
        move(target_file_path, source_file_path)
    
    正如前面的回答中所提到的,这是非常简单的:

    import fileinput
    
    def replace_in_file(file_path, search_text, new_text):
        with fileinput.input(file_path, inplace=True) as f:
            for line in f:
                new_line = line.replace(search_text, new_text)
                print(new_line, end='')
    
    说明:

    • fileinput
      可以接受多个文件,但我更喜欢在处理每个文件时立即关闭它。因此,将单个
      文件路径
      放置在带有
      语句的
    • print
      语句在
      inplace=True
      时不打印任何内容,因为
      STDOUT
      正在转发到原始文件
    • end='
      print
      语句中删除中间空白新行
    可按如下方式使用:

    file_path = '/path/to/my/file'
    replace_in_file(file_path, 'old-text', 'new-text')
    

    我知道这里面只有两行,但是我觉得代码本身并不是很有表现力。因为如果你想一想,如果你不知道这个函数,在发生什么的过程中几乎没有线索。打印行号和行号与写入行号不同。。。如果你明白我的要点…这会写入文件。它将标准输出重定向到该文件。请看下面的关键点,这里是print语句末尾的逗号:它在print语句中添加另一个换行符(因为这一行已经有一个换行符)时起作用。但这一点并不十分明显(这就是为什么Python 3更改了该语法,幸运的是)。请注意,当您为文件提供一个打开钩子时,例如,当您尝试读取/写入UTF-16编码的文件时,这一点不起作用。对于python3,
    print(line,end='')
    示例使用提供了一个正则表达式,但是,行中的
    searchExp和
    line.replace都不是正则表达式操作。示例使用肯定是错误的。如果searchExp在line:line=line.replace(searchExp,replacexpr)
    中,您可以只编写
    line=line.replace(searchExp,replacexpr)
    。没有产生异常,这行代码保持不变。对我来说也很好。我曾遇到过许多与此非常相似的其他示例,但关键是使用了
    sys.stdout.write(line)
    。再次感谢!如果我使用这个,我的文件将变为空白。知道吗?我用的是+1。另外,如果收到运行时错误:input()已处于活动状态,则调用fileinput.close()。请注意,
    files
    应该是一个包含文件名的字符串。print会添加一个可能已经存在的换行符。为了避免这种情况,请在替换的末尾添加.rstrip(),而不是在input()中使用files arg,它可以是fileinput.input(inplace=1),并将脚本调用为>python replace.py myfiles*.txt
    file_path = '/path/to/my/file'
    replace_in_file(file_path, 'old-text', 'new-text')