我正在使用Date解析器(完全是Tenor解析器),我必须将日期设置为天数,周数,月数和年数的总和。
这看起来像1d1y或100m2w或1y1d1m1w。
我已经构建了自定义解析器,但我正在寻找使用Regex的更干净的解决方案。我必须检查它每个日期字符(d,w,m和y)最多包含一次并用整数分隔。
^(?<ValueDay>[0-9]+(d))?(?<ValueWeek>[0-9]+(w))?(?<ValueMonth>[0-9]+(m))?(?<ValueYear>[0-9]+(y))?$
我遇到的问题是它可能以任何顺序发生(1d1w和1w1d一样)。我尝试使用正向前瞻(?=)如下,但它并不匹配所有标准。
^(?=.*(?<ValueDay>[0-9]+(d)))?(?=.*(?<ValueWeek>[0-9]+(w)))?(?=.*(?<ValueMonth>[0-9]+(m)))?(?=.*(?<ValueYear>[0-9]+(y)))?.*$
我怎么能这样做?
答案 0 :(得分:1)
如果每组必须出现零次或一次,您可以使用以下内容:
^
(
(?(y)(?!)|(?<y>\d+)y)
|
(?(m)(?!)|(?<m>\d+)m)
|
(?(w)(?!)|(?<w>\d+)w)
|
(?(d)(?!)|(?<d>\d+)d)
)+
$
对于每个字母,它会检查具有该字母作为名称的组是否已匹配。如果是这样,它会失败并转到下一个字母。如果没有,它会尝试捕获该字母后跟的数字,并将该字母作为名称。
以前的答案 - 一切都发生 - 恰好一次版本:
^((?<y>\d+)y|(?<m>\d+)m|(?<w>\d+)w|(?<d>\d+)d){4}$(?<-y>)(?<-m>)(?<-w>)(?<-d>)
这会检查:
y
,m
,w
或d
中的一个
y
匹配 m
匹配 w
匹配 d
匹配 答案 1 :(得分:0)
Regex r = new Regex(@"^(\d+)([wydm])(?!.*\2)"
+ @"(?:(\d+)([wydm])(?!.*\4))?"
+ @"(?:(\d+)([wydm])(?!.*\6))?"
+ @"(?:(\d+)([wydm]))?$");
这应该有效。它至少匹配\d+[wydm]
一次,最多匹配四次。在此之上,当一个角色[wydm]
匹配时,它向前看并且第二次在文本中不会出现相同的角色。现在您可以从组中获取值:
int GetValue(Match m)
{
int GetGroupValue(Group numberGroup, Group characterGroup)
{
if (!numberGroup.Success) { return 0; }
int number = int.Parse(numberGroup.Value);
switch (characterGroup.Value)
{
case "d": return number;
case "w": return 7 * number;
case "m": return 31 * number;
case "y": return 365 * number;
default: throw new NotSupportedException(characterGroup.Value + " is not supported");
}
}
return GetGroupValue(m.Groups[1], m.Groups[2])
+ GetGroupValue(m.Groups[3], m.Groups[4])
+ GetGroupValue(m.Groups[5], m.Groups[6])
+ GetGroupValue(m.Groups[7], m.Groups[8]);
}
以下是一些验证核心性的测试:
var tests = new (string s, bool isOk, int desiredValue)[] {
("15y", true, 15*365),
("1000w", true, 1000*7),
("10000d", true, 10000),
("100000m", true, 100000*31),
("15y8y", false, 0),
("", false, 0),
("7y9w12m2d", true, 7*365 + 9*7 + 12*31 + 2),
("7d9m12w2y", true, 7 + 9*31 + 12*7 + 2*365),
("7y9w12m2dd", false, 0),
("7y9w12m2y", false, 0),
("7y9w12m2x", false, 0),
("-5y", false, 0),
("1", false, 0),
("y2", false, 0),
("yd", false, 0),
("7y1", false, 0),
("m5d", false, 0)
};
foreach (var test in tests)
{
Match m = r.Match(test.s);
if (m.Success != test.isOk)
{
throw new Exception("Test failed for " + test.Item1);
}
if (GetValue(m) != test.desiredValue)
{
throw new Exception("Test failed for " + test.Item1);
}
}
MessageBox.Show("All " + tests.Count() + " tests passed");