сопоставление и замена подстановочных знаков и/ или сопоставление подстановочных знаков с несколькими строками

#python #regex #wildcard

#python #регулярное выражение #подстановочный знак

Вопрос:

У меня есть два очень связанных вопроса:

  • Я хочу сопоставить шаблон строки с подстановочным знаком (т. Е. содержащим один или несколько ‘*’ или ‘?’), А затем сформировать строку замены со вторым шаблоном подстановочных знаков. Там заполнители должны ссылаться на одну и ту же согласованную подстроку (как, например, в команде копирования DOS)

    Пример: pattern='*.txt' и replacement-pattern='*.doc' : я хочу aaa.txt —> aaa.doc и xx.txt.txt —> xx.txt.doc

    В идеале это будет работать с несколькими произвольно размещенными подстановочными знаками: например, pattern='*.*' и replacement-pattern='XX*.*' .

    Конечно, нужно применять некоторые ограничения (например, жадную стратегию). В противном случае шаблоны, такие как X*X*X , не являются уникальными для string XXXXXX .

  • или, альтернативно, сформировать множественное совпадение. То есть у меня есть один или несколько шаблонов подстановочных знаков, каждый из которых содержит одинаковое количество символов подстановки. Каждый шаблон сопоставляется с одной строкой, но символы подстановочных знаков должны ссылаться на один и тот же соответствующий текст.

    Пример: pattern1='*.txt' и pattern2='*-suffix.txt должен соответствовать паре string1='XX.txt' и string2='XX-suffix.txt' , но не string1='XX.txt' и string2='YY-suffix.txt'

    В отличие от первого, это более четко определенная проблема, поскольку она позволяет избежать проблемы неоднозначности, но, возможно, очень похожа.

Я уверен, что для этих задач существуют алгоритмы, однако я не могу найти ничего полезного.

Библиотека Python имеет fnmatch , но это не поддерживает то, что я хочу сделать.

Ответ №1:

Есть много способов сделать это, но я придумал следующее, которое должно сработать для вашего первого вопроса. Основываясь на ваших примерах, я предполагаю, что вы не хотите сопоставлять пробелы.

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

 import re

def replaceWildcards(string, pattern, replacementPattern):
    splitPattern = re.split(r'([*?])', pattern)
    splitReplacement = re.split(r'([*?])', replacementPattern)
    if (len(splitPattern) != len(splitReplacement)):
        raise ValueError("Provided pattern wildcards do not match")
    reg = ""
    sub = ""
    for idx, (regexPiece, replacementPiece) in enumerate(zip(splitPattern, splitReplacement)):
        if regexPiece in ["*", "?"]:
            if replacementPiece != regexPiece:
                raise ValueError("Provided pattern wildcards do not match")
            reg  = f"(\S{regexPiece if regexPiece == '*' else ''})" # Match anything but whitespace
            sub  = f"\{idx   1}" # Regex matches start at 1, not 0
        else:
            reg  = f"({re.escape(regexPiece)})"
            sub  = f"{replacementPiece}"
    return re.sub(reg, sub, string)
 

Пример вывода:

 replaceWildcards("aaa.txt xx.txt.txt aaa.bat", "*.txt", "*.doc")
# 'aaa.doc xx.txt.doc aaa.bat'

replaceWildcards("aaa10.txt a1.txt aaa23.bat", "a??.txt", "b??.doc")
# 'aab10.doc a1.txt aaa23.bat'

replaceWildcards("aaa10.txt a1-suffix.txt aaa23.bat", "a*-suffix.txt", "b*-suffix.doc")
# 'aaa10.txt b1-suffix.doc aaa23.bat'

replaceWildcards("prefix-2aaa10-suffix.txt a1-suffix.txt", "prefix-*a*-suffix.txt", "prefix-*b*-suffix.doc")
# 'prefix-2aab10-suffix.doc a1-suffix.txt
 

Обратите внимание, что для f-строк требуется Python> = 3.6.

Комментарии:

1. Отлично. Хорошая идея для создания регулярного выражения и использования re.sub . Я немного доработал (используйте . вместо S для сопоставления также с пробелами, сопоставляйте полную строку только с ^ и $ и косой чертой -экранируйте sub часть replacementPiece), и это работает очень хорошо.