代码之家  ›  专栏  ›  技术社区  ›  Martin

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

  •  22
  • Martin  · 技术社区  · 14 年前

    我想递归地搜索包含文本文件子目录的目录,并用多行字符串的内容替换文件中每次出现的{$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
    
    8 回复  |  直到 14 年前
        1
  •  27
  •   Eli Courtwright    14 年前

    退房 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
  •  51
  •   David Sulpy    13 年前

    步行很好。但是,看起来您需要对文件类型进行文件管理(如果您要遍历某个目录,我建议您这样做)。为此,您应该添加 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")
    
        3
  •  10
  •   Aaron N. Brock    7 年前

    对于那些使用 蟒蛇3.5+ 你现在可以使用 glob 递归地使用 ** 以及 recursive 旗帜。

    下面是一个替换 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)
    
        4
  •  6
  •   Community CDub    7 年前

    避免重复 .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")
    
        5
  •  1
  •   Jon Roland    10 年前

    苏比的回答很好,但不完整。用户可能希望通过一个entry小部件输入参数,因此我们可能会有类似的东西(也不完整,但作为练习留下):

    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()
    
        6
  •  0
  •   Narendra Pathai    12 年前

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

    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") 但他们也不会打印出来。

        7
  •  0
  •   Seraphina    7 年前

    不如用一下:

    clean = ''.join([e for e in text if e != 'string'])
    
        8
  •  0
  •   Kostynha    5 年前

    使用:

    pip3 install manip
    

    这允许您使用decorator创建如下内容:

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

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

        9
  •  -3
  •   LakshmanTeja    7 年前

    多个文件字符串更改

    导入全局

    对于glob.glob('*.txt')中的所有文件:

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