.NET 正则表达式来解析数字列表和数字范围
.NET Regex expression to parse list of numbers and number ranges
我需要解析以逗号分隔的数字列表和数字范围。这些字符串由用户输入到 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+)?)
不过我有两个问题:
我的模式中是否需要所有这些,或者是否有更简短的模式?
当字符串中包含无效字符时,我可以向模式添加一些内容以 return 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
第一次仓促尝试:
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
我需要解析以逗号分隔的数字列表和数字范围。这些字符串由用户输入到 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+)?)
不过我有两个问题:
我的模式中是否需要所有这些,或者是否有更简短的模式?
当字符串中包含无效字符时,我可以向模式添加一些内容以 return 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
第一次仓促尝试:
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