Python:有效替换子字符串

时间:2011-08-12 01:16:23

标签: python replace substring

我有这样的代码:

def escape_query(query):
    special_chars = ['\\','+','-','&&','||','!','(',')','{','}','[',']',
                     '^','"','~','*','?',':']
    for character in special_chars:
        query = query.replace(character, '\\%s' % character)
    return query

此函数应使用反斜杠转义&&中所有出现的每个子字符串(注意||special_characters)。

我认为,我的方法非常难看,我不禁想知道是否有更好的方法来做到这一点。答案应限于标准库。

4 个答案:

答案 0 :(得分:2)

使用reduce

def escape_query(query):
  special_chars =  ['\\','+','-','&&','||','!','(',')','{','}','[',']',
                     '^','"','~','*','?',':']
  return reduce(lambda q, c: q.replace(c, '\\%s' % c), special_chars, query)

答案 1 :(得分:2)

以下代码与steveha的原则完全相同。
但我认为它符合您的清晰度和可维护性要求,因为特殊字符仍然列在您的列表中。

special_chars = ['\\','+','-','&&','||','!','(',')','{','}','[',']',
                 '^','"','~','*','?',':']

escaped_special_chars = map(re.escape, special_chars)

special_chars_pattern = '|'.join(escaped_special_chars).join('()')

def escape_query(query, reg = re.compile(special_chars_pattern) ):
    return reg.sub(r'\\\1',query)

使用此代码:
执行函数定义时,将创建一个对象,其值为(正则表达式re.compile(special_chars_pattern))作为默认参数接收,名称 reg 分配给此对象并定义为参数功能。
这只发生一次,在执行函数定义时,在编译时只执行一次 这意味着在执行编译后发生的编译代码期间,每次调用该函数时,都不会再次执行此创建和分配:正则表达式对象已经存在并且是永久注册和可用的在元组 func_defaults 中,它是函数的权威属性。
如果在执行期间对函数进行了多次调用,那么这很有意思,因为如果在外部定义了Python,则不必在外部搜索正则表达式,或者如果它作为简单参数传递,则将其重新分配给参数 reg

答案 2 :(得分:1)

如果我理解你的要求,一些特殊的“字符”是两个字符的字符串(特别是:“&&”和“||”)。进行这种奇怪收集的最佳方式是使用正则表达式。您可以使用字符类来匹配一个字符长的任何内容,然后使用竖线来分隔一些替代模式,这些可以是多字符。最棘手的部分是字符的反斜杠逃避;例如,匹配“||”你需要把r'\ | \ |'因为垂直条在正则表达式中是特殊的。在字符类中,反斜杠是特殊的,因此是' - '和']'。代码:

import re
_s_pat = r'([\\+\-!(){}[\]^"~*?:]|&&|\|\|)'
_pat = re.compile(_s_pat)

def escape_query(query):
    return re.sub(_pat, r'\\\1', query)

我怀疑以上是Python中可能解决问题的最快方法,因为它将工作推向了用C语言编写的正则表达式机制。

如果您不喜欢正则表达式,可以使用详细格式更容易查看,并使用re.VERBOSE标志进行编译。然后,您可以将正则表达式扩展到多行,并在您发现令人困惑的任何部分之后添加注释。

或者,您可以构建特殊字符列表,就像您已经完成的那样,并通过此函数运行它,该函数将自动编译匹配列表中任何替代项的正则表达式模式。如果列表为空,我确保它不匹配。

import re
def make_pattern(lst_alternatives):
    if lst_alternatives:
        temp = '|'.join(re.escape(s) for s in lst_alternatives)
        s_pat = '(' + temp + ')'
    else:
        s_pat = '$^' # a pattern that will never match anything
    return re.compile(s_pat)

顺便说一句,我建议你把字符串和预编译的模式放在函数之外,如上所示。在您的代码中,Python将在每个函数调用上运行代码以构建列表并将其绑定到名称special_chars

如果你不想把除函数之外的任何东西放到命名空间中,这里有一种方法可以做到这一点而不需要任何运行时开销:

import re
def escape_query(query):
    return re.sub(escape_query.pat, r'\\\1', query)

escape_query.pat = re.compile(r'([\\+\-!(){}[\]^"~*?:]|&&|\|\|)')

上面使用函数的名称来查找属性,如果稍后重新绑定函数的名称,该属性将不起作用。这里有一个讨论和一个很好的解决方案:how can python function access its own attributes?

(注:以上段落取代了一些内容,包括下面讨论评论中讨论过的问题。)

实际上,经过进一步思考,我认为这更清洁,更Pythonic:

import re

_pat = re.compile(r'([\\+\-!(){}[\]^"~*?:]|&&|\|\|)')

def escape_query(query, pat=_pat):
    return re.sub(pat, r'\\\1', query)

del(_pat) # not required but you can do it

在编译escape_query()时,绑定到名称_pat的对象将绑定到函数名称空间内的名称(该名称为pat)。然后,如果您愿意,可以致电del()取消绑定名称_pat。这很好地将模式封装在函数内部,完全不依赖于函数的名称,并允许您根据需要传递替代模式。

P.S。如果你的特殊字符总是长一个字符,我会使用下面的代码:

_special = set(['[', ']', '\\', '+']) # add other characters as desired, but only single chars

def escape_query(query):
    return ''.join('\\' + ch if (ch in _special) else ch  for ch in query)

答案 3 :(得分:0)

不确定这是否更好,但它可行并且可能更快。

def escape_query(query):
    special_chars = ['\\','+','-','&&','||','!','(',')','{','}','[',']', '^','"','~','*','?',':']
    query = "".join(map(lambda x: "\\%s" % x if x in special_chars else x, query))
    for sc in filter(lambda x: len(x) > 1, special_chars):
        query = query.replace(sc, "\%s" % sc)
    return query