字符串简单替换

0 投票
5 回答
872 浏览
提问于 2025-04-11 09:29

有没有简单的方法可以把大多数人熟悉的简单正则表达式格式,转换成正确的Python正则表达式字符串呢?

举个例子,我需要把这个:

string = "*abc+de?"

转换成这个:

string = ".*abc.+de.?"

当然,我可以通过循环字符串,一个字符一个字符地构建另一个字符串,但这样做肯定效率不高吧?

5 个回答

1

你可能只是偶尔需要做这个替换,比如每次用户输入新的搜索字符串时,所以我觉得不需要太担心这个解决方案的效率。

你需要生成一个替换列表,用来把“用户格式”的内容转换成正则表达式。为了方便管理,我建议把这些替换存储在一个字典里,就像@Konrad Rudolph所说的那样,我会直接使用替换方法:

def wildcard_to_regex(wildcard):
    replacements = {
        '*': '.*',
        '?': '.?',
        '+': '.+',
        }
    regex = wildcard
    for (wildcard_pattern, regex_pattern) in replacements.items():
        regex = regex.replace(wildcard_pattern, regex_pattern)
    return regex

注意,这个方法只适用于简单的字符替换,虽然如果需要的话,其他复杂的代码至少可以隐藏在wildcard_to_regex这个函数里。

(另外,我不太确定?是否应该替换成.? -- 我觉得正常的通配符中?表示“恰好一个字符”,所以它的替换应该是简单的. -- 不过我还是按照你的例子来。)

2

.replacing() 方法可以快速处理通配符,但如果通配符字符串里还有其他正则表达式的特殊字符怎么办呢?比如,有人搜索 'my.thing*',可能并不是想让 '.' 匹配任何字符。在最糟糕的情况下,像是用来创建匹配组的括号可能会搞乱你最终处理正则匹配的结果。

可以使用 re.escape 来将字面字符放入正则表达式中。不过,你得先把通配符字符分开。通常的做法是用 re.split 配合一个匹配的括号,这样就能得到一个列表,格式是 [字面字符, 通配符, 字面字符, 通配符, 字面字符...]。

示例代码:

wildcards= re.compile('([?*+])')
escapewild= {'?': '.', '*': '.*', '+': '.+'}

def escapePart((parti, part)):
    if parti%2==0: # even items are literals
        return re.escape(part)
    else: # odd items are wildcards
        return escapewild[part]

def convertWildcardedToRegex(s):
    parts= map(escapePart, enumerate(wildcards.split(s)))
    return '^%s$' % (''.join(parts))
5

你试图转换的这些东西看起来不像正则表达式,更像是Unix系统中的通配符。Python已经有一个模块可以处理这个了。它不支持你用的“+”这种语法,但我的命令行也不支持,我觉得这种语法并不标准。

>>> import fnmatch
>>> fnmatch.fnmatch("fooabcdef", "*abcde?")
True
>>> help(fnmatch.fnmatch)
Help on function fnmatch in module fnmatch:

fnmatch(name, pat)
    Test whether FILENAME matches PATTERN.

    Patterns are Unix shell style:

    *       matches everything
    ?       matches any single character
    [seq]   matches any character in seq
    [!seq]  matches any char not in seq

    An initial period in FILENAME is not special.
    Both FILENAME and PATTERN are first case-normalized
    if the operating system requires it.
    If you don't want this, use fnmatchcase(FILENAME, PATTERN).

>>> 

撰写回答