正则表达式确实是这里的正确工具。首先,让我们看看如何使用硬编码的正则表达式来解析此日志。
使用硬编码的正则表达式进行解析
var str = "JAN 01 00:00:01 <Admin> Action, May have spaces etc.";
var re = new Regex("^" +
@"(?<month>(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))" +
" " +
@"(?<day>\d+)" +
" " +
@"(?<hour>\d+)" +
":" +
@"(?<the_rest>.*)" +
"$");
var match = re.Match(str);
我们在这里所做的是使用命名捕获组逐段创建正则表达式。为简洁起见,我没有捕获所有相关信息,也没有花太多时间考虑在每个组的上下文中什么是有效输入(例如day
will match 999
,尽管那不是有效的一天)。这一切都可以在以后发生;现在,看看它的实际效果。
从预定义的部分构造正则表达式
下一步是很好地将每个捕获组的定义提取到字典中:
var groups = new Dictionary<string, string>
{
{ "month", "(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)" },
{ "day", @"\d+" },
{ "hour", @"\d+" },
{ "the_rest", ".*" },
};
鉴于此,我们现在可以构造相同的正则表达式
var re = new Regex("^" +
string.Format("(?<month{0}>)", groups["month"]) +
" " +
string.Format("(?<day{0}>)", groups["day"]) +
" " +
string.Format("(?<hour{0}>)", groups["hour"]) +
":" +
string.Format("(?<the_rest{0}>)", groups["the_rest"]) +
"$");
好的,这开始看起来像是可以动态构建的东西。
根据用户提供的规范构造正则表达式
假设我们想从一个看起来像的规范构建它
"{month} {day} {hour}:{the_rest}"
这该怎么做?用另一个正则表达式!具体来说,我们将使用 that 的重载Regex.Replace
,可以用函数的结果替换匹配项:
var format = "{month} {day} {hour}:{the_rest}";
var result = Regex.Replace(format, @"\{(\w+)\}", m => groups[m.Groups[1].Value]);
回来之前看看这个。
使用正则表达式解析输入
此时,我们可以传入一个格式规范,并根据该格式取回一个匹配输入的正则表达式。还剩下什么?要将正则表达式与输入匹配的结果转换回“动态”结构:
var format = "{month} {day} {hour}:{the_rest}";
var re = Regex.Replace(format,
@"\{(\w+)\}",
m => string.Format("(?<{0}>{1})", m.Groups[1].Value, groups[m.Groups[1].Value]));
var regex = new Regex("^" + re + "$", RegexOptions.ExplicitCapture);
var match = regex.Match(str);
拉出最终结果
在此刻:
- 我们可以测试
match.Success
一下动态构造的表达式是否与输入匹配
- 我们可以迭代
regex.GetGroupNames()
以获取解析中使用的组的名称
- 我们可以迭代
match.Groups
得到解析每个组的结果
所以让我们把它们放在字典里:
var results = regex.GetGroupNames().ToDictionary(n => n, n => match.Groups[n].Value);
成功!
您现在可以创建一个Parse
允许这样做的方法:
var input = "JAN 01 00:00:01 <Admin> Action, May have spaces etc.";
var format = "{month} {day} {hour}:{the_rest}";
var results = Parse(input, format);
Parse
将识别(但不允许用户修改)表达式,例如"{month}"
,同时允许用户自由混合和匹配这些表达式以解析输入。
查看最终结果。