正则表达式:匹配一个可选单词

7

需要匹配句子的前半部分,直到某个给定的单词。 但是,如果该单词是可选的,那么我希望匹配整个句子。例如:

我有一个带有一个从句的句子。

我有一个句子,我喜欢它。

在第一种情况下,我想要 "我有一个句子"。 在第二种情况下,我想要"我有一个句子,我喜欢它。"

使用前瞻可以得到第一种情况,但是一旦我试图使其可选,以涵盖第二种情况,我就会得到整个第一句话。我尝试过让表达式懒惰...不起作用。

可以解决第一种情况的代码:

var regEx = new Regex(@".*(?=with)");
string matchstr = @"I have a sentence with a clause I don't want";

if (regEx.IsMatch(matchstr)) {
    Console.WriteLine(regEx.Match(matchstr).Captures[0].Value);
    Console.WriteLine("Matched!");
}
else {
    Console.WriteLine("Not Matched : (");
}

我希望能够使用以下表达式:

我希望这个表达式能够正常工作:

var regEx = new Regex(@".*(?=with)?");

有什么建议吗?

如果这些只是单纯的文字(一个微不足道的“模式”),考虑使用子字符串索引搜索而不是正则表达式。 - polygenelubricants
3个回答

11

有几种方法可以做到这一点。您可以像这样做:

^(.*?)(with|$)

第一组是勉强匹配,即尽可能少的字符。如果该组后面跟着with或行尾$ anchor,我们就有了整体匹配。

给定以下输入:

I have a sentence with a clause I don't want.
I have a sentence and I like it.

然后有两个匹配项(如在rubular.com上看到):

  • 匹配1:
    • 第1组: "我有一个句子 "
    • 第2组: "with"
  • 匹配2:
    • 第1组: "我有一个句子,我喜欢它"
    • 第2组: "" (空字符串)

如果您不需要区分这两种情况,则可以使用(?:with|$)使分组交替不捕获。

相关问题


当然,您可以不使用捕获组,并在备选部分中使用前瞻,即 ^.*?(?=with|$) http://www.rubular.com/r/1JVjxdk30T;这些都是相同基本思想的小变化。 - polygenelubricants
很好。使用了非捕获组,但是 (?:) 仍然因某些原因捕获了该组... (?=with|$) 则完全符合我的需求。谢谢! - James King
@James:非捕获和断言之间有区别。断言不会作为匹配的一部分被消耗掉。非捕获并不意味着不匹配。它仍然被匹配,但不会被捕获到一个组中。 - polygenelubricants
@James:我猜你在使用Captures[0]时,我本意是指Groups[1]。请参考https://dev59.com/GHA75IYBdhLWcg3wYYFQ。 - polygenelubricants
是的,我做到了!而且我也知道得更好:P 尽管对我来说仍然不清楚为什么Groups[0]返回I have a sentence with,以及为什么当我使用^(.*?)(?:with|$)Groups[1]返回I have a sentence - James King
显示剩余2条评论

1
string optional = "with a clause I don't want" 
string rx = "^(.*?)" + Regex.Escape(optional) + ".*$";

// displays "I have a sentence"
string foo = "I have a sentence with a clause I don't want.";
Console.WriteLine(Regex.Replace(foo, rx, "$1"));

// displays "I have a sentence and I like it."
string bar = "I have a sentence and I like it.";
Console.WriteLine(Regex.Replace(bar, rx, "$1"))

如果您不需要使用正则表达式提供的复杂匹配,那么您可以使用IndexOfRemove的组合。(当然,您还可以将逻辑抽象为帮助器和/或扩展方法等):
string optional = "with a clause I don't want" 

// displays "I have a sentence"
string foo = "I have a sentence with a clause I don't want.";
int idxFoo = foo.IndexOf(optional);
Console.WriteLine(idxFoo < 0 ? foo : foo.Remove(idxFoo));

// displays "I have a sentence and I like it."
string bar = "I have a sentence and I like it.";
int idxBar = bar.IndexOf(optional);
Console.WriteLine(idxBar < 0 ? bar : bar.Remove(idxBar));

这看起来好像可行...给它一个赞,因为它是一种不同的做法。谢谢! - James King

1
如果我理解你的需求正确,你想要匹配句子直到单词“with”,或者如果没有这个单词,就匹配整个句子?为什么不编写正则表达式来明确查找这两种情况呢?
/(.*) with |(.*)/

这样不会同时获取两种情况吗?


这也完全可行...我将其更改为(.*)(?=with)|.*以排除单词with。绝对点赞! - James King

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