在Python中search并replace文件中的一行

我想遍历一个文本文件的内容,并进行search和replace一些行,并将结果写回到文件。 我可以先将整个文件加载到内存中,然后写回去,但这可能不是最好的方法。

在下面的代码中,执行此操作的最佳方法是什么?

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 

我猜这样的事情应该这样做。 它基本上将内容写入一个新文件,并用新文件replace旧文件:

 from tempfile import mkstemp from shutil import move 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)) #Remove original file remove(file_path) #Move new file move(abs_path, file_path) 

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

 import fileinput for line in fileinput.input("test.txt", inplace=True): print "%d: %s" % (fileinput.filelineno(), line), 

这里发生的是:

  1. 原始文件被移动到备份文件
  2. 标准输出被redirect到循环内的原始文件
  3. 因此,任何print语句都会回写到原始文件中

fileinput有更多的花里胡哨。 例如,它可以用于自动操作sys.args[1:]所有文件,而不必显式地迭代它们。 从Python 3.2开始,它还提供了一个方便的上下文pipe理器,用于with语句。


尽pipefileinput对于一次性脚本来说非常棒,但是我会在实际的代码中使用它,因为不可否认的是它不是很熟悉。 在真实(生产)代码中,花费更多的代码行来显式执行这个过程是非常值得的,从而使代码具有可读性。

有两个选项:

  1. 该文件不是太大,你可以把它全部读到内存。 然后closures文件,以书写模式重新打开文件并将修改的内容写回。
  2. 该文件太大,无法存储在内存中; 你可以将它移动到一个临时文件并打开它,逐行阅读,写回原始文件。 请注意,这需要两次存储。

这是另一个经过testing的例子,它将匹配search和replace模式:

 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.") 

这应该工作:(就地编辑)

 import fileinput # Does a list of files, and # redirects STDOUT to the file in question for line in fileinput.input(files, inplace = 1): print line.replace("foo", "bar"), 

基于Thomas Watnedal的回答。 但是,这并不能完全回答原始问题的线到线部分。 该function仍然可以在线到线的基础上进行replace

此实现replace文件内容而不使用临时文件,因此文件权限保持不变。

此外re.sub而不是replace,允许正则expression式replace而不是纯文本replace。

将文件读取为单个string而不是一行一行地进行多行匹配和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() 

正如lassevk所build议的,随时写出新文件,下面是一些示例代码:

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

如果你想要一个用其他文本替代任何文本的generics函数,这可能是最好的方法,特别是如果你是正则expression式的粉丝:

 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 ) 

一个更pythonic的方式将是使用像下面的代码的上下文pipe理器:

 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) 

你可以在这里find完整的代码片段。

扩展@ Kiran的答案,我同意这个答案更简洁和Pythonic,这增加了编解码器来支持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) 

创build一个新文件,从旧到新复制行,并在将行写入新文件之前进行replace。

如果你删除下面的缩进,它会search和replace成多行。 看下面的例子。

 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) 

使用hamishmcn的答案作为模板,我能够search匹配我的正则expression式的文件中的一行,并用空stringreplace它。

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