我必须解析一个日志文件,并且不确定如何最好地利用每一行的不同部分。我面临的问题是原始开发人员使用':'来分隔令牌,这有点白痴,因为该行包含本身包含':'的时间戳!
示例行如下所示:
transaction_date_time:[systemid]:sending_system:receiving_system:data_length:data:[ws_name]
2019-05-08 15:03:13:494|2019-05-08 15:03:13:398:[192.168.1.2]:ABC:DEF:67:cd71f7d9a546ec2b32b,AACN90012001000012,OPNG:[WebService.SomeName.WebServiceModule::WebServiceName]
我没有问题,可以读取日志文件并访问每一行,但是不确定如何解析?
答案 0 :(得分:0)
使用正则表达式,我能够解析所有内容。数据似乎来自excel,因为秒的派系使用冒号而不是句点。 C#不喜欢冒号,所以我不得不用句号代替冒号。我还从右到左进行了解析,以解决结肠问题。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
namespace ConsoleApplication3
{
class Program1
{
const string FILENAME = @"c:\temp\test.txt";
static void Main(string[] args)
{
string line = "";
int rowCount = 0;
StreamReader reader = new StreamReader(FILENAME);
string pattern = @"^(?'time'.*):\[(?'systemid'[^\]]+)\]:(?'sending'[^:]+):(?'receiving'[^:]+):(?'length'[^:]+):(?'data'[^:]+):\[(?'ws_name'[^\]]+)\]";
while ((line = reader.ReadLine()) != null)
{
line = line.Trim();
if (line.Length > 0)
{
if (++rowCount != 1) //skip header row
{
Log_Data newRow = new Log_Data();
Log_Data.logData.Add(newRow);
Match match = Regex.Match(line, pattern, RegexOptions.RightToLeft);
newRow.ws_name = match.Groups["ws_name"].Value;
newRow.data = match.Groups["data"].Value;
newRow.length = int.Parse(match.Groups["length"].Value);
newRow.receiving_system = match.Groups["receiving"].Value;
newRow.sending_system = match.Groups["sending"].Value;
newRow.systemid = match.Groups["systemid"].Value;
//end data is first then start date is second
string[] date = match.Groups["time"].Value.Split(new char[] {'|'}).ToArray();
string replacePattern = @"(?'leader'.+):(?'trailer'\d+)";
string stringDate = Regex.Replace(date[1], replacePattern, "${leader}.${trailer}", RegexOptions.RightToLeft);
newRow.startDate = DateTime.Parse(stringDate);
stringDate = Regex.Replace(date[0], replacePattern, "${leader}.${trailer}", RegexOptions.RightToLeft);
newRow.endDate = DateTime.Parse(stringDate );
}
}
}
}
}
public class Log_Data
{
public static List<Log_Data> logData = new List<Log_Data>();
public DateTime startDate { get; set; } //transaction_date_time:[systemid]:sending_system:receiving_system:data_length:data:[ws_name]
public DateTime endDate { get; set; }
public string systemid { get; set; }
public string sending_system { get; set; }
public string receiving_system { get; set; }
public int length { get; set; }
public string data { get; set; }
public string ws_name { get; set; }
}
}
答案 1 :(得分:0)
由于输入字符串不是完全可分割的 ,由于定界符char也是内容的一部分,因此可以使用简单的regex表达式代替。
简单但可能足够快,即使使用默认设置也是如此。
输入字符串的不同部分可以通过以下捕获组分开:
string pattern = @"^(.*?)\|(.*?):\[(.*?)\]:(.*?):(.*?):(\d+):(.*?):\[(.*)\]$";
这将为您提供8组+ 1(Group[0]
),其中包含整个字符串。
使用Regex类,只需传递一个字符串即可解析(名称为line
,在此)并将正则表达式(名称为pattern
)传递给Match()方法,使用默认值设置:
var result = Regex.Match(line, pattern);
Groups.Value属性返回每个捕获组的结果。例如,两个日期:
var dateEnd = DateTime.ParseExact(result.Groups[1].Value, "yyyy-MM-dd hh:mm:ss:ttt", CultureInfo.InvariantCulture),
var dateStart = DateTime.ParseExact(result.Groups[2].Value, "yyyy-MM-dd hh:mm:ss:ttt", CultureInfo.InvariantCulture),
将IpAddress
提取为:\[(.*?)\]
。
您可以为此分组命名,这样可以更清楚地知道该值是指什么。只需添加前缀为?
并括在<>
或单引号'
中的字符串即可命名分组:
...\[(?<IpAddress>.*?)\]...
但是请注意,命名组会修改Regex.Groups
索引:未命名的组将首先插入,命名后的组将插入后面。因此,仅命名IpAddress
组将使其成为最后一项Groups[8]
。当然,您可以命名所有组,并且将保留索引。
var hostAddress = IPAddress.Parse(result.Groups["IpAddress"].Value);
此模式应允许 medium 机器每秒解析130,000~150,000
个字符串。
您必须对其进行测试才能找到 perfect 模式。例如,第一个匹配项(对应于第一个日期):(.*?)\|
,如果不贪心则更快(使用*?惰性量词)。最后一场比赛的对立面:\[(.*)\]
。 jdweng使用的模式比这里使用的模式还要快。
有关每个令牌的用法和含义的详细说明,请参见Regex101。