Заменить строки в файлах Python

Как вы можете заменить соответствие данной рекурсивно в заданной директории и ее подкаталогах?

Псевдо-код

import os
import re
from os.path import walk
for root, dirs, files in os.walk("/home/noa/Desktop/codes"):
        for name in dirs:
                re.search("dbname=noa user=noa", "dbname=masi user=masi")
                   // I am trying to replace here a given match in a file

Ответы

Ответ 1

Поместите весь этот код в файл с именем mass_replace. В Linux или Mac OS X вы можете сделать chmod +x mass_replace, а затем просто запустить это. В Windows вы можете запустить его с помощью python mass_replace, за которым следуют соответствующие аргументы.

#!/usr/bin/python

import os
import re
import sys

# list of extensions to replace
DEFAULT_REPLACE_EXTENSIONS = None
# example: uncomment next line to only replace *.c, *.h, and/or *.txt
# DEFAULT_REPLACE_EXTENSIONS = (".c", ".h", ".txt")

def try_to_replace(fname, replace_extensions=DEFAULT_REPLACE_EXTENSIONS):
    if replace_extensions:
        return fname.lower().endswith(replace_extensions)
    return True


def file_replace(fname, pat, s_after):
    # first, see if the pattern is even in the file.
    with open(fname) as f:
        if not any(re.search(pat, line) for line in f):
            return # pattern does not occur in file so we are done.

    # pattern is in the file, so perform replace operation.
    with open(fname) as f:
        out_fname = fname + ".tmp"
        out = open(out_fname, "w")
        for line in f:
            out.write(re.sub(pat, s_after, line))
        out.close()
        os.rename(out_fname, fname)


def mass_replace(dir_name, s_before, s_after, replace_extensions=DEFAULT_REPLACE_EXTENSIONS):
    pat = re.compile(s_before)
    for dirpath, dirnames, filenames in os.walk(dir_name):
        for fname in filenames:
            if try_to_replace(fname, replace_extensions):
                fullname = os.path.join(dirpath, fname)
                file_replace(fullname, pat, s_after)

if len(sys.argv) != 4:
    u = "Usage: mass_replace <dir_name> <string_before> <string_after>\n"
    sys.stderr.write(u)
    sys.exit(1)

mass_replace(sys.argv[1], sys.argv[2], sys.argv[3])

EDIT: я изменил приведенный выше код из исходного ответа. Есть несколько изменений. Во-первых, mass_replace() теперь вызывает re.compile(), чтобы предварительно скомпилировать шаблон поиска; во-вторых, чтобы проверить, какое расширение имеет файл, мы теперь передаем кортеж расширений файлов .endswith(), а не вызываем .endswith() три раза; в-третьих, теперь он использует оператор with, доступный в последних версиях Python; и, наконец, file_replace() теперь проверяет, найден ли шаблон в файле и не переписывает файл, если шаблон не найден. (Старая версия будет переписывать каждый файл, изменяя временные метки, даже если выходной файл был идентичен входному файлу, это было неэлегантно.)

EDIT: я изменил это значение на значение по умолчанию, чтобы заменить каждый файл, но с одной строкой, которую вы можете редактировать, чтобы ограничить ее конкретными расширениями. Я думаю, что заменять каждый файл является более полезным по умолчанию. Это можно расширить с помощью списка расширений или имен файлов, которые нельзя коснуться, варианты, чтобы сделать регистр нечувствительным и т.д.

EDIT: в комментарии @asciimo указал на ошибку. Я отредактировал это, чтобы исправить ошибку. str.endswith() документируется, чтобы принять кортеж строк, чтобы попытаться, но не список. Исправлена. Кроме того, я сделал пару функций, которые принимают необязательный аргумент, чтобы вы могли пройти в кортеже расширений; это должно быть довольно легко изменить, чтобы принять аргумент командной строки, чтобы указать, какие расширения.

Ответ 2

Вам действительно нужны регулярные выражения?

import os

def recursive_replace( root, pattern, replace )
    for dir, subdirs, names in os.walk( root ):
        for name in names:
            path = os.path.join( dir, name )
            text = open( path ).read()
            if pattern in text:
                open( path, 'w' ).write( text.replace( pattern, replace ) )

Ответ 3

Конечно, если вы просто хотите сделать это без кодирования, используйте find и xargs:

find /home/noa/Desktop/codes -type f -print0 | \
xargs -0 sed --in-place "s/dbname=noa user=noa/dbname=masi user=masi"

(И вы могли бы сделать это с помощью find -exec или что-то еще, но я предпочитаю xargs.)

Ответ 4

Вот как я могу найти и заменить строки в файлах с помощью python. Это простая небольшая функция, которая будет рекурсивно искать каталоги для строки и заменять ее на строку. Вы также можете ограничить файлы определенным расширением файла, как показано ниже.

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

Ответ 5

это должно работать:

import re, os
import fnmatch
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("namelist.wps", 'a') as out:
               with open("namelist.wps", 'r') as readf:
                   for line in readf:
                       line = re.sub(r"dbname=noa user=noa", "dbname=masi user=masi", line)
                       out.write(line)