当从内存的某个区域读取数据时,结果是字节串的形式,通常我们关心的数据是由4个字节组成的(是int或float类型),并且数据的地址可以被4整除。现在,可以使用Python中的regex模式找到数据的地址(使用像re. findall或re. finditer这样的函数),有没有办法通过每4个字节搜索一次模式来更快地做到这一点?
我知道这可以在CheatEngine这样的软件中实现,但我还没有找到直接在Python中实现的方法,使用4步循环和re.match相当慢。
示例:在中查找pattern = b'\x01\x02\x03\x04'
的匹配项(通常是非字符串模式)bytestr = b'\xa1\x02\x03\x04\x01\x02\x03\x04\xb1\x02\x03\x04'
,
在这里,它出现在索引4处。由于我们知道模式只能出现在索引0、4、8 ...处,因此我们不使用re.findall(pattern, bytestr)
,而是使用类似于
for ind in range(0, len(bytestr), 4):
if re.match(pattern, bytestr[ind : ind + 4]) is not None:
print(ind)
但是当bytestr
很大的时候,它变得相当慢,甚至比re.findall(pattern, bytestr)
还慢,有没有一种直接的方法可以用Python实现这种改进?
我在程序中使用的真正模式是
pattern = br"\x93\x5F\x01\x00\x01\x00\x00\x00.....[\x00-\xea]\x00\x00\x60\xea\x00\x00"
2条答案
按热度按时间o8x7eapl1#
The Greatest Regex Trick Ever的变体:不仅匹配您想要的内容,而且作为后备,匹配任意四个字节。
对于您的示例,
re.findall
返回[b'', b'\x01\x02\x03\x04', b'']
,您只需要报告所需的匹配项并忽略不需要的匹配项。这要求模式总是精确匹配4个字节(的倍数)。如果不是这样,也许您可以根据需要将
.
附加到您的模式中,使其成为这样。或者...我想我可以将模式放入一个前瞻中...等等...好的......这里有一个版本,它总是匹配四个字节,但是在每个位置也会尝试以积极的前瞻方式匹配您想要的模式(回退到匹配空字符串,这样就可以保证匹配并且不干扰进程)。
嗯......它比你的原始版本快,但不是很多。而且只比你的简单优化版本快一点点。下面是一个1 MB字节串中模式出现1000次的时间:
Kelly1和Kelly2就是上面的例子。Kelly3是另一个想法,我构建了一个
str
,在每个四字节的块前面加上一个“非字节”字符,然后使用它来锚模式。这避免了像我的其他解决方案中那样过滤掉错误匹配。但是它只在模式匹配最多四个字节时有效,而更新的问题现在显示不是这样。此外,速度不够快,所以我没有完全开发它。准则代码(未清理):
Attempt This Online!
krugob8w2#
我们可以使用该算法来提高搜索的性能。
这完成了两件事。
首先,它消除了
re.match
函数调用产生的开销(我们使用==操作符节省了CPU周期和内存)。[1]其次,它比
re.findall
更高效,因为我们将搜索限制在字符串中距离原点为4的倍数的位置。(The
re.findall
函数检查字符串中的每个索引是否匹配,而上面的代码只检查4的倍数的索引。)另一种提高效率的方法是用C/C ++编写代码。
脚注:
[1]如果我们有一个非常长的字节串,并且在每个4的倍数索引处调用
re.match
,那么我们最终会多次调用re.match
,这会产生大量的开销。