【发布时间】:2015-01-07 18:06:03
【问题描述】:
我需要解析一个逗号分隔的数字列表和数字范围。字符串由用户输入到 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 正则表达式,这个模式很好地用我需要的项目填充 Matches 集合(注意使用非捕获组):
(\d+(?:\.\d+)?-\d+(?:\.\d+)?)|(\d+(?:\.\d+)?)
不过我有两个问题:
我的模式中是否需要所有这些,或者是否有更简短的模式?
当字符串中包含无效字符时,是否可以向模式添加一些内容以返回 0 个匹配项?例如,如果我在字符串中任何我不希望发生匹配的地方包含一个字母字符。现在我通过两遍执行此操作,一次验证字符串是否只有有效字符 [\d,.- ],另一遍用于获取匹配项,假设它在第一遍中验证。
提前感谢您的想法。
更新:
这是我最终采用的解决方案(请参阅@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
【问题讨论】:
-
你在做什么不能通过两个或三个
String.Split()调用来完成? -
类似于
\d*(\.\d*)?(-(\d*(\.\d*)?))?的东西应该是有效的简化。要验证整个内容,您必须将该正则表达式包装在另一个捕获组中,其中包含逗号和可选空格。 -
我宁愿说
\d+(?:\.\d+)?(?:-\d+(?:\.\d+)?)?。使用0捕获组(整个匹配)。 -
这么说,你可能最好只使用
String.Split和double.TryParse。 -
@valverij,没什么特别的。我对选择这条路线的正则表达式感到满意。