Python - 以递归方式查找和替换文本文件中的字符串的方法

时间:2010-11-17 15:18:11

标签: python

我想以递归方式搜索包含文本文件子目录的目录,并用多行字符串的内容替换文件中每次出现的{$ replace}。如何用python实现这一目标?

[编辑]

到目前为止,我所拥有的是使用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

9 个答案:

答案 0 :(得分:47)

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)

这允许您执行以下操作:

findReplace("some_dir", "find this", "replace with this", "*.txt")

答案 1 :(得分:24)

结帐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)

上述解决方案存在缺陷,例如它会逐字地打开它找到的每个文件,或者每个文件都被完全读入内存的事实(如果你有一个1GB的文本文件会很糟糕),但它应该是是一个很好的起点。

如果您想要查找/替换比查找特定字符串更复杂的查找/替换,您也可能需要查看re module

答案 2 :(得分:7)

对于使用 Python 3.5 + 的用户,现在可以使用**recursive标记递归glob

以下是将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)

答案 3 :(得分:6)

为避免递归到.svn目录,os.walk()允许您更改dirs列表。要简化文件中的文本替换而不需要在内存中读取整个文件,可以使用fileinput module。要使用文件模式过滤文件名,您可以使用fnmatch module作为suggested by @David Sulpy

#!/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")

答案 4 :(得分:1)

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

答案 5 :(得分:0)

这是我的代码(我认为它与上面相同,但我将它包括在内以防万一有些巧妙的不同之处):

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)

它运行没有错误。 但是,z:\test中的文件未更改。 我已经加入了印刷语句,如print("got here"),但它们也没有打印出来。

答案 6 :(得分:0)

如何使用:

clean = ''.join([e for e in text if e != 'string'])

答案 7 :(得分:0)

使用:

pip3 install manip

这使您可以使用装饰器来创建类似以下内容的文件:

@manip(at='.php$', recursive=True) # to apply to subfolders
def replace_on_php(text, find, replacement):
    return text.replace(find, replacement)

现在,在提示符下,您应该可以拨打电话

replace_on_php('explode', 'myCustomExplode', path='./myPhPFiles', modify=True)

这应该使函数将自身应用于整个文件夹。

答案 8 :(得分:-3)

多个文件字符串更改

导入glob

表示glob.glob中的所有文件(&#39; * .txt&#39;):

for line in open(allfiles,'r'):
    change=line.replace("old_string","new_string")
    output=open(allfiles,'w')
    output.write(change)