在单词之前或之后匹配一个字符,但在正则表达式中不匹配

时间:2012-06-21 14:50:01

标签: regex

让我们说我需要匹配一个单词word,其中可能有一段时间在词之前或之后,但不是两者。然后是word.wordword。应匹配,但.word.不应匹配。我如何匹配这个并捕获单词之前和之后发生的事情?

这是一个简化的例子,我需要扩展到更复杂的案例。例如,现在符号.'可能出现在单词之前或之后,但它们只能出现一次。例如,.word'wordword.'.word'只是有效匹配中的一部分,但.'word.'之类的内容不匹配,甚至是.'word'

以上示例是我的主要优先事项,但额外的好处是添加句点和撇号的顺序。因此'.word.'word都应匹配。我认为应该对此有用的一种方法是\.?'?|'?\.?word,但我希望某种方式,OR子句中的语句数量不依赖于符号数。

4 个答案:

答案 0 :(得分:0)

确定。为了正确处理word出现在字符串开头或结尾的情况,需要更多时间。

 "(?:\.word(?:[^.]|$))|(?:(?:[^.]|^)word(?:[^.]|$))|(?:(?:[^.]|^)word\.)"

相同regexp Lookaheads Lookbehinds (在python中测试):

 "(?:\.word(?:(?!\.)|$))|(?:(?:(?<!\.)|^)word(?:(?!\.)|$))|(?:(?:(?<!\.)|^)word\.)"

有效:

 re.findall(pattern(above), '.word. .word .word. word.'") // return ['.word ', ' word.'] 

答案 1 :(得分:0)

这适用于javascript,包含您提供的好的和坏的值。

var func = function (str) {
    var result = true, match, re = /^([^a-z]+)[a-z]+([^a-z]+)$/i;
    if (re.test(str)) {
        match = re.exec(str);
        re = new RegExp("[" + match[1] + "]");
        result = !re.test(match[2]);
    }
    return result;
};

这是一个简单的解释。 如果字符串在字母前后包含非字母,则非字母将被提取并相互测试。否定测试结果以确定单词是好还是坏。

str = .'word.
".'", "word", "."
/[.']/.test( "." )

func函数需要一个单词(没有空格的字符)作为字符串。 如果你想检查一个句子然后用空格分割,那么检查每个单词。 这样的事情。

    var sentence = "What does .'words'. means?";
var words = sentence.split(/\s+/g);
    var areWordsOk;
for( var i = 0, len = words.length; i < len; i++ ){
    areWordsOk = func( words[i] );
    if( !areWordsOk ){
        throw new Error( "bad word." ); // error is thrown
    }
}

这是我的测试用例。 现场演示:http://jsfiddle.net/Tb68G/2 这是测试用例的来源。

var func = function (str) {
    var result = true, match, re = /^([^a-z]+)[a-z]+([^a-z]+)$/i;
    if (re.test(str)) {
        match = re.exec(str);
        re = new RegExp("[" + match[1] + "]");
        result = !re.test(match[2]);
    }
    return result;
};
test("test good values", function () {
    var arr = [
        "word",
        ".word",
        "word.",
        ".word",
        "'word",
        "word.'",
        ".word'"
    ];
    var i = arr.length,
    str;
    while (i--) {
        str = arr[i];
        equal(func(str), true, str + " should be true.");
    }
});
test("test bad values", function () {
    var arr = [
        ".word.",
        ".'word.'",
        ".'word'.",
        ".'word'"
    ];
    var i = arr.length,
    str;
    while (i--) {
        str = arr[i];
        equal(func(str), false, str + " should be false.");
    }
});

答案 2 :(得分:0)

哪种味道?如果它是JavaScript,这应该有效:

(?:^|[^\w.'])(?=[.']*(word))(?!'*\.'*\1'*\.)(?!\.*'\.*\1\.*')([.']*)\1([.']*)

说明:

  • (?:^|[^\w.']) - 确保word不是较大单词的尾端,并阻止正则表达式绕过前导分隔符(.或{{1} })如果他们在那里。

  • ' - 确保(?=[.']*(word\b))不是更长字的开头,并且除了您选择的分隔符之前没有任何内容。这个词目前没有消耗,它只是在组#1中被捕获,所以它可以用来锚定接下来的两个前瞻。

  • word - 仍然位于前导分隔符(如果有的话)之前,这可以确保,如果在该单词之前有(?!'*\.'*\1'*\.),那么之后就没有一个。{/ p>

  • . - 这对(?!\.*'\.*\1\.*')也是如此。

  • ' - 最后,继续使用该词以及任何前导或尾随分隔符,捕获#2和#3组中的那些。

如果您使用的是支持lookbehind的风味,它可能无济于事。大多数口味严格限制了后视镜中可以匹配的内容,使其无法完成此任务。上面的JavaScript正则表达式可能仍然是您的最佳选择。但是,这个正则表达式适用于.NET和JGSoft,这是我所知道的唯一支持完全不受限制的外观的风格:

([.']*)\1([.']*)

说明:

  • (?<=(?:\.(?<dot1>)|'(?<apos1>))*)\bword\b(?=(?:\.(?<dot2>)|'(?<apos2>))*)(?!\k<dot1>\k<dot2>|\k<apos1>\k<apos2>) - 向后扫描分隔符字符。当每个匹配时,跟随它的空捕获组有效地将该角色标记为已被看到。

  • (?<=(?:\.(?<dot1>)|'(?<apos1>))*) - 消费这个词。

  • \bword\b - 提前划分更多的分隔符并将它们关闭,就像后视一样。

  • (?=(?:\.(?<dot2>)|'(?<apos2>))*) - 断言点和撇号都不会出现在单词之前和之后。对空组的反向引用从不消耗任何字符,它只是断言该组已参与该匹配。

在这两种风格之后,Java的外观可能是最灵活的,但它也是出了名的错误。我应该能够通过将第一个(?!\k<dot1>\k<dot2>|\k<apos1>\k<apos2>)更改为*来将此正则表达式移植到Java,但它只会抛出“没有明显的最大长度”异常。同样,您可能最好使用上面与JavaScript兼容的正则表达式。

答案 3 :(得分:-1)

我认为正则表达式很酷......
但有时候,你需要使用其他方法,
看着这样一个看似简单的怪异表达......

我说CODE IT!

    int findWord(string text, string word, char ch, int startIdx = 0)
    {
        while(startIdx < text.Length)
        {
            int indexOf = text.IndexOf(word, startIdx);
            if (indexOf < 0) return -1;

            char preChar = (char) 0;
            char postChar = (char) 0;

            if (indexOf > 0)
                preChar = text[indexOf - 1];

            if (indexOf < text.Length - word.Length)
                postChar = text[indexOf + word.Length];


            if ((preChar == ch) ^ (postChar == ch))
            {
                return indexOf;
            }
            startIdx = indexOf + word.Length + 1;
        } 
    }

不是那么简单,它超过一行:) 但表现更好,如果你在一两个月后阅读它就可以理解。