【问题标题】:How can I modify this regex pattern so that once it finds a match it returns the whole sentence, not just the words it matched?如何修改此正则表达式模式,以便一旦找到匹配项,它会返回整个句子,而不仅仅是匹配的单词?
【发布时间】:2018-08-13 13:34:43
【问题描述】:

正如标题所解释的,这个正则表达式模式基本上检查描述变量以匹配集合内的单词组合,例如:

set = ["oak", "wood"]

然后,如果它在 5 个单词间距内找到这 2 个单词,它将返回这些单词。但是,我需要它来返回匹配的句子。因此,例如,如果描述是:

description = "...would be a lovely addition to any home. This lovely oak hard wood table comes in a variety of sizes. Another great reason to consider..." 

我希望它返回包含关键字的整个句子,而不是只返回匹配的单词。

这就是我目前正在使用的,显然只是返回匹配的集合对。

re.findall(r"\b(?:(%s)\W+(?:\w+\W+){0,5}?(%s)|(%s)\W+(?:\w+\W+){0,5}?(%s))\b" % (set[0], set[1], set[1], set[0]), description)

我也知道,我相信这种模式将超越单个句子进行匹配,因此您可能会遇到这样的情况,即它会在 2 个不同的句子中找到匹配项。如果可能的话,我还想找到一种方法,将匹配限制为只能在同一个句子中进行。

如果能得到任何帮助,我将不胜感激。

编辑:只是为了澄清我想要的输出是:

"This lovely oak hard wood table comes in a variety of sizes."

因为这是包含匹配关键字对的句子。

谢谢!

【问题讨论】:

  • @KyleA 我能说我非常感谢你这么说。由于这里有些人的态度和心态,我从字面上从来没有在 StackOverflow 上问过任何问题。我花了好几个小时确保没有发布任何类似的内容,因此我不会冒犯任何人,然后确保问题尽可能简洁且解释清楚。
  • 我宁愿采用不同的方式:先拆分句子(使用例如nltk,查找sent_tokenize())并在每个句子中搜索您的单词。
  • 顺序重要吗?你传递了 4 个单词,你在任何地方都找到了一个带有这 4 个单词的句子。还是必须按顺序出现?
  • @Julio 关键字对的顺序并不重要,但要避免误报,它们的接近度很重要。

标签: python regex


【解决方案1】:

根据我的评论,一些使用nltk 的虚拟代码(目前无法访问Python):

from nltk import sent_tokenize

for sent in sent_tokenize(your_data_here):
    if any(['foo', 'bar']) in sent:
        # do sth. useful here

显然,您甚至可以在 sent 上应用您的初始正则表达式(毕竟它是一个字符串)。

【讨论】:

  • 我喜欢这种方法。我不知道句子标记器的存在。
  • @KyleA:分词器也会考虑其他标点符号(如?、!、: 等)。为什么要重新发明轮子……
  • 我相信这是我正在寻找的,因为初始正则表达式仍然非常必要,因为由于较长描述中可能出现误报,因此需要进行邻近检测,例如:“这不是橡木但是,我们确实生产了其他品种,它们使用樱桃来制作不同风格的硬木”——如果不检查接近度,这显然仍会返回 True,因为“橡木”和“木头”仍然存在。一旦我可以确认,我会试一试,包括正则表达式并标记为已解决。谢谢:)
  • @Jan 很棒的解决方案伙伴。
【解决方案2】:

您可以使用以下正则表达式:

print(re.findall(r"(^|(?<=\.))([a-zA-Z0-9\s]*oak[a-zA-Z0-9\s]*wood.*?(?=\.|$)).*?|([a-zA-Z0-9\s]*wood[a-zA-Z0-9\s]*oak.*?(?=\.|$))", description))

地点:

r"(^|(?<=\.))" # means start with 'start of string' or '.'
r"([a-zA-Z0-9\s]*oak[a-zA-Z0-9\s]*wood.*?(?=\.)).*?" # means any letter/number/space followed bi 'oak', followed by any letter/number/space, followed by wood, stopping at the first occurrence of a '.' or 'end of line'
r"([a-zA-Z0-9\s]*wood[a-zA-Z0-9\s]*oak.*?(?=\.|$))" # same as previous, but with | (or) condition matches the wood-oak case

输出:

('', '这款可爱的橡木硬木桌有多种尺寸', '')

【讨论】:

  • 嗨,伙计,非常感谢您在这里的意见。只是阅读输出我想你可能稍微误解了我在这里寻找的东西,也许我没有正确解释它,并将在 OP 中添加我想要的输出,但我只是想找回“这棵可爱的橡树硬木桌子有多种尺寸。”因为这是包含匹配关键字对的句子:)
  • 好的,抱歉。所以它应该包含这两个元素,并且“句子”定义为从. 或字符串开头到. 或\n 或字符串结尾
  • 这不会做提问者想要的,因为它返回所有文本而不是包含单词的句子的文本
  • @JGNI 我正在修改我的答案。如果你在你之前读过 cmets,你会发现有一个误解
  • @gsk 抱歉,已修复 :-)
【解决方案3】:

必须使用正则表达式吗?我发现只使用以下内容更加严格:

set = ["oak","wood"]
description = "...would be a lovely addition to any home. This lovely oak hard wood table comes in a variety of sizes. Another great reason to consider..."

description2 = "...would be a lovely addition to any home. This is NOT oak however we do make other varieties that use cherry for a different style of hard wood."

def test_result(desc):
    desc = desc.split(". ")
    for sent in desc:
        if all(s in sent for s in set):
            if -5 <= sent.split(" ").index("oak") - sent.split(" ").index("wood") <= 5:
                print (sent)

test_result(description)
test_result(description2)

结果:

This lovely oak hard wood table comes in a variety of sizes

【讨论】:

    【解决方案4】:

    您可以尝试以下正则表达式:

    [^.]*?\boak(?:\W+[^\W.]+){0,5}?\W+wood(?:\W+[^\W.]+){0,5}?\W+table(?:\W+[^\W.]+){0,5}?\W+variety[^.]*\.+

    Demo with several examples

    解释:

    [^.]*?                 # Anything but a dot, ungreedy
      \b oak               # First word (with word boundary)
    (?:\W+[^\W.]+){0,5}?   # Some (0-5) random words: (separator + word except dot) x 5, ungreedy
     \W+ wood              # Second word. Starts with some separator
    (?:\W+[^\W.]+){0,5}?   # Again, random words, ungreedy
     \W+ table             # third word. Starts with some separator
    (?:\W+[^\W.]+){0,5}?   # Again, random words, ungreedy
     \W+ variety           # Final required word
    [^.]*                  # The rest of the sentence (non dot characters) up to the end
    \.+                    # We match the final dot (or ... if more exist)
    

    【讨论】:

      【解决方案5】:

      您可以让它在结尾查找句点来捕捉整个句子。您还可以通过将\W(匹配非单词字符)替换为[^.\w](匹配任何不是句点或单词字符)来从中间的搜索中排除句点。

      "(^|\.)([^.]*\b(?:(%s)[^.\w]+(?:\w+[^.\w]+){0,5}?(%s)|(%s)[^.\w]+(?:\w+[^.\w]+){0,5}?(%s))\b[^.]*)(\.|$)"

      (^|\.) 将匹配输入的开头或句点,(\.|$) 将匹配句点或输入的结尾(如果在最后一个句点之后有输入)。

      我现在无法在 python 中对此进行测试,但即使我有错误或拼写错误,它也应该为您指明正确的方向。

      【讨论】:

      • 如何处理 5.67 或 IP 地址,甚至是先生、小姐、博士、教授等。
      • @JGNI,它会在句号处拆分句子。我假设这些句子中没有句号。从中间排除句点是可选的,但 OP 询问是否可以防止跨句子边界搜索。如果您需要更智能地检测句子边界,那么 RegEx 可能是错误的工具,因为这变得非常难以检测。
      猜你喜欢
      • 2017-07-08
      • 2013-09-16
      • 1970-01-01
      • 1970-01-01
      • 2019-10-02
      • 1970-01-01
      相关资源
      最近更新 更多