解析日志字符串,不:拆分

时间:2012-06-20 10:24:13

标签: c# regex string-parsing

搜索了一下,但我只发现了用逗号大小分割的情况。这种情况不同。

为了解释我的问题,我将展示一个小例子:

JAN 01 00:00:01 <Admin> Action, May have spaces etc.

(这是一个日志条目)

我想将这个字符串解析成几个变量。第一位显然是一个日期,没有一年。在&lt;&gt;之间列出登录名,并在日志条目后面。

配置应该是这样的:

{month} {day} {hour}:{minute}:{second} <{login}> {the_rest}

这将允许更改而不需要对整个事物进行硬编码(使用拆分等)。

我认为使用正则表达式在这里可能很有用,但我对它并不是很了解,如果它在这种情况下可以使用的话。 速度并不重要,但我真的不知道如何实现这一点。

谢谢,

〜Tgys

6 个答案:

答案 0 :(得分:3)

string line = "JAN 01 00:00:01 <Admin> Action, May have spaces etc.";
var m = Regex.Match(line, @"(\w{3} \d{2} \d{2}:\d{2}:\d{2}) \<(\w+)\>([\w ]+),([\w ]+)");

var date = DateTime.ParseExact(m.Groups[1].Value,"MMM dd HH:mm:ss",CultureInfo.InvariantCulture);
var user = m.Groups[2].Value;
var action = m.Groups[3].Value;
var text = m.Groups[4].Value;

答案 1 :(得分:1)

您可以使用拆分静止,拆分空格字符。

显然你的问题是你想在一定数量的分裂后保留空格,以便你的“其余”保持在一起。

split的可选int参数允许您提供您希望执行的最大拆分量,因此可能会提供您正在寻找的解决方法。

http://msdn.microsoft.com/en-us/library/c1bs0eda.aspx

答案 2 :(得分:1)

您也可以将其用作正则表达式并使用捕获的组:

^(?<Month>\w{3})\s(?<Day>\d{2})\s(?<Hour>\d{2}):(?<Min>\d{2}):(?<Sec>\d{2})\s(?<User>\<(\w.+?)\>)(.+)$

RegEx Hero sample here

编辑:错过了用户部分。

答案 3 :(得分:1)

正则表达式确实是正确的工具。首先,让我们看看如何使用硬编码的正则表达式来解析此日志。

使用硬编码正则表达式

进行解析
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将匹配999,尽管那是不是有效的一天)。这一切都可以在以后发生;现在,see it in action

从预定义的部分

构造正则表达式

下一步是很好地将每个捕获组的定义提取到字典中:

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}"

怎么做?用另一个正则表达式!具体来说,我们将使用Regex.Replace的重载,它可以用函数的结果替换匹配:

var format = "{month} {day} {hour}:{the_rest}";
var result = Regex.Replace(format, @"\{(\w+)\}", m => groups[m.Groups[1].Value]);
回来之前

See this in action

使用正则表达式解析输入

此时,我们可以传入格式规范并获取与基于此格式的输入匹配的正则表达式。还剩什么?要将正则表达式与输入匹配的结果转换回“动态”结构:

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}"等表达式,同时允许用户自由地混合和匹配这些表达式以解析输入。

<强> See the final result

答案 4 :(得分:0)

您可以使用此正则表达式:

(?<Month>[A-Z]{3})\s(?<Day>[0-9]{1,2})\s(?<Hour>[0-9]{1,2}):(?<Minute>[0-9]{1,2}):(?<Second>[0-9]{1,2})\s<(?<Login>[^>]+)>(?<Rest>.*)

这有点笨拙和复杂,但我希望下面的例子可以让你得到你想要的。

class Foo
{
public string Month { get; set; }
public int Day { get; set; }
public int Hour { get; set; }
public int Minute { get; set; }
public int Second { get; set; }
public string Login { get; set; }
public string Rest { get; set; }
}

string strRegex = @"(?<Month>[A-Z]{3})\s(?<Day>[0-9]{1,2})\s(?<Hour>[0-9]{1,2}):(?<Minute>[0-9]{1,2}):(?<Second>[0-9]{1,2})\s<(?<Login>[^>]+)>(?<Rest>.*)";
RegexOptions myRegexOptions = RegexOptions.None;
Regex myRegex = new Regex(strRegex, myRegexOptions);
string strTargetString = @"JAN 01 00:00:01 <Admin> Action, May have spaces etc. \n";

foreach (Match myMatch in myRegex.Matches(strTargetString))
{
    if (myMatch.Success)
    {
        new Foo
        {
            Month = myMatch.Groups["Month"].Value,
            Day = Convert.ToInt32(myMatch.Groups["Day"].Value),
            Hour = Convert.ToInt32(myMatch.Groups["Hour"].Value),
            Minute = Convert.ToInt32(myMatch.Groups["Minute"].Value),
            Second = Convert.ToInt32(myMatch.Groups["Second"].Value),
            Login = myMatch.Groups["Login"].Value,
            Rest = myMatch.Groups["Rest"].Value
        }
    }
}

答案 5 :(得分:0)

以下正则表达式将起到作用。

^([A-Z]{3})\s*([0-9]{1,2})\s*([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})\s*<(.+)>\s*(.+)

使用在线regex builder进行测试。

它将返回7个被捕获的组。

  • 第1组:([A-Z] {3}):月
  • 第2组:([0-9] {1,2}):日
  • 第3组:([0-9] {1,2}):小时
  • 第4组:([0-9] {1,2}):分钟
  • 第5组:([0-9] {1,2}):第二个
  • 第6组:(。+):用户名
  • 第7组:(。+):其余的