我有这样的代码:
def escape_query(query):
special_chars = ['\\','+','-','&&','||','!','(',')','{','}','[',']',
'^','"','~','*','?',':']
for character in special_chars:
query = query.replace(character, '\\%s' % character)
return query
此函数应使用反斜杠转义&&
中所有出现的每个子字符串(注意||
和special_characters
)。
我认为,我的方法非常难看,我不禁想知道是否有更好的方法来做到这一点。答案应限于标准库。
答案 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