TCL字符串匹配与正则表达式比较

6

我们应该避免使用正则表达式因为它速度较慢,而应该使用字符串操作。但是,是否存在某些情况下同时使用正则表达式和字符串操作更好呢?

6个回答

4
你应该使用适当的工具来完成工作。也就是说,你不应该避免正则表达式,在必要时应该使用它。
如果你只是搜索一系列固定的字符,请使用字符串操作。
如果你正在搜索一个模式,则使用正则表达式。
例如:
搜索单词"Foo"。使用字符串操作可以找到"Foobar",这样可以吗?不行,那么也许搜索"Foo ",但是它将无法找到"Foo,"和"Foo."。
使用正则表达式没有问题,你可以匹配单词边界/\mFoo\M/,这个正则表达式不会很慢。
我认为这种负面形象来自于特殊问题,比如catastrophic backtracking
最近有一个例子(catastrophic-backtracking-shouldnt-be-happening-on-this-regex),其中这种行为是意外的。

结论

正则表达式必须设计得很好,否则性能可能会灾难性的下降。但是如果使用不良算法,则您的普通代码也可能发生相同的情况。

对于小型任务,使用正则表达式几乎永远不会成为问题,如果您的任务更大且需要经常重复,请进行基准测试。

从我的经验来看,我正在分析非常大的文本文件(几百 MB),并使用正则表达式查找我感兴趣的行,由于正则表达式,我没有遇到性能问题。

这里有一篇关于代码优化的有趣阅读材料


3
正则表达式(REs)是一种非常好用的工具。它们可以优雅地解决一些问题,也可以通过暴力解决更多问题,但这并不美观。如果你足够努力,有些问题可以通过正则表达式解决,但是有更好的解决方案可用(例如适合使用“string map”的内容)。
“字符串匹配”或通配符可以被认为是正则表达式的简化版本。通配符模式通常比等效的正则表达式短(字符类是一个例外-ER支持它们,而通配符需要拼写它们)。我不知道性能差异如何; 我预计“字符串匹配”在等效模式上会稍微快一些,因为逻辑更简单,但“时间”比期望更可靠。
对于某些特定情况,正则表达式更容易使用,例如根据上下文提取子字符串与仅通过字符位置提取子字符串,或者匹配多个选项之一。
我的经验法则是使用最简单的方法。如果是“字符串匹配”,那就太好了。如果似乎该模式过于复杂,则转向正则表达式,并且很高兴您有选择的权利。

事实上,Tcl的“regexp”引擎可以检测到一些可以通过“string match”完成的简单模式,因此对于一些简单的模式,您不会看到太大的区别。 - schlenk
准确地说,非常简单的正则表达式在Tcl字节码编译器中被转换为“字符串匹配”调用。任何稍微复杂一点的都会进入主要的RE引擎。 - Donal Fellows

2
我能给出的最好建议,也是我自己使用的建议是,只有在简单的解决方案行不通时才使用正则表达式。
如果可以使用简单的字符串匹配或使用全局模式,请使用它们。只有当这些方法不能解决问题时才应该使用正则表达式。
针对你的具体问题,我会说,没有什么情况可以二选一,但正则表达式是更好的选择。也许有一些我没想到的边缘情况,但一般来说,简单的解决方案总是更好的。

1

我不太了解Tcl,但一般来说,如果你要查找精确的文本匹配(例如查找所有以#define开头的行),那么字符串操作会更快。但是,如果你要查找模式(例如包含以c开头且以t结尾的单词的所有行),那么正则表达式就是正确的工具(\bc\w*t\b是一个很好的正则表达式 - 如果你自己编写程序逻辑进行比较,你会发现这个正则表达式非常方便)。

即使在这种情况下正则表达式速度较慢,执行速度的影响可能不大,但当需要更改匹配逻辑时,它将非常重要(哦,现在我们需要查找以c开头且以t结尾的单词,但包含至少两个a和没有x -> \bc(?=\w*a\w*a)(?!\w*x)\w*t\b)。

大多数正则表达式引擎不愿涉及的地方是递归(匹配嵌套标记、嵌套括号等)。这就是解析器进入图片的地方。


一个真正的正则表达式(在正则语言的意义上)不能匹配嵌套的括号(它们不能被泵送)。但是大多数现代正则表达式引擎允许相当多的非正则行为(例如后向引用),因此您可能能够匹配它们。然而,您仍然不应该这样做。 - evil otto
@evil otto:这就是为什么我写了“大多数正则表达式引擎”。它们中的大多数支持反向引用甚至环视,但只有少数支持递归和嵌套标记(PCRE、Perl 6、.NET)。而且你是对的,你可能还是不应该使用。 - Tim Pietzcker
Tcl的RE引擎不支持递归;它严格遵循自动机理论。 - Donal Fellows

1

正则表达式匹配是一种字符串操作。虽然它不像一些更基本的操作那样快,但它的能力更强大。它也更难使用,特别是如果您不知道RE的基本语法,但这并不是避免使用它们的理由。然而,用一组基本的字符串操作替换正则表达式可能会导致程序变得非常冗长:有时候,您需要复杂的操作。

Tcl做了很多事情来使RE操作更有效率。特别地,它检测到特别简单的RE并将它们转换为类似于glob的匹配(如string match),这样速度更快但功能更少,并且它还做了很多事情来缓存RE的编译形式,以便匹配具有更少的开销。它还使用了一个自动机理论匹配引擎,在匹配时间内没有太多意外(代价是在首次编译RE时需要更多时间)。

总之,不要避免使用它们。在适当的地方使用它们。(如果您对速度有疑问,请使用time命令。)


0

regexp,也称正则表达式,用于匹配许多不同的字符串,可以非常复杂,甚至可以验证特定的输入。
string match 只允许使用通配符,如 *?,以及基本字符分组,就像在 regexp 中一样。
您可以在此处阅读有关它的更多信息:http://www.tcl.tk/man/tcl8.5/TclCmd/string.htm#M40
这里有一个基本指南,其中包含一些示例,说明了 regexp 的功能:http://www.regular-expressions.info/

简而言之:如果您不需要 regexp,甚至对其了解不多,我建议您不要使用它。如果您只想比较两个字符串是否相等,请使用 string equal


2
我从阅读《精通正则表达式》第二版中得到的印象是,在Tcl中,由于正则表达式引擎的实现方式,灾难性回溯不是一个问题。 - glenn jackman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接