【问题标题】:Boost regex too complex提升正则表达式太复杂
【发布时间】:2014-09-22 12:01:08
【问题描述】:

我想处理一个巨大的 HTML 电子书。我用 C# 编写了一个应用程序,可以很好地做到这一点,但我宁愿在 Notepad++ 中使用 Python 脚本来做到这一点。所以我写了最简单的正则表达式,它抛出:

匹配正则表达式的复杂度超出了预定义的范围。尝试重构正则表达式以使状态机做出的每个选择都明确无误。抛出这个异常是为了防止“永恒的”匹配需要无限期的时间来定位。

我对更改正则表达式不感兴趣,也不关心性能。我在哪里可以找到那些“预定义的界限”以便我可以更改它们,或者我该如何绕过它,以防万一?

编辑:这是一段带有正则表达式的代码:

editor.rereplace("li .+\}", "", re.S )

【问题讨论】:

  • 它可能有助于我们查看正则表达式,即使您设置它保持不变。
  • 说你不关心性能有点傻,因为程序警告你,由于无限递归,匹配可能会永远持续下去。您当前的正则表达式模式没有为.+ 指定惰性匹配,这意味着匹配将持续到它看到的最后一个\}。如果每个li 元素都在自己的行上,并且多行匹配默认为关闭,那么这不会造成问题,但这可能是您现在遇到的问题的一部分。此外,li.+\} 之间的空格是不必要的,因为 . 也匹配 ` `。请考虑使用li.+?\}
  • 它不会花很长时间,因为它已经过测试。我不想要一个懒惰的比赛,因为它不会完成任务。这个问题与我的正则表达式模式无关!
  • @Caustin: OP 可能不想匹配例如“lid”,所以空间很好保留
  • 好的,那么你的问题根本就不是python问题?

标签: python regex boost


【解决方案1】:

CAustin 已经在这里给出了正确的答案,但没有解释为什么贪婪表达式li .+\} 会导致超出预定义范围的消息。好吧,我也不知道确切的原因,因为这需要通过调试来分析查找替换函数本身。

但是,此表达式匹配以li 开头和空格并以 last 结束的字符串,无论是在当前行还是如果点还匹配整个字符串中的回车符和换行符文件。

如果执行查找的字符串或类似此处的文件非常大,例如几 MB 甚至几 GB,并且字符串/文件不包含任何行终止字符或点也匹配,则这是有问题的那些字符。

这种贪婪的表达式很容易导致为找到的字符串分配越来越多的空闲内存,直到机器上没有足够的空闲内存,这将导致这台机器上几乎所有正在运行的应用程序分别崩溃和内存异常因为没有更多可用内存了。

具有几 GB RAM 的现代 PC 的用户通常主要用于文件缓存而不是用于正在运行的应用程序,他们总是想知道为什么在脚本或程序中运行正则表达式查找/替换时会出现此类错误消息,因为有这么多可用 RAM总共。

嗯,总共 4 GB 的可用内存并不意味着有 4 GB 的连续块可用。任务管理器不会显示 RAM 的碎片化程度。并且 32 位应用程序总共不能分配超过 2 GB 的 RAM,因为无法解决更多问题,除非特别为超过 2 GB 的使用而编码,这是非常罕见的。很容易发生总共 2 GB 的空闲 RAM,不可能分配 32 MB 作为连续块。

但正则表达式函数和库的开发人员需要考虑的不仅仅是单个用户同时运行几个应用程序的 PC。它们的函数和库也用于只有几 MB 总 RAM 的嵌入式设备。网络服务器上的许多脚本都使用正则表达式,这些脚本必须同时处理数百甚至数千个用户。如果可以通过简单的贪婪正则表达式从服务器分配几 MB 的 RAM - 8 MB 用于找到的字符串 x 2000 次查找 = 服务器已终止,那么服务器可以很容易地被终止。

因此,通过正则表达式找到的字符串的总字节数被限制在几 KB 以内。我不知道边界。我曾尝试过一次找出它,但对于不同的发现得到不同的结果。它不是简单的 x KB 或 x MB。还包括其他一些限制,例如最大递归次数(防止堆栈溢出)或最大处理时间(保持应用程序响应)。我真的不知道正则表达式函数和库的开发人员为防止由于正则表达式查找/替换导致的应用程序或整个机器的终止而包含哪些内容。

因此,在 *+ 之后使用问号的惰性(非贪婪)表达式应始终优于贪婪表达式。

如果需要一个贪婪的表达式,用户应该使用{min,max}而不是*(0或更多)或+(1或更多)来限制它。

因此这里有一些可能的解决方案:

  • (?-s)li .+?\} ... 非贪婪,单行。
  • (?s)\bli\b.+?\} ... 非贪婪,多行。
  • \bli [^}\r\n]\} ... 非贪婪,单行。
  • (?s)li .{1,16384}\} ... 贪婪,多行。

但是,如果字符串或文件的大小未确定且应用该表达式的数据未确定,即使是非贪婪表达式也可能导致超出预定义范围的消息警告。

查看(?s)li .+?\} 并考虑一个文件,例如,它有 8 GB,但在开头的某个地方有 li  而没有地方或在结尾的某个地方}

解决方案是列表中的最后一个表达式,它是一个贪婪表达式,但在找到 li  后停止解析 16384 个字符后的右括号。因此,贪婪表达式(?s)li .{1,16384}\} 比上面列表中的其他 3 个非贪婪表达式更安全地用于未确定大小的未确定数据。


(?s) 在正则表达式的开头启用按点匹配换行符,即打开多行模式行为。因此独立于内部默认值,. 现在也匹配回车符和换行符,即可以匹配多行。

(?-s) 正好相反。它禁用多行并启用点的单行模式匹配行为。

查看页面:

对正则表达式搜索字符串中模式更改标志的支持取决于使用的库和使用的版本。

【讨论】:

  • 出于好奇,(?s) 是做什么的?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-21
  • 1970-01-01
  • 2014-05-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多