通常,我喜欢正则表达式的挑战,甚至更好 - 解决它们 但似乎我有一个我无法弄清楚的案例。
我有一串用CSV格式分隔的值,它们看起来像这样:
123-234;FOO-456;45-67;FOO-FOO;890;FOO-123;11-22;123;123;44-55;098-567;890;123-FOO;
在这一行中,我希望匹配所有整数和整数范围,以便稍后提取它们。可能只有单个值(没有分号)。
经过大量的搜索,我设法写了这个表达式:
(?:^|;)(?<range>\d+-\d+)(?:$|;)|(?:^|;)(?<integer>\d+)(?:$|;)
我正在使用的测试字符串:
123
123-234;FOO-456;45-67;FOO-FOO;890;FOO-123;11-22;123;123;44-55;098-567;890;123-FOO;
123-456
123-FOO
FOO-123
FOO-FOO
第1行和第3行正确匹配,第4,5行不是 在第2行中,只有一个值正确匹配。
以下是regex101.com的链接,说明了它:https://regex101.com/r/zA7uI9/5
我还需要分别选择整数和范围(在不同的组中)。
注意:我找到了一个可以帮助我并尝试答案的问题(通过调整)但它没有用。
Regular expression for matching numbers and ranges of numbers
你对我遗失的东西有什么想法吗?
将“使用”此正则表达式的语言是C#,但我不知道它是否对我的问题有用。
barlop 添加了
以下是当前正则表达式给出的匹配,如regex101.com链接所示 以及他 所以他的正则表达式似乎错过了123中的一个,44-45和最后的89. 123-234;FOO-456;45-67;FOO-FOO;890;FOO-123;11-22;123;123;44-55;098-567;89
123-234
45-67
890
11-22
123
098-567
答案 0 :(得分:6)
使用内置的CSV解析器并分别检查每个字段:
using Microsoft.VisualBasic.FileIO;
....
var str = "123-234;FOO-456;45-67;FOO-FOO;890;FOO-123;11-22;123;123;44-55;098-567;890;123-FOO;";
var csv_parser = new TextFieldParser(new StringReader(str));
csv_parser.HasFieldsEnclosedInQuotes = false; // Fields are not enclosed with quotes
csv_parser.SetDelimiters(";"); // Setting delimiter
string[] fields;
var range_fields = new List<string>();
var integer_fields = new List<string>();
while (!csv_parser.EndOfData)
{
fields = csv_parser.ReadFields();
foreach (var field in fields)
{
if (!string.IsNullOrWhiteSpace(field) && field.All(x => Char.IsDigit(x)))
{
integer_fields.Add(field);
Console.WriteLine(string.Format("Intger field: {0}", field));
}
else if (!string.IsNullOrWhiteSpace(field) && Regex.IsMatch(field, @"\d+-\d+"))
{
range_fields.Add(field);
Console.WriteLine(string.Format("Range field: {0}", field));
}
}
}
csv_parser.Close();
结果是:
Range field: 123-234
Range field: 45-67
Intger field: 890
Range field: 11-22
Intger field: 123
Intger field: 123
Range field: 44-55
Range field: 098-567
Intger field: 890
你的正则表达式失败的原因是你实际上消费非捕获组的分隔符(即(?:^|;)
和(?:$|;)
仍匹配文本,该文本被追加匹配值,正则表达式索引前进到;
,字符串的开始/结束后的位置。
您需要使用的是lookarounds。它们不消耗文本,只是检查是否可以在当前位置之前或之后找到与环绕模式匹配的文本。因此,您有机会获得重叠匹配,并且这是场景非常方便的场景之一。
(?<=^|;)((?<range>\d+-\d+)|(?<integer>\d+))(?=$|;)
regex demo for a .NET regex at a .NET regex syntax supporting RegexStorm
一个很好的图表:
请注意RegexOptions.ExplicitCapture
flag的使用:这样,我们避免使用编号(即未命名)捕获组捕获子匹配,只获取命名捕获(正是我们需要的)。
var s = "123-234;FOO-456;45-67;FOO-FOO;890;FOO-123;11-22;123;123;44-55;098-567;890;123-FOO;";
var rx = new Regex(@"(?<=^|;)((?<range>\d+-\d+)|(?<integer>\d+))(?=$|;)", RegexOptions.ExplicitCapture);
var result = rx.Matches(s)
.Cast<Match>()
.Select(x => x.Groups["range"].Success ?
x.Groups["range"].Value : x.Groups["integer"].Value
).ToList();
foreach (var x in result)
Console.WriteLine(x);
答案 1 :(得分:2)
我无法轻易地在regex101中看到捕获组,因此该部分可能需要一些调整,但这会使所有匹配正确,并且捕获。希望有人会发布一个改进的答案,但与此同时。
(^\d+(?=;|$))|((?<=;)\d+$)|(?<=;)\d+(?=;)|\d+-\d+
图片,如由ro yo添加的图片
逻辑是,
匹配(^\d+(?=;|$))
或((?<=;)\d+$)
或(?<=;)\d+(?=;)
或\d+-\d+
即。例如一开始(或单独)123,结尾123,中间123,或任何地方。
我无法让regex101.com列出匹配项,但正则表达式正常工作
C:\blah>echo 123-234;FOO-456;45-67;FOO-FOO;890;FOO-123;11-22;123;123;44-55;098-567;89| grep -oP "(^\d+(?=;))|((?<=;)\d+$)|(?<=;)\d+(?=;)|\d+-\d+"
123-234
45-67
890
11-22
123
123
44-55
098-567
89
答案 2 :(得分:1)
(?<=;|^)[0-9]+(?:-[0-9]+|(?=;|$))
此正则表达式将执行以下操作:
123
等单个整数或123-456
等整数范围现场演示
https://regex101.com/r/oL1cN2/2
示例文字
123
123-234;FOO-456;45-67;FOO-FOO;890;FOO-123;11-22;123;123;44-55;098-567;890;123-FOO;
123-456
123-FOO
FOO-123
FOO-FOO
样本匹配
123
123-234
45-67
890
11-22
123
123
44-55
098-567
890
123-456