使用 c# 和正则表达式解析日志文件
Posted
技术标签:
【中文标题】使用 c# 和正则表达式解析日志文件【英文标题】:Parsing logfile with c# and regex 【发布时间】:2016-02-19 09:54:29 【问题描述】:我有一个大的日志文件,看起来像下面的 3 行示例。
\LogFiles\W3SVC1\u_ex12.log:32:2015-01-04 07:11:22 &actor=%7B%22name%22%3A%5B%22Smith%2C%20Steve%22%5D%2C%22mbox%22%3A%5B%22mailto%3ASmith.Steve%40xyz.com%22%5D%7D&
\LogFiles\W3SVC1\u_ex12.log:32:2015-06-08 02:04:13 &actor=%7B%22name%22%3A%5B%22Brown%2C%20Bob%22%5D%2C%22mbox%22%3A%5B%22mailto%3ABrown.Bob%40xyz.com%22%5D%7D&
\LogFiles\W3SVC1\u_ex12.log:32:2014-08-02 05:50:37 &actor=%7B%22name%22%3A%5B%22Franklin%2C%20Francis%22%5D%2C%22mbox%22%3A%5B%22mailto%3AFranklin.Francis%40xyz.com%22%5D%7D&
我需要提取隐藏在日志文件中的日期、名称和 mailto 字段。
我尝试使用在线正则表达式生成器,但在它似乎变得笨拙之前只到了这一步。
using System;
using System.Text.RegularExpressions;
namespace ConsoleApplication1
class Program
static void Main(string[] args)
//test string
string txt="\LogFiles\W3SVC1\u_ex12.log:32:2014-08-02 05:50:37 &actor=%7B%22name%22%3A%5B%22Franklin%2C%20Francis%22%5D%2C%22mbox%22%3A%5B%22mailto%3AFranklin.Francis%40xyz.com%22%5D%7D&";
string re1=".*?"; // Non-greedy match on filler
string re2="((?:(?:[1]1\\d1\\d1\\d1)|(?:[2]1\\d3))[-:\\/.](?:[0]?[1-9]|[1][012])[-:\\/.](?:(?:[0-2]?\\d1)|(?:[3][01]1)))(?![\\d])"; // YYYYMMDD 1
Regex r = new Regex(re1+re2,RegexOptions.IgnoreCase|RegexOptions.Singleline);
Match m = r.Match(txt);
if (m.Success)
String yyyymmdd1=m.Groups[1].ToString();
Console.Write("("+yyyymmdd1.ToString()+")"+"\n");
Console.ReadLine();
有没有办法在 c# 中使用或不使用正则表达式来做到这一点?
谢谢!
【问题讨论】:
你试过HttpUtility.UrlDecode
吗?字符串解码后,提取会更容易。
您的“大型日志文件”有多大? 50MB? 10GB? 500GB?多少行文字?
@JamesBlond 最大的是 18MB
什么工具或库正在记录这个?尝试获取它的解析器。我确定有一个。
试试 Tx。这是一个示例 sn-p。 gist.github.com/anonymous/589458d2cc3dcdc390fe 它允许您直接在原始事件源上执行语言集成查询 (LINQ)。 tx.codeplex.com
【参考方案1】:
假设您使用正则表达式并且它是这种广义的行形式,这样的东西应该可以工作 -
(?m)^\S+:(?<Date>\d+-\d+-\d+)\s(?:(?!&actor=).)+&actor=(?:%[0-9a-fA-F]2)*name(?:%[0-9a-fA-F]2)*(?<LastName>(?:(?!%[0-9a-fA-F]2|mbox).)+)(?:%[0-9a-fA-F]2)+(?<FirstName>(?:(?!%[0-9a-fA-F]2|mbox).)*)(?:%[0-9a-fA-F]2)*mbox(?:%[0-9a-fA-F]2)+mailto(?:%[0-9a-fA-F]2)+(?<MailUser>(?:(?!%[0-9a-fA-F]2).)+)(?:%[0-9a-fA-F]2)+(?<MailDomain>(?:(?!%[0-9a-fA-F]2).)+)(?:%[0-9a-fA-F]2)+&
它使用正则表达式中修饰符组中的多行修饰符(?m)
。
格式化:
(?m)
^
\S+
:
(?<Date> #_(1 start)
\d+
-
\d+
-
\d+
) #_(1 end)
\s
(?:
(?! &actor= )
.
)+
&actor=
(?: % [0-9a-fA-F]2 )*
name
(?: % [0-9a-fA-F]2 )*
(?<LastName> #_(2 start)
(?:
(?! % [0-9a-fA-F]2 | mbox )
.
)+
) #_(2 end)
(?: % [0-9a-fA-F]2 )+
(?<FirstName> #_(3 start)
(?:
(?! % [0-9a-fA-F]2 | mbox )
.
)*
) #_(3 end)
(?: % [0-9a-fA-F]2 )*
mbox
(?: % [0-9a-fA-F]2 )+
mailto
(?: % [0-9a-fA-F]2 )+
(?<MailUser> #_(4 start)
(?:
(?! % [0-9a-fA-F]2 )
.
)+
) #_(4 end)
(?: % [0-9a-fA-F]2 )+
(?<MailDomain> #_(5 start)
(?:
(?! % [0-9a-fA-F]2 )
.
)+
) #_(5 end)
(?: % [0-9a-fA-F]2 )+
&
输出:
** Grp 1 [Date] - ( pos 31 , len 10 )
2015-01-04
** Grp 2 [LastName] - ( pos 80 , len 5 )
Smith
** Grp 3 [FirstName] - ( pos 91 , len 5 )
Steve
** Grp 4 [MailUser] - ( pos 133 , len 11 )
Smith.Steve
** Grp 5 [MailDomain] - ( pos 147 , len 7 )
xyz.com
---------------------
** Grp 1 [Date] - ( pos 197 , len 10 )
2015-06-08
** Grp 2 [LastName] - ( pos 246 , len 5 )
Brown
** Grp 3 [FirstName] - ( pos 257 , len 3 )
Bob
** Grp 4 [MailUser] - ( pos 297 , len 9 )
Brown.Bob
** Grp 5 [MailDomain] - ( pos 309 , len 7 )
xyz.com
----------------------
** Grp 1 [Date] - ( pos 359 , len 10 )
2014-08-02
** Grp 2 [LastName] - ( pos 408 , len 8 )
Franklin
** Grp 3 [FirstName] - ( pos 422 , len 7 )
Francis
** Grp 4 [MailUser] - ( pos 466 , len 16 )
Franklin.Francis
** Grp 5 [MailDomain] - ( pos 485 , len 7 )
xyz.com
此外,稍作修改,您可以将它们全部放入 CaptureCollection 列表 在一场比赛中。
C#
string log =
@"
\LogFiles\W3SVC1\u_ex12.log:32:2015-01-04 07:11:22 &actor=%7B%22name%22%3A%5B%22Smith%2C%20Steve%22%5D%2C%22mbox%22%3A%5B%22mailto%3ASmith.Steve%40xyz.com%22%5D%7D&
\LogFiles\W3SVC1\u_ex12.log:32:2015-06-08 02:04:13 &actor=%7B%22name%22%3A%5B%22Brown%2C%20Bob%22%5D%2C%22mbox%22%3A%5B%22mailto%3ABrown.Bob%40xyz.com%22%5D%7D&
\LogFiles\W3SVC1\u_ex12.log:32:2014-08-02 05:50:37 &actor=%7B%22name%22%3A%5B%22Franklin%2C%20Francis%22%5D%2C%22mbox%22%3A%5B%22mailto%3AFranklin.Francis%40xyz.com%22%5D%7D&
sfgbadfbdfbadfbdab
junk .........
\LogFiles\W3SVC1\u_ex12.log:32:2014-08-02 05:50:37 &actor=%7B%22name%22%3A%5B%22Smith%2C%20Joe%22%5D%2C%22mbox%22%3A%5B%22mailto%3ASmith.Joe%40xyz.com%22%5D%7D&
\LogFiles\W3SVC1\u_ex12.log:32:2014-08-02 05:50:37 &actor=%7B%22name%22%3A%5B%22Doe%2C%20Jane%22%5D%2C%22mbox%22%3A%5B%22mailto%3ADoe.Jane%40xyz.com%22%5D%7D&
";
Regex RxLog = new Regex(@"(?m)(?:^\S+:(?<Date>\d+-\d+-\d+)\s(?:(?!&actor=).)+&actor=(?:%[0-9a-fA-F]2)*name(?:%[0-9a-fA-F]2)*(?<LastName>(?:(?!%[0-9a-fA-F]2|mbox).)+)(?:%[0-9a-fA-F]2)+(?<FirstName>(?:(?!%[0-9a-fA-F]2|mbox).)*)(?:%[0-9a-fA-F]2)*mbox(?:%[0-9a-fA-F]2)+mailto(?:%[0-9a-fA-F]2)+(?<MailUser>(?:(?!%[0-9a-fA-F]2).)+)(?:%[0-9a-fA-F]2)+(?<MailDomain>(?:(?!%[0-9a-fA-F]2).)+)(?:%[0-9a-fA-F]2)+&\s*|(?:.*\s))+");
Match logMatch = RxLog.Match(log);
if (logMatch.Success)
CaptureCollection ccDate = logMatch.Groups["Date"].Captures;
CaptureCollection ccLname = logMatch.Groups["LastName"].Captures;
CaptureCollection ccFname = logMatch.Groups["FirstName"].Captures;
CaptureCollection ccUser = logMatch.Groups["MailUser"].Captures;
CaptureCollection ccDomain = logMatch.Groups["MailDomain"].Captures;
for (int i = 0; i < ccDate.Count; i++)
Console.WriteLine("0 1, 2 3@4", ccDate[i].Value, ccLname[i].Value, ccFname[i].Value, ccUser[i].Value, ccDomain[i].Value );
输出:
2015-01-04 Smith, Steve Smith.Steve@xyz.com
2015-06-08 Brown, Bob Brown.Bob@xyz.com
2014-08-02 Franklin, Francis Franklin.Francis@xyz.com
2014-08-02 Smith, Joe Smith.Joe@xyz.com
2014-08-02 Doe, Jane Doe.Jane@xyz.com
【讨论】:
【参考方案2】:你可以做的是将行分成几个部分,然后解码 url 部分,获取 actor 参数,将其反序列化为 Actor
并使用它的属性。一个简单的例子是:
string txt = @"\LogFiles\W3SVC1\u_ex12.log:32:2014-08-02 05:50:37 &actor=%7B%22name%22%3A%5B%22Franklin%2C%20Francis%22%5D%2C%22mbox%22%3A%5B%22mailto%3AFranklin.Francis%40xyz.com%22%5D%7D&";
var parts = txt.Split(' ');
var urlParams = HttpUtility.UrlDecode(parts[2]);
string actorJson = HttpUtility.ParseQueryString(urlParams).Get("actor");
Actor actor = JsonConvert.DeserializeObject<Actor>(actorJson);
Console.WriteLine(actor.Name + " " + actor.EmailAddress);
您需要添加对 System.Web
和 Json.Net
的引用才能使其正常工作,当然还需要为您的 Actor 类添加一个定义,例如:
namespace MyNamespace
public class Actor
public string[] name get; set;
public string[] mbox get; set;
public string Name get return name[0];
public string EmailAddress get return mbox[0].Replace("mailto:", "");
现在您只需获取 File 类的所有行并循环遍历它们中的每一行,然后将所有反序列化的 actor 放入一个 List 或类似的东西中。
【讨论】:
一行一行调试一下就知道了。它只需要日志行的 url 参数部分,然后解码 url(删除所有 % 字符等),然后选择 actor 参数值(即 json),将 json 值反序列化为 Actor 类的实例就是这样。不需要正则表达式。 愚蠢的问题,但我在控制台应用程序中使用 .net 4.6 并且无法访问 System.Web。你遇到过这个问题吗? @JamesBlond 如上所述,您需要添加对System.Web
的引用,然后在文件顶部放置一个using System.Web;
,并将NuGet 包Json.NET
添加到您的项目中并使用for它也是。
我进入了参考 > 添加参考,选择了 System.Web,但它从未出现在列表中。我正在使用VS2015。我会在 2013 年尝试。谢谢!
我昨天在 VS2015 中做到了,没有问题。但它可能是 .net 4.52,而不是 4.6,我不记得了。以上是关于使用 c# 和正则表达式解析日志文件的主要内容,如果未能解决你的问题,请参考以下文章