【问题标题】:Partially match a regular expression部分匹配正则表达式
【发布时间】:2014-06-18 21:36:59
【问题描述】:

我正在尝试搜索句子中的某些单词(使用 PHP)。无论出于何种原因,这些词可能会被空格分开。 (例如 'alpha betical' 而不是 'alphabetical')。出于某种原因,我正在将该句子中除以空格的每组字符分别与某个正则表达式进行比较。因此,我无法将 'alpha betical' 与 'alphabetical' 匹配,因为它会尝试分别匹配 'alpha' 和 'betical'。不过,'alpha' 确实部分匹配正则表达式('alphabetical');如果添加“betical”,它将匹配。

我需要像 Java 的 Matcher.hitEnd() 这样的东西。 (如果在此匹配器执行的最后一次匹配操作中搜索引擎命中了输入的结尾,则返回 true。 当此方法返回 true 时,有可能更多的输入会改变上次搜索的结果。)This question 提出了同样的问题,再加上一点,但没有适当的答案。我发现this question 得到了回答,但只提供了适用于 Java(在本段开头提到)而不是 PHP 的解决方案。

基本上,如果我将'alpha''/alphabetical/' 匹配,我想告诉我它至少匹配正则表达式的一部分。 (我知道在这种情况下,我可以切换它们并将alphabetical'/^alpha/' 匹配,但是当我使用它时,正则表达式'/alphabetical/' 会稍微复杂一些,因此不适合切换。 . 想象一下'/[Aa]lpha-?betical(ly)?|[Ll]exicographical(ly)?/')

我知道正则表达式不能部分工作,只有匹配或没有匹配。有没有办法得到我想要的,或者我必须以完全不同的方式解决我的问题?

【问题讨论】:

  • 如果您要进行部分匹配,您的表达式会匹配几乎所有内容吗?与您的 alphabetical 示例一样,您希望它与字母 a 部分匹配,对吗?我认为更好的方法是从输入中删除所有空格,然后搜索完整的字符串。
  • 好的。我明白了。我用另一个想法更新了我的答案。考虑实现一个通配符字符串函数,如 SQL 的“LIKE 'alpha^'”,它根据匹配长度返回匹配项。很容易指定最小长度/分数以符合匹配条件。

标签: php regex partial


【解决方案1】:

一个正则表达式要么匹配,要么不匹配。它是一个完成与否的有限自动机。现在肯定有自动机可以在任何节点退出图形并返回“分数”,但它们是非标准的。

您可以通过匹配多个正则表达式来添加布尔逻辑。或者通过添加lookahead 或lookbehind。

为什么不直接编写正则表达式来使空格成为可选?

  /a\s*l\s*p\s*h\s*a\s*b\s*e\s*t\s*i\s*c\s*a\s*l/

匹配各种组合:

  alpha betical
  al p habet i cal

如果你熟悉通配符/前缀匹配(如 SQL 的 LIKE 函数),它很容易实现。这样就够了吗?

考虑一个字符串扫描算法的简单实现,它根本不使用正则表达式,而是搜索并返回按分数排序的匹配项,其中分数是匹配项的长度,您甚至可以指定最小分数。

例子:

FindLike(haystack: s, needle: "alphabetical", minlen:5);

应该直接编写一个不区分大小写的函数以迭代方式扫描字符串,使用搜索字符串作为前缀匹配,一旦匹配初始字符,迭代两个字符串索引直到一个结束或不匹配,然后返回,或将子字符串添加到结果列表中,然后继续。

也就是说,您可能对模糊逻辑或模糊匹配或近似匹配感兴趣。

http://laurikari.net/tre/about/

Fuzzy Regular Expressions

【讨论】:

  • 仅仅知道这些匹配是否发生对我来说是不够的,我想编辑原始句子中匹配发生的部分。我可以尝试使用 PREG_OFFSET_CAPTURE 标志来做到这一点,但这只会给我比赛的开始位置,而不是结束。我还过滤不仅仅是空格,例如 .,:;'"| 和掩码字符,例如 @ 到 a,3 到 e 等。最后,如果我匹配,我无法可靠地判断匹配包含多少个字符一口气读完整个句子。
  • 一旦你定义了你的方法,编辑就很容易了。正则表达式库都有捕获组和替换选项。我只是不确定哪种方法可以满足您的要求。另外,不要将你的思维局限在正则表达式上。
  • 如果我理解正确的话,这个想法目前的形式是不够的。我真的不能不匹配或忽略一些匹配。它确实提供了一种全新的方式来解决我的问题。这可能正是我想要的。
  • 好的,然后忽略模糊匹配选项,考虑前两个选项。
【解决方案2】:

你的问题很大,这个答案集中在这部分:

如果我将 'alpha' 与 '/alphabetical/' 相匹配,我想说明一下 我认为它至少匹配正则表达式的一部分。

两种选择

有几种方法可以做到这一点。无论您选择哪种方式,您都需要以编程方式构建模式。

一般选项

这是我喜欢使用的一种通用方式,因为它很简单。它是一系列可选的前瞻,在字符串中越看越远。每个前瞻内部都有一个捕获组。

^(?=(a))?(?=(al))?(?=(alp))?(?=(alph))?(?=(alpha))?(?=(alphab))?(?=(alphabe))?(?=(alphabet))?(?=(alphabeti))?(?=(alphabetic))?(?=(alphabetica))?(?=(alphabetical))?(?=(alphabetical$))?

设置的最高捕获组告诉您我们匹配了多远。例如,对于alpha(?=(alpha)) 将成功,并且将设置第 5 组(以及第 1、2、3、4、5 组)。

这适用于 PCRE。在某些引擎中,您需要像这样包装外观:(?:(?=(a)))? 而在某些引擎中它根本不起作用。

互斥令牌的一种选择

这是@CasimirEtHippolyte 在其他地方提出的另一种方式,它非常紧凑。当令牌不能“吃掉”本应与后面的令牌匹配的文本时,它会起作用,这里就是这种情况。

^(a(l(p(h(a(b(e(t(i(c(a(l($)?)?)?)?)?)?)?)?)?)?)?)?)?

您检查设置了哪些捕获组。设置的最大捕获组会告诉您匹配了多少个字母。

【讨论】:

  • 它工作得很好,但对我来说并不完全实用。不过,谢谢。
  • 好答案。来自我的 +1。
  • 谢谢!你的有趣之处在于不同的方式——它们相得益彰:) +1
猜你喜欢
  • 2011-06-13
  • 2015-04-12
  • 2017-11-29
  • 2017-06-23
  • 2020-01-02
  • 1970-01-01
  • 1970-01-01
  • 2011-09-04
相关资源
最近更新 更多