Python - 在文本文件中递归查找和替换字符串的方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4205854/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Python - Way to recursively find and replace string in text files
提问by Martin
I want to recursively search through a directory with subdirectories of text files and replace every occurrence of {$replace} within the files with the contents of a multi line string. How can this be achieved with python?
我想递归搜索包含文本文件子目录的目录,并用多行字符串的内容替换文件中每次出现的 {$replace}。这如何用python实现?
[EDIT]
[编辑]
So far all I have is the recursive code using os.walk to get a list of files that are required to be changed.
到目前为止,我所拥有的只是使用 os.walk 获取需要更改的文件列表的递归代码。
import os
import sys
fileList = []
rootdir = "C:\test"
for root, subFolders, files in os.walk(rootdir):
if subFolders != ".svn":
for file in files:
fileParts = file.split('.')
if len(fileParts) > 1:
if fileParts[1] == "php":
fileList.append(os.path.join(root,file))
print fileList
采纳答案by Eli Courtwright
Check out os.walk:
查看os.walk:
import os
replacement = """some
multi-line string"""
for dname, dirs, files in os.walk("some_dir"):
for fname in files:
fpath = os.path.join(dname, fname)
with open(fpath) as f:
s = f.read()
s = s.replace("{$replace}", replacement)
with open(fpath, "w") as f:
f.write(s)
The above solution has flaws, such as the fact that it opens literally every file it finds, or the fact that each file is read entirely into memory (which would be bad if you had a 1GB text file), but it should be a good starting point.
上面的解决方案有缺陷,例如它会打开它找到的每个文件,或者每个文件都被完全读入内存(如果你有一个 1GB 的文本文件会很糟糕),但它应该是一个好初始点。
You also may want to look into the re moduleif you want to do a more complex find/replace than looking for a specific string.
如果您想要执行比查找特定字符串更复杂的查找/替换操作,您可能还需要查看re 模块。
回答by David Sulpy
os.walk is great. However, it looks like you need to filer file types (which I would suggest if you are going to walk some directory). To do this, you should add import fnmatch.
os.walk 很棒。但是,看起来您需要过滤文件类型(如果您要浏览某个目录,我建议您这样做)。为此,您应该添加import fnmatch.
import os, fnmatch
def findReplace(directory, find, replace, filePattern):
for path, dirs, files in os.walk(os.path.abspath(directory)):
for filename in fnmatch.filter(files, filePattern):
filepath = os.path.join(path, filename)
with open(filepath) as f:
s = f.read()
s = s.replace(find, replace)
with open(filepath, "w") as f:
f.write(s)
This allows you to do something like:
这允许您执行以下操作:
findReplace("some_dir", "find this", "replace with this", "*.txt")
回答by Ron
Here's my code (which I think is the same as the above but I'm including it just in case there's something subtly different about it):
这是我的代码(我认为它与上面的相同,但我将它包含在内,以防万一它有一些细微的不同):
import os, fnmatch, sys
def findReplace(directory, find, replace, filePattern):
for path, dirs, files in os.walk(os.path.abspath(directory)):
for filename in fnmatch.filter(files, filePattern):
filepath = os.path.join(path, filename)
with open(filepath) as f:
s = f.read()
s = s.replace(find, replace)
with open(filepath, "w") as f:
f.write(s)
it runs without error.
BUT, the file, in z:\testis unchanged.
I've put in print statements, like print("got here")but they don't print out either.
它运行没有错误。但是,文件 inz:\test没有改变。我已经放入了打印语句,print("got here")但它们也不打印出来。
回答by jfs
To avoid recursing into .svndirectories, os.walk()allows you to change the dirslist inplace. To simplify the text replacement in a file without requiring to read the whole file in memory, you could use fileinputmodule. And to filter filenames using a file pattern, you could use fnmatchmoduleas suggested by @David Sulpy:
为了避免递归到.svn目录中,os.walk()允许您dirs就地更改列表。为了简化文件中的文本替换而不需要读取内存中的整个文件,您可以使用fileinputmodule。并且要使用文件模式过滤文件名,您可以按照@David Sulpy 的建议使用fnmatch模块:
#!/usr/bin/env python
from __future__ import print_function
import fnmatch
import os
from fileinput import FileInput
def find_replace(topdir, file_pattern, text, replacement):
for dirpath, dirs, files in os.walk(topdir, topdown=True):
dirs[:] = [d for d in dirs if d != '.svn'] # skip .svn dirs
files = [os.path.join(dirpath, filename)
for filename in fnmatch.filter(files, file_pattern)]
for line in FileInput(files, inplace=True):
print(line.replace(text, replacement), end='')
find_replace(r"C:\test", "*.php", '{$replace}', "multiline\nreplacement")
回答by Jon Roland
Sulpy's answer is good but incomplete. The user would be likely to want to input the parameters through an entry widget, so we might have something more like this (also incomplete, but left as an exercise):
Sulpy 的回答很好但不完整。用户可能想要通过入口小部件输入参数,所以我们可能有更像这样的东西(也不完整,但留作练习):
import os, fnmatch
from Tkinter import *
fields = 'Folder', 'Search', 'Replace', 'FilePattern'
def fetch(entvals):
# print entvals
# print ents
entItems = entvals.items()
for entItem in entItems:
field = entItem[0]
text = entItem[1].get()
print('%s: "%s"' % (field, text))
def findReplace(entvals):
# print ents
directory = entvals.get("Folder").get()
find = entvals.get("Search").get()
replace = entvals.get("Replace").get()
filePattern = entvals.get("FilePattern").get()
for path, dirs, files in os.walk(os.path.abspath(directory)):
for filename in fnmatch.filter(files, filePattern):
# print filename
filepath = os.path.join(path, filename)
print filepath # Can be commented out -- used for confirmation
with open(filepath) as f:
s = f.read()
s = s.replace(find, replace)
with open(filepath, "w") as f:
f.write(s)
def makeform(root, fields):
entvals = {}
for field in fields:
row = Frame(root)
lab = Label(row, width=17, text=field+": ", anchor='w')
ent = Entry(row)
row.pack(side=TOP, fill=X, padx=5, pady=5)
lab.pack(side=LEFT)
ent.pack(side=RIGHT, expand=YES, fill=X)
entvals[field] = ent
# print ent
return entvals
if __name__ == '__main__':
root = Tk()
root.title("Recursive S&R")
ents = makeform(root, fields)
# print ents
root.bind('<Return>', (lambda event, e=ents: fetch(e)))
b1 = Button(root, text='Show', command=(lambda e=ents: fetch(e)))
b1.pack(side=LEFT, padx=5, pady=5)
b2 = Button(root, text='Execute', command=(lambda e=ents: findReplace(e)))
b2.pack(side=LEFT, padx=5, pady=5)
b3 = Button(root, text='Quit', command=root.quit)
b3.pack(side=LEFT, padx=5, pady=5)
root.mainloop()
回答by LakshmanTeja
Multiple files string change
多个文件字符串更改
import glob
导入全局
for allfiles in glob.glob('*.txt'):
对于 glob.glob('*.txt') 中的所有文件:
for line in open(allfiles,'r'):
change=line.replace("old_string","new_string")
output=open(allfiles,'w')
output.write(change)
回答by Aaron N. Brock
For those using Python 3.5+you can now use a globrecursively with the use of **and the recursiveflag.
对于使用Python 3.5+ 的用户,您现在可以通过使用和标志递归地使用glob。**recursive
Here's an example replacing hellowith worldfor all .txtfiles:
这里有一个例子替换hello用world的所有.txt文件:
for filepath in glob.iglob('./**/*.txt', recursive=True):
with open(filepath) as file:
s = file.read()
s = s.replace('hello', 'world')
with open(filepath, "w") as file:
file.write(s)
回答by Seraphina
How about just using:
如何只使用:
clean = ''.join([e for e in text if e != 'string'])
回答by Kostynha
Use:
用:
pip3 install manip
This lets you use a decorator to create something like:
这让你可以使用装饰器来创建类似的东西:
@manip(at='.php$', recursive=True) # to apply to subfolders
def replace_on_php(text, find, replacement):
return text.replace(find, replacement)
Now in your prompt you should be able to call
现在在您的提示中,您应该可以调用
replace_on_php('explode', 'myCustomExplode', path='./myPhPFiles', modify=True)
and this should make the function apply itself on the entire folder.
这应该使该功能适用于整个文件夹。

