正则表达式:匹配至少两个搜索词

3
我有一组搜索词,希望能够匹配至少两个词的所有项的正则表达式。
搜索词:war|army|fighting|rebels|clashes
匹配:这周战争反叛军军队之间发生了几次冲突。(4次命中)
不匹配:在打击恐怖主义的战争中,奥巴马政府希望增加无人机攻击的数量。(仅1次命中)
背景:我使用Tiny Tiny RSS收集和过滤大量新闻源用于新闻报道项目。每天我会得到1000-2000条订阅项,并希望通过关键字进行筛选。但是只使用|OR表达式会有太多误报,所以我想要匹配一个订阅项中至少两个搜索词。
谢谢!
编辑:
我对正则表达式知之甚少,所以一直使用简单的|OR运算符。我尝试将搜索词放在括号中(war|fighting|etc){2,},但这只匹配一个订阅项中相同的词。
编辑2:抱歉造成混淆,我对正则表达式等一切都很陌生。事实上:正则表达式查询一个MySQL数据库。它在tt-rss后端作为过滤器输入,只允许一行(尽管理论上可以无限制输入字符)。导入订阅项到MySQL数据库时会使用该过滤器。

1
可能是正则表达式匹配包含两个名称的字符串(顺序任意)的重复问题。根据您使用的语言,只需循环单词并检查它们是否存在于字符串中即可(找到2个匹配项时退出),这可能会更容易(许多)。 - AD7six
你用什么语言编写代码?你尝试过什么? - ghoti
1
人们回答是因为这是一个有趣的问题,但问题的质量需要提高。请标记您的问题所涉及的编程语言,并展示您已经尝试过的任何步骤。 - Todd A. Jacobs
4个回答

9
(.*?\b(war|army|fighting|rebels|clashes)\b){2,}

如果您需要避免匹配相同的术语,可以使用以下方式:
.*?\b(war|army|fighting|rebels|clashes).*?(\b(?!\1)(war|army|fighting|rebels|clashes)\b)

使用否定预查,可以匹配一个词项,但避免再次匹配相同的词项。
在Java中:
Pattern multiword = Pattern.compile(
    ".*?(\\b(war|army|fighting|rebels|clashes)\\b)" +
    ".*?(\\b(?!\\1)(war|army|fighting|rebels|clashes)\\b)"
);
Matcher m;
for(String str : Arrays.asList(
        "war",
        "war war war",
        "warm farmy people",
        "In the war on terror rebels eating faces"

)) {
    m = multiword.matcher(str);
    if(m.find()) {
        logger.info(str + " : " + m.group(0));
    } else {
        logger.info(str + " : no match.");
    }
}

输出:

war : no match.
war war war : no match.
warm farmy people : no match.
In the war on terror rebels eating faces : In the war on terror rebels

嗯,确实,这个问题有点不清楚是否需要那个。也许可以通过使用反向引用来避免这个问题。 - beerbajay
我无法让那个正则表达式起作用,但如果它真的能起作用那就太棒了。正则表达式中没有单词边界,因此它将匹配包含“温暖的农场人”等文本的文本。 - AD7six
查询SELECT DISTINCT date_entered, guid, ttrss_entries.id,ttrss_entries.title, updated, label_cache, tag_cache, always_display_enclosures, site_url, note, num_comments, comments, int_id, unread,feed_id,marked,published,link,last_read,orig_feed_id, SUBSTRING(last_read,1,19) as last_read_noms, ttrss_feeds.title AS feed_title, content as content_preview, SUBSTRING(updated,1,19) as updated_noms, author,score FROM ttrss_entries,ttrss_user_entries,ttrss_feeds WHERE ttrss_user_entries.feed_id = ttrss_feeds.id AND ttrss_user_entries.ref_id = ttrss_entries.id AND - user1428228
ttrss_user_entries.owner_uid = '1' AND (LOWER(ttrss_entries.title) REGEXP LOWER('(.?\b(.krieg.|konflikt.|.k.mpf.|.töt.|frieden.|feuerpause|waffen.|panzer|.gewehr.|.miliz.|armee|rebell.|aufstand|terror.)\b){2,}') OR LOWER(ttrss_entries.content) REGEXP LOWER('(.?\b(.krieg.|konflikt.|.k.mpf.|.töt.|frieden.|feuerpause|waffen.|panzer|.gewehr.|.miliz.|armee|rebell.|aufstand|terror.)\b){2,}')) AND ttrss_entries.date_entered > DATE_SUB(NOW(), INTERVAL 14 DAY) AND cat_id = '2' ORDER BY date_entered DESC LIMIT 30 OFFSET 0 failed: Got error 'repetition-operator operand invalid' from regexp - user1428228

1

这不完全是正则表达式的工作。更好的方法是扫描文本,然后计算唯一匹配组的数量。

在Ruby中,根据匹配计数进行分支非常简单。例如:

terms = /war|army|fighting|rebels|clashes/
text = "The war between the rebels and the army resulted in..."

# The real magic happens here.
match = text.scan(terms).uniq

# Do something if your minimum match count is met.
if match.count >= 2
  p match
end

这将打印["战争", "叛军", "军队"]

0

正则表达式可以解决问题,但是正则表达式会非常庞大。

请记住,它们只是简单的工具(基于有限状态自动机),因此没有任何内存来记住已经看到的单词。因此,这样的正则表达式虽然可能,但其看起来可能只是一个巨大的“或”块(即每个输入可能顺序的一个“或”)。

我建议自己进行解析,例如:

var searchTerms = set(yourWords);
int found = 0;
foreach (var x in words(input)) {
    if (x in searchTerms) {
        searchTerms.remove(x);
        ++found;
    }
    if (found >= 2) return true;
}
return false;

0
如果你想用正则表达式来完成所有操作,那么这可能不太容易。
但是你可以尝试像这样做:
<?php
...
$string = "The war between the rebels and the army resulted in several clashes this week. (4 hits)";


preg_match_all("@(\b(war|army|fighting|rebels|clashes))\b@", $string, $matches);
$uniqueMatchingWords = array_unique($matches[0]);
if (count($uniqueMatchingWords) >= 2) {
    //bingo
}

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