在JavaScript中,除非单词列在被排除的单词列表中,否则如何使用正则表达式进行匹配?

时间:2012-01-13 17:39:51

标签: javascript regex

除了某些单词列表外,如何使用正则表达式匹配任何单词(\ w)?例如:

我想匹配单词useutilize以及其后的任何字词,除非这些字词是somethingfish

use this  <-- match
utilize that  <-- match
use something   <-- don't want to match this
utilize fish <-- don't want to match this

如何指定我不想匹配的单词列表?

4 个答案:

答案 0 :(得分:20)

您可以使用否定前瞻来确定您要匹配的单词不是特定的东西。您可以使用以下正则表达式执行此操作:

(use|utilize)\s(?!fish|something)(\w+)

这将匹配“use”或“utilization”后跟一个空格,然后如果下面的单词不是“fish”或“something”,它将匹配下一个单词。

答案 1 :(得分:3)

这应该这样做:

/(?:use|utilize)\s+(?!something|fish)\w+/

答案 2 :(得分:3)

不要对正则表达式进行硬编码

不是试图将所有搜索和排除术语放入单个硬编码的正则表达式中,而是使用短路评估来选择字符串,这通常更易于维护(当然更具可读性)匹配所需的术语,然后拒绝包含不良术语的字符串。

然后,您可以将此测试封装到一个函数中,该函数根据数组的运行时值返回一个布尔值。例如:

'use strict';

// Join arrays of terms with the alternation operator.
var searchTerms   = new RegExp(['use', 'utilize'].join('|'));
var excludedTerms = new RegExp(['fish', 'something'].join('|'));

// Return true if a string contains only valid search terms without any
// excluded terms.
var isValidStr = function (str) {
    return (searchTerms.test(str) && !excludedTerms.test(str));
};

isValidStr('use fish');       // false
isValidStr('utilize hammer'); // true

答案 3 :(得分:-6)

  

有些人在遇到问题时会想“我知道,我会用   正则表达式。“

     

现在他们有两个问题。

正则表达式适合匹配常规符号序列,而不是单词。任何词法分析器+解析器都会更合适。例如,在Antlr中,此任务的语法看起来非常简单。如果你无法承受词法分析器/解析器背后的学习曲线(它们对于你的给定任务来说非常容易),那么将你的文本分成带有正则表达式的单词,然后简单搜索前瞻1就足够了。

带有单词的正则表达式非常复杂。它们难以阅读且难以保留。

更新: 感谢所有的downvotes。这是我的意思的一个例子。

import re

def Tokenize( text ):
    return re.findall( "\w+", text )

def ParseWhiteListedWordThenBlackListedWord( tokens, whiteList, blackList ):
    for i in range( 0, len( tokens ) - 1 ):
        if tokens[i] in whiteList and tokens[i + 1] not in blackList:
            yield ( tokens[i], tokens[i + 1] )

以下是一些性能测试:

>>> timeit.timeit( 'oldtime()', 'from __main__ import oldtime', number=1 )
0.02636446265387349

>>> timeit.timeit( 'oldtime()', 'from __main__ import oldtime', number=1000 )
28.80968123656703

>>> timeit.timeit( 'newtime()', 'from __main__ import newtime', number=100 )
44.24506212427741

>>> timeit.timeit( 'newtime11()', 'from __main__ import newtime11', number=1 ) +
timeit.timeit( 'newtime13()', 'from __main__ import newtime13', number=1000 )
103.07938725936083

>>> timeit.timeit( 'newtime11()', 'from __main__ import newtime11', number=1 ) + 
timeit.timeit( 'newtime12()', 'from __main__ import newtime12', number=1000 )
0.3191265909927097

一些说明: 测试超过了Jane Austin的Pride anf Prejudice的英文文本,第一个词是'Mr'和'my',第二个词是'Bennet'和'亲爱的'。

oldtime()是正则表达式。 newtime()是Tokenizer + Parser,请注意它运行了100次,而不是1000次,因此,它的可比时间为~442。

接下来的两个测试是模拟在同一文本上重复运行Parser,同时重用Tokenizer结果。

newtime11()仅限Tokenizer。 newtime13()是Parser,结果转换为list(模拟结果的遍历)。 newtime12()只是Parser。

好吧,正则表达式在单次传递的情况下相当快,即使在生成器的情况下(在Tokenizer + Parser的情况下,大部分时间用于标记文本)。但是当你可以重复使用标记化文本并懒惰地评估解析器结果时,生成器表达式非常快。

可以进行相当多的性能优化,但它会使解决方案复杂化,可能会使正则表达式成为最佳实现。

tokenizer + parser方法既有优点也有缺点: - 解决方案的结构更复杂(更多元素)但每个元素更简单 - 元素易于测试,包括自动测试 - 它很慢,但重复使用相同的文本并懒洋洋地评估结果会变得更好 - 由于发电机和惰性评估,可以避免一些工作 - 改变白名单和/或黑名单是微不足道的 - 拥有几个白名单,几个黑名单和/或它们的组合是微不足道的 - 添加重新使用标记生成器结果的新解析器是微不足道的

现在到了那个棘手的你不需要它的问题。你不会需要原始问题的解决方案,除非它是更大任务的一部分。而那个更大的任务应该决定最好的方法。

更新:对http://commandcenter.blogspot.ru/2011/08/regular-expressions-in-lexing-and.html的lexing和解析中的regualr表达进行了很好的讨论。我将用引用来概括它

  

鼓励正则表达式作为所有文本处理的灵丹妙药   问题不仅是懒惰和糟糕的工程,它还强化了   不应该使用它们的人使用它们。