何时不在 C#(或 Java、C++ 等)中使用 Regex

Posted

技术标签:

【中文标题】何时不在 C#(或 Java、C++ 等)中使用 Regex【英文标题】:When not to use Regex in C# (or Java, C++, etc.) 【发布时间】:2010-11-01 10:04:55 【问题描述】:

很明显,有很多问题看起来像一个简单的正则表达式就能解决,但事实证明用正则表达式很难解决。

那么,不是正则表达式专家的人如何知道他/她是否应该学习正则表达式来解决给定的问题?

(请参阅"Regex to parse C# source code to find all strings" 了解我提出这个问题的方式。)

这似乎总结得很好:

Some people, when confronted with a problem, think “I know, I'll use regular expressions.” Now they have two problems...

(我刚刚更改了问题的标题以使其更具体,因为 C# 中 Regex 的一些问题在 Perl 和 JScript 中已解决,例如,两个级别的引用使 Regex 如此难以阅读.)

【问题讨论】:

另见:***.com/questions/263072/…***.com/questions/764247/…***.com/questions/590122/… 也许另请参阅Why it's not possible to use regex to parse html/XML: a formal explanation in layman's terms,它专门讨论了为什么要避免使用 XML 和 HTML 等结构化格式(以及扩展 JSON、YAML、大多数语言的源代码等)的正则表达式。 【参考方案1】:

不要尝试使用正则表达式来解析像程序源(或嵌套 XML)这样的分层文本:它们被证明不够强大,例如,对于一串括号,它们无法弄清楚是否平衡。

为此使用解析器生成器(或类似技术)。

另外,我不建议使用正则表达式来验证具有严格正式标准的数据,例如电子邮件地址。 它们比你想要的更难,而且你的正则表达式要么不准确,要么很长。

【讨论】:

我想指出,某些正则表达式引擎确实支持递归模式,允许您匹配 XML 和其他嵌套结构。 喜欢哪个?我说的主要是PCRE。 即使你确实支持递归正则表达式模式,但在性能方面你还是最好使用递归下降解析器。 如今,对于大多数代码而言,“性能方面”是编写或维护代码所需的时间。处理非常大的文件/数据集是一个例外。但是我认为递归正则表达式模式会增加维护成本 特别是 PCRE(和 .NET)支持递归:tin.org/bin/man.cgi?section=3&topic=PCRE(搜索“RECURSIVE”。【参考方案2】:

有两个方面需要考虑:

能力:您尝试识别的语言是 Type-3 语言(常规语言)吗?如果是,那么你可能会使用正则表达式,如果不是,你需要一个更强大的工具。

可维护性:如果编写、测试和理解正则表达式的时间比编程对应的正则表达式要多,那么这是不合适的。如何检查这很复杂,我建议与您的同事进行同行评审(如果他们看到它时说“what the ...”,那就太复杂了)或者只是将其无证几天,然后进行自己看,衡量理解它需要多长时间。

【讨论】:

【参考方案3】:

我是正则表达式的初学者,但恕我直言,花一些时间学习基本的正则表达式是值得的,您会意识到您以不同方式解决的许多问题可以(并且可能应该)使用正则表达式。

对于一个特定的问题,尝试在regexlib之类的网站上找到解决方案,看看你是否能理解解决方案。

如上所述,正则表达式可能不足以解决特定问题,但浏览像 regexlib 这样的网站肯定会告诉您正则表达式是否是解决问题的正确方法。

【讨论】:

我不认为使用“魔术”字符串而不是精心设计的对象来控制命令发送是优雅的。代码应该清晰、可测试且易于阅读 当然,你不应该使用“魔法”正则表达式,但是在学习“语言”时浏览库会很有帮助(至少,它对我来说是这样)。【参考方案4】:

您应该始终学习正则表达式 - 只有这样您才能判断何时使用它们。通常,当您需要非常好的性能时,它们会出现问题。但通常使用正则表达式比编写大的 switch 语句要容易得多。

看看this question - 与类似的 if() 构造相比,它向您展示了正则表达式的优雅...

【讨论】:

【参考方案5】:

使用正则表达式来识别文本中的(常规)模式。不要将其用于将文本解析为数据结构。当表达式变得非常大时,不要使用正则表达式。

通常不清楚何时不使用正则表达式。例如,您不应该使用正则表达式来进行正确的电子邮件地址验证。起初这似乎很容易,但有效电子邮件地址的规范并不像您想象的那样常规。您可以使用正则表达式来初始搜索电子邮件地址候选人。但是您需要一个解析器来实际验证候选地址是否符合给定标准。

【讨论】:

【参考方案6】:

至少,我会说学习正则表达式只是为了让您完全理解它们并能够在它们可以工作的情况下应用它们。在我的脑海中,我会使用正则表达式:

识别字符串的各个部分。 检查字符串是否符合某种格式或结构。 查找与特定模式匹配的子字符串。 将符合特定模式的字符串转换为不同的形式(搜索替换、大写等)。

理论层面的正则表达式构成了状态机的基础——在计算机科学中,有确定性有限自动机 (DFA) 和非确定性有限自动机 (NFA)。您可以使用正则表达式对输入进行某种类型的验证——正则表达式引擎只需将正则表达式模式/字符串解释或转换为实际的运行时操作。

一旦您知道要确定为有效的字符串(或数据)是否可以通过 DFA 进行测试,您就可以选择是使用自己的代码还是使用正则表达式引擎自行实现该 DFA。您会发现了解正则表达式实际上会增强您的工具箱以及您对字符串处理实际上如何变得复杂的理解。

基于简单的正则表达式,您可以了解解析器以及解析器的工作原理。在最低级别,您正在查看词法分析(正则表达式起作用),在更高级别,您正在查看语法和语义操作。这些是编译器和解释器工作的基础,也是协议解析器实现和文档呈现/转换应用程序所依赖的基础。

【讨论】:

【参考方案7】:

这里主要关注的是可维护性。

对我来说很明显,任何称职的程序员必须知道正则表达式。不了解它们就像不知道抽象和封装是什么,只是,可能更糟。所以这是不可能的。

另一方面,应该考虑的是,维护正则表达式驱动的代码(用任何语言编写)即使对于真正擅长它们的人来说也是一场噩梦。因此,在我看来,这里的正确方法是仅在不可避免的情况下使用它们,并且当使用正则表达式的代码比其非正则表达式变体更具可读性时。而且,当然,正如已经指出的那样,不要将它们用于某些事情,它们不应该做(如 xml)。也没有电子邮件地址验证(我最讨厌的一个:P)!

但是说真的,当你将所有这些 substr 用于某些可以用少数字符解决的东西时,会不会感觉不对劲,看起来像线条噪音?我知道它对我有用。

【讨论】:

至少使用“所有这些 substrs”我可以分解问题并对每个位进行单元测试。但是我认为问题的一部分是我作为一名程序员已经超过 15 年,我不需要每年进行超过 1 或 2 次的字符串处理(除了将输出组合到 UI 之外)。抽象和封装我大部分时间都要做…… 我认为只有在不可避免的情况下才使用正则表达式(这绝不是不可避免的)是没有意义的,只有当其复杂性的成本超过其简洁和强大的好处时才有意义。 @shylent:很抱歉这么晚才发帖!我只是在这里跌跌撞撞。因此,正如您所说,不应将正则表达式用于电子邮件,但大多数时候,我在客户端验证时看到电子邮件是由正则表达式验证的。此外,在link 中,我发现电子邮件的正则表达式为^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]2,9)$。我输入了各种电子邮件地址,但它毫不费力地匹配!那么,为什么我们不应该使用它们呢?有什么我不知道的负面警告吗? @Razort4x 电子邮件地址实际上很难使用正则表达式进行验证,例如地址very.“(),:;[]”.VERY.“very@\\ "very”.unusual@strange.example.com 表示here 为有效地址。this link 处的正则表达式声称适用于 99% 的电子邮件地址。

以上是关于何时不在 C#(或 Java、C++ 等)中使用 Regex的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中正确使用堆栈和堆?

PHP 是不是具有 C#、JAVA、C++ 等函数重载功能 [重复]

标记和未标记的中断,在 C# 或 C++ 中继续 [关闭]

c# WebBrowser DocumentText 工作一次但不在循环中?

何时应该使用 Pipes 或 gRPC 进行进程间通信(在 C# .NET Core 中)?

如何在编译时驱动 C#、C++ 或 Java 编译器计算 1+2+3+...+1000?