繁体   English   中英

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

[英]My regex to find illegal XML characters is very slow in Python

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

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

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

% time python3 temp.py
python3 temp.py  105.52s user 0.50s system 96% cpu 1:50.00 total

运行这 100,000 次需要 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 秒大约有 1,000 个。 这很好。 有趣的是,我使用了一种现有的方法,通过 Python 将数据输入 XML 解析器,让它告诉我是否存在无效字符。 XML 解析器和正则表达式方法的速度几乎完全相同。

信用正则表达式来自: https : //lsimons.wordpress.com/2011/03/17/stripping-illegal-characters-out-of-xml-in-python/

答案从问题的难度开始,以一个可选的优化结束


我测试了,只是为了踢,Numpy 如何解决您的问题(使用您的问题的 Integer 版本和numpy.isin )。 由于 Numpy 作为共识快速,并且令人惊讶的是慢得多(甚至不接近),我认为您需要考虑您的问题。 re显然是针对这类问题进行了非常优化,可能与范围句柄有点像在这个答案的底部所做的那样。

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

2079*200000*100000 = 41580000000000

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

  1. 并行化这个算法,将消息分成块
  2. 如果您从某个地方(例如网络)读取此 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]

对我来说大约是re两倍。 不幸的是,对于每个布尔条件,消息都会循环,所以最快的方法就是在 C 中编写一个循环并导入它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM