Regex Parse最多只能输入一次

时间:2018-03-07 14:19:27

标签: c# regex

我正在使用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)))?.*$

我怎么能这样做?

2 个答案:

答案 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>)

这会检查:

  • 输入开始
  • ymwd中的一个
    • 四次(=团体数量)
  • 输入结束
  • 至少有一个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");