如何使用带有线锚的 C# 正则表达式 Lookbehind
Posted
技术标签:
【中文标题】如何使用带有线锚的 C# 正则表达式 Lookbehind【英文标题】:How to use C# Regular Expression Lookbehind with line anchors 【发布时间】:2018-08-21 02:08:45 【问题描述】:当同时使用行开始和行结束锚时,我在 C# 的正则表达式匹配中遇到后视断言问题。 在下面的示例中,Regex B 的行为完全符合我的预期(并且如此处所述:https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference
我最初对 RegEx A 不匹配第 1 行感到惊讶。现在我想我明白为什么 RegEx A 不匹配第 1 行了。[因为断言是零宽度 - 表达式基本上是 ^\d2$,这显然与 4 位数年份不匹配 - 这就是它匹配第 6 行和第 7 行的原因。
我知道我可以像这样重写肯定断言(RegEx A):^19\d2$。
但我的最终目标是像 RegEx C 这样的正则表达式 - 使用否定断言来查找所有不以给定前缀开头的字符串。也就是说,我正在尝试创建一个带有否定断言的表达式,它对第 3 行和第 4 行而不是第 5-7 行返回 true。
RegEx D 是 C# 文档中类似的否定断言示例,但不使用开始/结束锚点,第 3 行和第 4 行以及第 5-7 行也是如此。
考虑到这一点,我怎样才能使否定断言(如 RegEx C)与 line-begin/-end 锚点一起工作,以便在验证输入是单行时它的功能类似于 RegEx D 中的示例?
我想知道这是否根本不可能使用断言。这意味着替代方案是表达所有评估为否定例外的积极案例(类似于在 Regex E 中使用 19),但是当目标是排除一个特定的单个(可能是复杂的)案例。
谢谢!
示例程序:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
namespace RegExTest
class Program
static void Main(string[] args)
string[] reList = new string[]
@"^(?<=19)\d2$", // RegEx A
@"(?<=19)\d2", // RegEx B
@"^(?<!19)\d2$", // RegEx C
@"(?<!19)\d2\b", // RegEx D
@"^19\d2$", // RegEx E
;
string[] tests = new string[]
"1999", // Line 1
"1851 1999 1950 1905 2003", // Line 2
"1895", // Line 3
"2095", // Line 4
"195", // Line 5
"18", // Line 6
"19", // Line 7
;
foreach (var r in reList)
var re = new Regex(r);
Console.WriteLine("");
Console.WriteLine($"r");
Console.WriteLine("==========================");
foreach (var s in tests)
Console.WriteLine($"s=re.IsMatch(s)");
if (re.IsMatch(s))
foreach (Match m in re.Matches(s))
Console.WriteLine($"Match @ (m.Index, m.Length)");
输出:
^(?<=19)\d2$
==========================
1999=False
1851 1999 1950 1905 2003=False
1895=False
2095=False
195=False
18=False
19=False
(?<=19)\d2
==========================
1999=True
Match @ (2, 2)
1851 1999 1950 1905 2003=True
Match @ (7, 2)
Match @ (12, 2)
Match @ (17, 2)
1895=False
2095=False
195=False
18=False
19=False
^(?<!19)\d2$
==========================
1999=False
1851 1999 1950 1905 2003=False
1895=False
2095=False
195=False
18=True
Match @ (0, 2)
19=True
Match @ (0, 2)
(?<!19)\d2\b
==========================
1999=False
1851 1999 1950 1905 2003=True
Match @ (2, 2)
Match @ (22, 2)
1895=True
Match @ (2, 2)
2095=True
Match @ (2, 2)
195=True
Match @ (1, 2)
18=True
Match @ (0, 2)
19=True
Match @ (0, 2)
^19\d2$
==========================
1999=True
Match @ (0, 4)
1851 1999 1950 1905 2003=False
1895=False
2095=False
195=False
18=False
19=False
【问题讨论】:
我不清楚你想要什么输出。正则表达式 D 没有行开始/结束标记,所以你是什么意思让 C 像 D 一样工作? C有什么问题? 这个问题确实需要澄清一下,但是您的意思是使用负前瞻功能吗?像:^(?!19)\d4$ ? @NetMage - 第 3 行和第 4 行为真,第 5-7 行为假。^(?<!19)\d4$
呢?
@Brad 你试过我的建议了吗?断言它不是以 19 开头并且由 4 位数字组成,其中包含一个完整的行……是一个否定断言。听起来你需要什么,不是吗?你的意思是只专门使用lookbehind吗?这在我的书中会有点倒退......
【参考方案1】:
您将环视断言与正常模式的默认行为混淆了。环视断言这意味着它不消耗字符。
它寻找一个条件,如果满足则将光标带回它开始的位置,否则它会使引擎回溯或立即失败。
正则表达式 A ^(?<!19)\d2$
不应匹配字符串 1 1999
,因为引擎以这种方式工作:
^
断言字符串的开头(我们在位置 0)
(?<!19)
检查前面的字符是否不是19
(确定在
位置 0 我们没有前面的字符,所以这满足)
\d2
消耗两位数(我们在位置2)
$
断言字符串结尾(实际上我们还有 2 个字符要到达
字符串结束,因此引擎立即失败)
所以你必须这样做^\d2(?<!19)\d2$
或^(?!19)\d4$
第二个更合适。
【讨论】:
对断言的良好解释,因此被赞成,但没有回答完整的问题。 我在答案中添加了最后一行,它指向了正确的解决方案。 @布拉德 完美,谢谢。如果(例如)我只想捕获最后 2 位数字,则提供这两种替代方案会使行为一清二楚,并提供后视替代方案。 对于负前瞻,您也可以通过分隔它们来捕获最后两位数:^(?!19)\d2(\d2)$
以上是关于如何使用带有线锚的 C# 正则表达式 Lookbehind的主要内容,如果未能解决你的问题,请参考以下文章
需要一些带有(正则表达式)的 C# 代码,这将改变 url 域