0

我目前有两个单独的正则表达式模式来查找目标词+下一个词和目标词+上一个词:

string text = "Here is a test MYWORD statement for MYWORD regex";
string pattern = "(\\bMYWORD\\s)(\\w+)"; //MYWORD statement; MYWORD regex
string pattern = "(\\w+)(\\s\\bMYWORD)"; //test MYWORD; for MYWORD

正则表达式是否提供了一种优雅的方法来组合上述两种模式以用于单个调用?

谢谢

编辑:非常感谢 m.buettner 和 Qtax 的精彩解释和示例 - 非常有用!

我已经尝试过提供的一些示例,这些示例在所需的上下文中与“MYWORD”匹配,但也许我还不够清楚:我正在尝试返回上面评论的所有短语,即:

Matches(pattern) 应该返回以下所有字符串:

'MYWORD statement'
'MYWORD regex'
'test MYWORD'
'for MYWORD'

如果我最初的问题解释得不够好,我深表歉意!

4

3 回答 3

5

在前瞻中进行匹配:

string pattern = @"\b(?=(\w+\s+MYWORD|MYWORD\s+\w+)\b)";

string[] result = Regex.Matches(text, pattern)
                       .Cast<Match>()
                       .Select(match => match.Groups[1].Value)
                       .ToArray();

此正则表达式在匹配时不消耗任何字符,这使得重叠匹配成为可能。您不必担心无限循环,因为正则表达式引擎会在开始寻找下一个匹配项之前自动向前移动一个位置。捕获组仍然正常工作。

如果您需要像提到的其他响应者一样在字符串的开头和结尾处理匹配项,应该这样做:

string pattern = @"\b(?=((?:^|\w+\s+)MYWORD|MYWORD(?:\s+\w+|$))\b)";

更新:一位评论者询问如何在不包括目标词的情况下捕获前后单词。答案很简单,但并不明显:

string pattern = @"\b(?=((\w+)\s+MYWORD|MYWORD\s+(\w+))\b)";

string[] result = Regex.Matches(text, pattern)
                       .Cast<Match>()
                       .Select(match => match.Groups[2].Value + match.Groups[3].Value)
                       .ToArray();

简单的部分是为单个单词添加捕获组。不明显的部分是意识到在 .NET 中,如果捕获组不参与匹配,并且您访问它的Value属性,您会得到一个空字符串。我们知道两组中只有一组将参加每场比赛。我们不需要知道它是哪一个,我们只需要它的价值。连接字符串值给了我们想要的东西。

但它变得更好:

string[] result = Regex.Matches(text, pattern)
                       .Cast<Match>()
                       .Select(match => match.Result("$2$3"))
                       .ToArray();

Result()方法没有得到太多使用,因为 .NET 的 Regex API 的其余部分设计得非常好,但是当它有用时,它就很棒!

于 2013-07-07T13:40:40.307 回答
2

首先,一些建议:使用逐字字符串。他们使逃逸更容易处理:

string pattern = @"(\bMYWORD\s)(\w+)"; //MYWORD statement; MYWORD regex
string pattern = @"(\w+)(\s\bMYWORD)"; //test MYWORD; for MYWORD

请注意,您的第二个模式在错误的末尾有单词边界:

string pattern = @"(\w+)(\sMYWORD\b)"; //test MYWORD; for MYWORD

现在,天真的方法就是这样:

string pattern = @"(\w+)\s(MYWORD)\s(\w+)";

这有几个问题。首先,它需要两个单词都在那里,所以如果MYWORD出现在字符串的一端,你将不会得到任何匹配。这可以通过允许使用锚点而不是单词来解决:

string pattern = @"(?:(\w+)\s|^)(MYWORD)(?:\s(\w+)|$)";

现在还剩下一个问题。匹配不能重叠。如果你有abc MYWORD def MYWORD ghi,第二个MYWORD将不匹配。您可以通过使用lookarounds从匹配中排除周围的单词来解决此问题:

string pattern = @"(?<=(\w+)\s|^)(MYWORD)(?=\s(\w+)|$)";

如果要允许匹配,则既不在字符串的末尾,也没有相邻的单词(例如foo. MYWORD bar,其中.“阻止”了前面的单词)。只需将环视设置为可选。如果它们可以匹配,它们将被包含,如果不匹配,它们不会导致模式失败:

string pattern = @"(?<=(\w+)\s)?(MYWORD)(?=\s(\w+))?";

工作演示。

于 2013-07-07T10:59:34.140 回答
0

对于您的示例,一些简单的方法可以工作:

(\w+)\sMYWORD\s(\w+)

但这要求两边都有字MYWORD

如果某些方面可能没有一个词,您可以将它们设为可选,例如:

(?:(\w+)\s)?\bMYWORD\b(?:\s(\w+))?

但这将匹配 aMYWORD周围没有单词。

如果您想将 aMYWORD与它周围的至少一个单词匹配,您可以使用:

(?:(\w+)\sMYWORD\b(?:\s(\w+))?|\bMYWORD\s(\w+))

尽管如此,右边的单词将在第 2 组或第 3 组中。

于 2013-07-07T10:58:07.950 回答