更新
我尝试将RegexOptions.Singleline添加到我的regex
选项中。它的工作原理是捕获了以前未捕获的行,但将整个文本文件置于第一个匹配项中,而不是根据需要在每个日期创建一个匹配项。
更新结束
更新#2
添加了使用Poul Bak的修改时显示匹配和分组的新输出。请参见下面标题为 Poul Bak修改后的输出
的屏幕截图更新#2结束
最终更新
将目标框架从4.6.1更新到4.7.1,并对Poul Bak的reg ex
进行一些调整,从而解决了所有问题。请参阅下面的Poul Bak的答案
最终更新结束
原始问题:背景
我有以下文本文件test_text.txt
:
2018-10-16 12:00:01 - Error 1<CR><LF>
Error 1 text line 1<CR><LF>
Error 1 text line 2<CR><LF>
2018-10-16 12:00:02 AM - Error 2<CR><LF>
Error 2 text line 1<CR><LF>
Error 2 text line 2<CR><LF>
Error 2 text line 3<CR><LF>
Error 2 text line 4<CR><LF>
2018-10-16 12:00:03 PM - Error 3
客观
我的目标是使每个匹配项由3个named
组组成:日期,日期和文本,如下所示。
注意:撇号仅用于表示匹配文本的限制。
我希望看到的比赛:
Match 1: '2018-10-16 12:00:01 - Error 1<CR><LF>'
Date group = '2018-10-16 12:00:01'
Delim group = ' - '
Text group = 'Error 1<CR><LF>Error 1 text line 1<CR><LF>Error 1 text line 2<CR><LF>'
Match 2: '2018-10-16 12:00:02 AM - Error 2<CR><LF>'
Date group = '2018-10-16 12:00:02 AM'
Delim group = ' - '
Text group = 'Error 2 text line 1<CR><LF>Error 2 text line 2<CR><LF>Error 2 text line 3<CR><LF>Error 2 text line 4<CR><LF>'
Match 3: `2018-10-16 12:00:03 PM - Error 3`
Date group = '2018-10-16 12:00:03 PM'
Delim group = ' - '
Text group = 'Error 3'
问题
我的正则表达式在第二行及后续文本行(例如,“错误1文本行1”,“错误2文本行1”)中无法正常工作。我希望它们会被捕获,因为我正在使用Multiline
选项。
如何修改我的正则表达式以捕获第二行和第二行文本?
当前代码
using System;
using System.Text.RegularExpressions;
namespace ConsoleApp_RegEx
{
class Program
{
static void Main(string[] args)
{
string text = System.IO.File.ReadAllText(@"C:\Users\bill\Desktop\test_text.txt");
string pattern = @"(?<Date>\d{4}-\d{2}-\d{2}\s{1}\d{2}:\d{2}:\d{2}.*)(?<Delim>\s-\s)(?<Text>.*\n|.*)";
RegexOptions regexOptions = (RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled);
Regex rx = new Regex(pattern, regexOptions);
MatchCollection ms = rx.Matches(text);
// Find matches.
MatchCollection matches = rx.Matches(text);
Console.WriteLine("Input Text\n--------------------\n{0}\n--------------------\n", text);
// Report the number of matches found.
Console.WriteLine("Output ({0} matches found)\n--------------------\n", matches.Count);
int m = 1;
// Report on each match.
foreach (Match match in matches)
{
Console.WriteLine("Match #{0}: ", m++, match.Value);
int g = 1;
GroupCollection groups = match.Groups;
foreach (Group group in groups)
{
Console.WriteLine(" Group #{0} {1}", g++, group.Value);
}
Console.WriteLine();
}
Console.Read();
}
}
}
当前输出
Poul Bak修改的输出(在正确的轨道上,但尚不完全正确)
@"(?<Date>\d{4}-\d{2}-\d{2}\s{1}\d{2}:\d{2}:\d{2}(?:\s\w\w)?)(?<Delim>\s-\s)(?<Text>([\s\S](?!\d{4}))*)"
答案 0 :(得分:1)
您可以使用从您修改的以下正则表达式:
@"(?<Date>\d{4}-\d{2}-\d{2}\s{1}\d{2}:\d{2}:\d{2}(?:\s\w\w)?)(?<Delim>\s-\s)(?<Text>([\s\S](?!\d{4}))*)"
我更改了'Date'
组,以便它接受'AM'
或'PM'
(否则它将只与第一个匹配)。
然后,我更改了'Text'
组,因此它匹配任何数量的任何字符(包括换行符),直到looks forward
为止并找到新日期。
修改:
我不明白,当您说'AM'
和'PM'
不匹配时,它们是'Date'
组的一部分。我假设您希望它们成为'Delim'
组的一部分,所以我已将支票移至该组。
我也已将网上论坛更改为非捕获网上论坛。
新正则表达式:
@"(?<Date>\d{4}-\d{2}-\d{2}\s{1}\d{2}:\d{2}:\d{2})(?<Delim>(?:\s\w\w)?\s-\s)(?<Text>(?:[\s\S](?!\d{4}))*)"
顺便说一句:您应该更改用于检查组的代码,如下所示:
foreach (Group group in groups)
{
Console.WriteLine(" Group #{0} {1}", group.Name, group.Value);
}
然后您将看到named Groups
和Name
旁的Value
。为组命名后,无需按索引访问。
修改 2:
关于“ group.Name”:我误用了“ Group”(大写),应该是:“ group.Name”。
这是正则表达式现在的样子:
@"(?<Date>\d{4}-\d{2}-\d{2}\s{1}\d{2}:\d{2}:\d{2}(?:\s\w\w)?)(?<Delim>\s-\s)(?<Text>(?:[\s\S](?!\d{4}))*)"
我建议您设置'RegexOptions.ExplicitCapture
'标志,然后仅获取命名组。