.NET Regex表达式用于解析数字和数字范围列表

时间:2015-01-07 18:06:04

标签: c# .net regex

我需要解析逗号分隔的数字和数字范围列表。用户将字符串输入到UI中,看起来就像其中一个(六个不同的样本输入):

1-3, 5, 7-10
1
21.1
1.2-3,5.1,7-10.1
1-3, 5.1, 7-10, 21
1.1-3.1,5.1,7.1-10.1

我的最终目标是拥有一系列我可以在下游处理的数字和数字范围。例如,在解析第一个字符串示例之后,我的最终结果将是一个包含3个元素的集合:1-3,5和7-10。

使用C#和.NET Regex这个模式很好地填充了Matches集合中只包含我需要的项目(注意使用非捕获组):

(\d+(?:\.\d+)?-\d+(?:\.\d+)?)|(\d+(?:\.\d+)?)

我有两个问题:

  1. 我是否需要我的模式中的所有内容,或者是否有更简短的模式?

  2. 当字符串中包含无效字符时,是否可以添加到模式中以返回0匹配?例如,如果我在字符串中包含字母字符,我希望不会发生匹配。现在我通过两次传递执行此操作,一次验证字符串只有有效字符[\ d,.-],另一次传递以获取匹配,假设它在第一次传递中验证。

  3. 提前感谢您的想法。

    更新

    这是我最终选择的解决方案(参见@ Xiaoy312答案):

    public static IEnumerable<DataRange> ParseInput(string input)
    {
        if (!Regex.IsMatch(input.Replace(" ", string.Empty), @"^[\d\.,\-]+$"))
            return Enumerable.Empty<DataRange>();
    
        return Regex.Matches(input.Replace(" ", string.Empty), 
            @"(?<A>\d+(?:\.\d+)?)(?:-(?<B>\d+(?:\.\d+)?))?").Cast<Match>()
            .Select(m => new DataRange
            {
                A = double.Parse(m.Groups["A"].Value,
                     System.Globalization.CultureInfo.InvariantCulture),
                B = m.Groups["B"].Success ? double.Parse(m.Groups["B"].Value, 
                     System.Globalization.CultureInfo.InvariantCulture) : (double?)null
            });
    
    }
    
    public class DataRange
    {
        public double A;
        public double? B;
    }
    

    此处的示例用法:

    static void Main(string[] args)
    {
        Console.WriteLine("A\tB");
        var items = ParseInput("1");
        Array.ForEach(items.ToArray(), i => Console.WriteLine("{0}\t{1}", i.A, i.B));
        items = ParseInput("21.1");
        Array.ForEach(items.ToArray(), i => Console.WriteLine("{0}\t{1}", i.A, i.B));
        items = ParseInput("1-3,5,7-10");
        Array.ForEach(items.ToArray(), i => Console.WriteLine("{0}\t{1}", i.A, i.B));
        items = ParseInput("1.2-3,5.1,7-10.1");
        Array.ForEach(items.ToArray(), i => Console.WriteLine("{0}\t{1}", i.A, i.B));
        items = ParseInput("1-3, 5.1,  7-10,21");
        Array.ForEach(items.ToArray(), i => Console.WriteLine("{0}\t{1}", i.A, i.B));
        items = ParseInput("1.1-3.1,5.1,7.1-10.1");
        Array.ForEach(items.ToArray(), i => Console.WriteLine("{0}\t{1}", i.A, i.B));
        items = ParseInput("1.1-3.1,5.1,7.1-10.1a");
        Array.ForEach(items.ToArray(), i => Console.WriteLine("{0}\t{1}", i.A, i.B));
    }
    

    示例输出:

    A       B
    1
    21.1
    1       3
    5
    7       10
    1.2     3
    5.1
    7       10.1
    1       3
    5.1
    7       10
    21
    1.1     3.1
    5.1
    7.1     10.1
    

1 个答案:

答案 0 :(得分:1)

第一次匆忙尝试:

public IEnumerable<object> ParseInput(string input)
{
    return Regex.Matches(input.Replace(" ", string.Empty), @"(?<A>\d+(\.\d+)?)(-(?<B>\d+(\.\d+)?))?").Cast<Match>()
        .Select(m => new
        { 
            A = m.Groups["A"].Value,  
            B = m.Groups["B"].Value
        });
}

修正:

public IEnumerable<DataRange> ParseInput(string input)
{
    if (!Regex.IsMatch(input.Replace(Environment.NewLine, string.Empty), @"^[\d\.,\- ]+$"))
        return Enumerable.Empty<object>();

    return input
        .Replace(" ", string.Empty)
        .Split(new[] { Environment.NewLine, "," }, StringSplitOptions.RemoveEmptyEntries)
        .Select(x => Regex.Match(x, @"(?<A>\d+(\.\d+)?)(-(?<B>\d+(\.\d+)?))?"))
        .Select(m => new DataRange
        {
            A = double.Parse(m.Groups["A"].Value, System.Globalization.CultureInfo.InvariantCulture),
            B = m.Groups["B"].Success ? double.Parse(m.Groups["B"].Value, System.Globalization.CultureInfo.InvariantCulture) : (double?)null
        });
}

public class DataRange
{
    public double A;
    public double? B;
}

输入:

    const string SampleInput = 
    @"1-3, 5, 7-10, 
1
21.1
1.2-3,5.1,7-10.1
1-3, 5.1, 7-10, 21
1.1-3.1,5.1,7.1-10.1";

输出:

A B
1 3 
5 null 
7 10 
1 null 
21,1 null 
1,2 3 
5,1 null 
7 10,1 
1 3 
5,1 null 
7 10 
21 null 
1,1 3,1 
5,1 null 
7,1 10,1