进行单个字符通配符搜索时,Regex IsMatch 真的很慢
Regex IsMatch really slow when doing a single character wildcard search
我们遇到这样一种情况,即在开始时使用单个字符进行通配符搜索,然后在通配符之后使用其他字符进行通配符搜索,并且 运行 速度非常慢(至少在 c# 中是这样)。
这有什么原因和改善方法吗?它在几乎所有其他情况下都更快。
20k 长随机字符串的示例 运行 1000 次:
- a.*r1 花费时间:1802
- r1.*一次用时:9
- r1.*b.*c 所用时间:9
- r1f.*b.*c 耗时:16
- a.*r1f.*c 耗时:3199
- a.*r1.*c 所用时间:1895
- a.*b.*r1f 耗时:55450
绝对不是随机字符串,因为已经尝试过不同的。
模式肯定是,如果第一部分是单个字符,然后是通配符后的任何字符,它总是慢得多。
--更新--
我想知道 Regex 的工作方式是否是循环查找单个字符,当它找到它时,它会一直搜索直到结束寻找下一个模式。当它找不到它时,它会返回到第一个字符并开始寻找下一个第一个字符,直到它再次找到第一个匹配项并执行一些完整的逻辑,即使它可以跳过它在第一个 运行.
我想我已经通过生成一个没有字符“a”的随机字符串来证实这一点——如果我随后使用这个字符作为第一个字符,它真的很快,但是如果我使用“c”它很慢。即 a.*b.*r1f 在这种情况下是即时的,但 c.*b.*r1f 需要很长时间。
如果是这样,想知道您是否可以通过某种方式在正则表达式中对其进行优化?
这种性能差异的原因在于优化搜索的方式。
当模式以文字字符开头时,在正则表达式引擎的“正常遍历”之前使用快速算法来查找字符串中模式可能成功的可能位置(文字子字符串所在的位置)。然后正则表达式引擎仅在这些位置测试模式。
这就是为什么以字母 a
开头的模式,对于不包含字母 'a' 的字符串(无论大小)很快解决的原因(不匹配,整个模式从未经过测试。
现在为什么对于同一种模式,以字母 a
开头的模式(只有一个文字字符)和以 abcd
开头的模式在大多数情况下会给出不同的表现随机字符串。答案很简单,四个字符 abcd
的位置比只有 a
的位置出现的频率低。更少的尝试位置 => 更快的结果。
另请注意,像 a.*b.*c
这样的模式称为病态模式,因为它可能导致回溯步骤数量的潜在爆炸。如果使用非贪婪量词有时可以减少问题,则不能保证它总是会提高性能(这不是魔杖)。最好的方法始终是使用适当的字符 类、适当的量词和最准确的字符串描述来做到最精确,尽可能避免使用 .*
或 .*?
。 a[^b]*b[^c]*c
例如。
我们遇到这样一种情况,即在开始时使用单个字符进行通配符搜索,然后在通配符之后使用其他字符进行通配符搜索,并且 运行 速度非常慢(至少在 c# 中是这样)。 这有什么原因和改善方法吗?它在几乎所有其他情况下都更快。
20k 长随机字符串的示例 运行 1000 次:
- a.*r1 花费时间:1802
- r1.*一次用时:9
- r1.*b.*c 所用时间:9
- r1f.*b.*c 耗时:16
- a.*r1f.*c 耗时:3199
- a.*r1.*c 所用时间:1895
- a.*b.*r1f 耗时:55450
绝对不是随机字符串,因为已经尝试过不同的。
模式肯定是,如果第一部分是单个字符,然后是通配符后的任何字符,它总是慢得多。
--更新--
我想知道 Regex 的工作方式是否是循环查找单个字符,当它找到它时,它会一直搜索直到结束寻找下一个模式。当它找不到它时,它会返回到第一个字符并开始寻找下一个第一个字符,直到它再次找到第一个匹配项并执行一些完整的逻辑,即使它可以跳过它在第一个 运行.
我想我已经通过生成一个没有字符“a”的随机字符串来证实这一点——如果我随后使用这个字符作为第一个字符,它真的很快,但是如果我使用“c”它很慢。即 a.*b.*r1f 在这种情况下是即时的,但 c.*b.*r1f 需要很长时间。
如果是这样,想知道您是否可以通过某种方式在正则表达式中对其进行优化?
这种性能差异的原因在于优化搜索的方式。
当模式以文字字符开头时,在正则表达式引擎的“正常遍历”之前使用快速算法来查找字符串中模式可能成功的可能位置(文字子字符串所在的位置)。然后正则表达式引擎仅在这些位置测试模式。
这就是为什么以字母 a
开头的模式,对于不包含字母 'a' 的字符串(无论大小)很快解决的原因(不匹配,整个模式从未经过测试。
现在为什么对于同一种模式,以字母 a
开头的模式(只有一个文字字符)和以 abcd
开头的模式在大多数情况下会给出不同的表现随机字符串。答案很简单,四个字符 abcd
的位置比只有 a
的位置出现的频率低。更少的尝试位置 => 更快的结果。
另请注意,像 a.*b.*c
这样的模式称为病态模式,因为它可能导致回溯步骤数量的潜在爆炸。如果使用非贪婪量词有时可以减少问题,则不能保证它总是会提高性能(这不是魔杖)。最好的方法始终是使用适当的字符 类、适当的量词和最准确的字符串描述来做到最精确,尽可能避免使用 .*
或 .*?
。 a[^b]*b[^c]*c
例如。