我的正则表达式在Python中查找非法XML字符的速度非常慢

我的正则表达式在Python中查找非法XML字符的速度非常慢,python,python-3.x,regex,Python,Python 3.x,Regex,我试图确定字符串是否包含非法的XML字符 为了测试,我创建了一个200K的有效字符字符串,然后在末尾添加一个无效字符 然后我用Python进行正则表达式搜索 % time python3 temp.py python3 temp.py 105.52s user 0.50s system 96% cpu 1:50.00 total 运行100000次需要105秒吗?对我来说似乎很慢 有人能建议我如何加快速度吗?我只需要知道字符串是否包含>0个非法字符,仅此而已 更多信息-如果我将正则表达式更改

我试图确定字符串是否包含非法的XML字符

为了测试,我创建了一个200K的有效字符字符串,然后在末尾添加一个无效字符

然后我用Python进行正则表达式搜索

% time python3 temp.py
python3 temp.py  105.52s user 0.50s system 96% cpu 1:50.00 total
运行100000次需要105秒吗?对我来说似乎很慢

有人能建议我如何加快速度吗?我只需要知道字符串是否包含>0个非法字符,仅此而已

更多信息-如果我将正则表达式更改为:

_illegal_xml_chars_RE = re.compile(u'[\x00]')
那么时间是8秒:

python3 tmp.py  8.17s user 0.11s system 80% cpu 10.282 total
为了回答评论,我也对脚本计时,但在创建随机消息后立即退出,这似乎需要大约1/3秒:

python3 tmp.py  0.36s user 0.03s system 92% cpu 0.422 total
代码如下:

import re
from random import choice
from string import ascii_letters, digits

# make a 200KB string of valid characters
random_message = ''.join(choice(ascii_letters + digits) for i in range(200000))
# add an illegal character to the end
random_message += '\x00'
_illegal_xml_chars_RE = re.compile(u'[\x00-\x08\x0b\x0c\x0e-\x1F\uD800-\uDFFF\uFFFE\uFFFF]')

for x in range(100000):
    result = _illegal_xml_chars_RE.search(random_message)
更新:
最后,我决定这里的代码可能在所有方面都很快,正如@sabik和@Maxt8r所指出的。大约每1.3秒1000次。那很好。有趣的是,我正在使用一种现有的方法,通过Python将数据输入XML解析器,让它告诉我是否有无效字符。XML解析器和正则表达式方法的速度几乎完全相同

贷记正则表达式来源:
答案从问题的难度开始,以可选的优化结束


我测试了Numpy如何处理您的问题(使用问题的整数版本,以及
Numpy.isin
)。由于Numpy是一个共识迅速,令人惊讶的是慢得多(甚至不接近),我认为你需要考虑你的问题。code>re显然是针对这类问题进行了非常优化的,可能与答案底部的范围句柄类似

您的非法字符集是2079,上面的测试有一条长度为200000的消息,您比较了100000次,示例是最坏的情况(消息中的所有字母都有效,因此必须进行所有比较)。这是一个您无法逃避的数字(最多进行一些优化,但仍按此大小进行扩展):

这意味着您需要重新考虑您的设计,因为我认为对于类比较方法,
re
(或
re2
)不会做得更好。有几个选择

  • 并行化此算法,将消息划分为块
  • 如果您从web之类的地方读取此xml,请在流中读取,将每个传入字符与非法字符集进行比较
  • 对于后代,我也使用enumerate进行列表理解检查,并查找集合中的每个序号,但这仍然比
    re
    慢得多


    当然,我们在这个答案中使用整数的事实很好地暗示了另一种优化——我们可以更轻松地对连续范围进行比较,从而节省大量比较:

    msg_array = np.array([ord(x) for x in random_message])
    
    result = np.where((msg_array < 10) | (msg_array == 11) |
                      (msg_array == 12) |
                      ((msg_array >= 14) & (msg_array <= 31)) |
                      ((msg_array >= 55296) & (msg_array <= 57344)) |
                      (msg_array == 65534) |
                      (msg_array == 65535))[0]
    
    msg\u array=np.array([ord(x)表示随机消息中的x])
    结果=np。其中((msg_数组<10)|(msg_数组==11)|
    (msg_数组==12)|
    
    ((味精数组>=14)和(味精数组=55296)和(说句公道话,它正在扫描20GB的数据……一种可能是
    re2
    库;在某些情况下,它可能会更快……@sabik I添加了一些进一步的信息——更改正则表达式会将其缩短到8秒,因此数据量似乎不是一个因素。嗯,可能有一些仅ASCII的优化开始了使用更简单的regex.And,查找代理项对的一半是否被视为非法字符?我的意思是,这不取决于源是否为utf16吗?至少0xd800-0xdfff范围也可以作为位掩码;
    msg_array&0xd800==0xd800
    ;不知道这是更快、更慢还是没有区别。顺便说一句,键入:字符32在XML中是有效的。@sabik谢谢,修复了。位掩码将保存一个比较,所以我猜它的好处是微不足道的,但它仍然是一个改进。我收回这一点,它仍然是两个操作,所以我不确定它是否会更快,再考虑一下。我最后决定,我拥有的代码可能是非常快的所有事情考虑。它是大约每1.3秒1000个字符。这很好。有趣的是,我正在使用一种现有的方法,通过Python将数据输入XML解析器,让它告诉我是否有无效字符。两种方法的速度几乎相同。
    msg_array = np.array([ord(x) for x in random_message])
    
    result = np.where((msg_array < 10) | (msg_array == 11) |
                      (msg_array == 12) |
                      ((msg_array >= 14) & (msg_array <= 31)) |
                      ((msg_array >= 55296) & (msg_array <= 57344)) |
                      (msg_array == 65534) |
                      (msg_array == 65535))[0]