正则表达式跳过模式

时间:2011-03-01 11:38:38

标签: c# regex

问题

我需要用百分号('%')替换所有星号('*')。方括号中的星号符号应该被忽略。

示例

    [Test]
    public void Replace_all_asterisks_outside_the_square_brackets()
    {
        var input = "Hel[*o], w*rld!";
        var output = Regex.Replace(input, "What_pattern_should_be_there?", "%")

        Assert.AreEqual("Hel[*o], w%rld!", output));
    }

3 个答案:

答案 0 :(得分:3)

尝试使用前瞻:

\*(?![^\[\]]*\])

这是一个更强大的解决方案,可以更好地处理[]块,甚至可以转义\[个字符:

string text = @"h*H\[el[*o], w*rl\]d!";
string pattern = @"
\\.                 # Match an escaped character. (to skip over it)
|
\[                  # Match a character class 
    (?:\\.|[^\]])*  # which may also contain escaped characters (to skip over it)
\]
|
(?<Asterisk>\*)     # Match `*` and add it to a group.
";

text = Regex.Replace(text, pattern,
    match => match.Groups["Asterisk"].Success ? "%" : match.Value,
    RegexOptions.IgnorePatternWhitespace);

如果您不关心转义字符,可以将其简化为:

\[          # Skip a character class
    [^\]]*  # until the first ']'
\]
|
(?<Asterisk>\*)

可以在没有评论的情况下编写:@"\[[^\]]*\]|(?<Asterisk>\*)"

要理解它的工作原理,我们需要了解Regex.Replace的工作原理:对于字符串中的每个位置,它都会尝试匹配正则表达式。如果失败,则移动一个字符。如果成功,则移动整个比赛。
在这里,我们有[...]块的虚拟匹配,所以我们可以跳过我们不想替换的星号,并且只匹配孤独的星号。该决定是在回调函数中进行的,该函数检查Asterisk是否匹配。

答案 1 :(得分:2)

我无法想出一个纯粹的RegEx解决方案。因此,我正在为您提供务实的解决方案。我测试了它并且它有效:

[Test]
public void Replace_all_asterisks_outside_the_square_brackets()
{
    var input = "H*]e*l[*o], w*rl[*d*o] [o*] [o*o].";
    var actual = ReplaceAsterisksNotInSquareBrackets(input);
    var expected = "H%]e%l[*o], w%rl[*d*o] [o*] [o*o].";

    Assert.AreEqual(expected, actual);
}

private static string ReplaceAsterisksNotInSquareBrackets(string s)
{
    Regex rx = new Regex(@"(?<=\[[^\[\]]*)(?<asterisk>\*)(?=[^\[\]]*\])");

    var matches = rx.Matches(s);
    s = s.Replace('*', '%');

    foreach (Match match in matches)
    {
        s = s.Remove(match.Groups["asterisk"].Index, 1);
        s = s.Insert(match.Groups["asterisk"].Index, "*");
    }
    return s;
}

答案 2 :(得分:-1)

EDITED

好的,这是我最后的尝试;)

使用负面后瞻(?<!)和否定前瞻(?!)

var output = Regex.Replace(input, @"(?<!\[)\*(?!\])", "%");

这也将评论中的测试传递给另一个答案"Hel*o], w*rld!"