Преобразование строк ввода пользователя в исходный строковый литерал для создания регулярного выражения
Я знаю, что есть некоторые сообщения о преобразовании строки в строковый литерал строки, но ни один из них не помогает моей ситуации.
Моя проблема:
Скажем, например, я хочу знать, находится ли шаблон "\ section" в тексте "abcd\sectiondefghi". Конечно, я могу это сделать:
import re
motif = r"\\section"
txt = r"abcd\sectiondefghi"
pattern = re.compile(motif)
print pattern.findall(txt)
Это даст мне то, что я хочу. Тем не менее, каждый раз, когда я хочу найти новый шаблон в новом тексте, мне нужно изменить код, который является болезненным. Поэтому я хочу написать что-то более гибкое, например this (test.py):
import re
import sys
motif = sys.argv[1]
txt = sys.argv[2]
pattern = re.compile(motif)
print pattern.findall(txt)
Затем я хочу запустить его в терминале следующим образом:
python test.py \\section abcd\sectiondefghi
Однако это не сработает (я ненавижу использовать \\\\section
).
Итак, есть ли способ конвертировать мой пользовательский ввод (либо из терминала, либо из файла) в строку python raw? Или есть лучший способ сделать компиляцию шаблона регулярного выражения из пользовательского ввода?
Большое спасибо.
Ответы
Ответ 1
Используйте re.escape()
, чтобы убедиться, что входной текст рассматривается как буквальный текст в регулярном выражении:
pattern = re.compile(re.escape(motif))
Демо:
>>> import re
>>> motif = r"\section"
>>> txt = r"abcd\sectiondefghi"
>>> pattern = re.compile(re.escape(motif))
>>> txt = r"abcd\sectiondefghi"
>>> print pattern.findall(txt)
['\\section']
re.escape()
избегает всех не-буквенно-цифровых символов; добавив обратную косую черту перед каждым таким символом:
>>> re.escape(motif)
'\\\\section'
>>> re.escape('\n [hello world!]')
'\\\n\\ \\[hello\\ world\\!\\]'
Ответ 2
Один из способов сделать это - использовать парсер аргументов, например optparse
или argparse
.
Ваш код будет выглядеть примерно так:
import re
from optparse import OptionParser
parser = OptionParser()
parser.add_option("-s", "--string", dest="string",
help="The string to parse")
parser.add_option("-r", "--regexp", dest="regexp",
help="The regular expression")
parser.add_option("-a", "--action", dest="action", default='findall',
help="The action to perform with the regexp")
(options, args) = parser.parse_args()
print getattr(re, options.action)(re.escape(options.regexp), options.string)
Пример использования мной:
> code.py -s "this is a string" -r "this is a (\S+)"
['string']
Используя ваш пример:
> code.py -s "abcd\sectiondefghi" -r "\section"
['\\section']
# remember, this is a python list containing a string, the extra \ is okay.
Ответ 3
Итак, чтобы быть понятным, это то, что вы ищете ( "\ section" в вашем примере), которое должно быть регулярным выражением или литеральной строкой? Если последний, модуль re
не является правильным инструментом для задачи; с помощью строки поиска needle
и целевой строки haystack
вы можете:
# is it in there
needle in haystack
# how many copies are there
n = haystack.count(needle)
python test.py \\section abcd\sectiondefghi
# where is it
ix = haystack.find(needle)
все из которых более эффективны, чем версия на основе regexp.
re.escape
по-прежнему полезен, если вам нужно вставить литеральный фрагмент в большее регулярное выражение во время выполнения, но если вы закончите выполнение re.compile(re.escape(needle))
, в большинстве случаев для этой задачи есть лучшие инструменты.
EDIT: Я начинаю подозревать, что настоящая проблема здесь - это правила экранирования оболочки, которые не имеют ничего общего с Python или необработанными строками. То есть, если вы наберете:
python test.py \\section abcd\sectiondefghi
в оболочку в стиле Unix, часть "\ section" преобразуется в "\ section" оболочкой, прежде чем Python ее увидит. Самый простой способ исправить это - сообщить оболочке пропустить unescaping, что вы можете сделать, поместив аргумент в одинарные кавычки:
python test.py '\\section' 'abcd\sectiondefghi'
Сравнение и контраст:
$ python -c "import sys; print ','.join(sys.argv)" test.py \\section abcd\sectiondefghi
-c,test.py,\section,abcdsectiondefghi
$ python -c "import sys; print ','.join(sys.argv)" test.py '\\section' 'abcd\sectiondefghi'
-c,test.py,\\section,abcd\sectiondefghi
(явно используя печать на объединенной строке здесь, чтобы избежать repr
добавления еще большей путаницы...)