使用C#字典解析日志文件

本文关键字:日志 文件 字典 使用 | 更新日期: 2023-09-27 18:29:21

我正在尝试解析一个相当长的日志文件,并创建一个更好、更易于管理的问题列表。

我能够逐行读取和解析单个日志,但我需要做的是只显示唯一的条目,因为有些错误发生的频率比其他错误高,并且总是用相同的文本记录。

我要做的是创建一个Dictionary对象来保存每个唯一的条目,当我处理日志文件时,搜索Dictionary物体,看看是否已经有了相同的值。

这是我的一个粗略的代码示例(一项正在进行的工作,我希望我的语法都正确),但它不起作用。出于某种原因,此脚本从未看到任何不同的条目(若语句从未通过):

    string[] rowdta = new string[4];
    Dictionary<string[], int> dict = new Dictionary<string[], int>();
    int ctr = -1;
    if (linectr == 1)
        {
            ctr++;
            dict.Add(rowdta, ctr);
        }
        else
        {
            foreach (KeyValuePair<string[], int> pair in dict)
            {
                if ((pair.Key[1] != rowdta[1]) || (pair.Key[2] != rowdta[2])| (pair.Key[3] != rowdta[3]))
                {
                    ctr++;
                    dict.Add(rowdta, ctr);
                }
            }
        }

一些示例数据:一线

    rowdta[0]="ErrorType";
    rowdta[1]="Undefined offset: 0";
    rowdta[2]="/url/routesDisplay2.svc.php";
    rowdta[3]="Line Number 5";

二线

    rowdta[0]="ErrorType";
    rowdta[1]="Undefined offset: 0";
    rowdta[2]="/url/routesDisplay2.svc.php";
    rowdta[3]="Line Number 5";

第三行

    rowdta[0]="ErrorType";
    rowdta[1]="Undefined variable: fvmsg";
    rowdta[2]="/url/processes.svc.php";
    rowdta[3]="Line Number 787";

因此,有了这个,字典将有两个项目,第一行和第三行。

我也尝试过以下内容,nalso在日志文件文本中没有发现任何变化。

    if (!dict.ContainsKey(rowdta)) {}

有人能帮我把这个语法弄对吗?我只是C#的一个新手,但这应该相对简单。和往常一样,我认为这应该是足够的信息来开始对话。如果你想要/需要更多的细节,请告诉我。

使用C#字典解析日志文件

为实现IEquatable的字符串创建一个包装器。

public class LogFileEntry :IEquatable<LogFileEntry>
{
    private readonly string[] _rows;
    public LogFileEntry(string[] rows)
    {
        _rows = rows;
    }
    public override int GetHashCode()
    {
        return 
            _rows[0].GetHashCode() << 3 | 
            _rows[2].GetHashCode() << 2 | 
            _rows[1].GetHashCode() << 1 | 
            _rows[0].GetHashCode();
    }
    #region Implementation of IEquatable<LogFileEntry>
    public override bool Equals(Object obj)
    {
        if (obj == null) 
            return base.Equals(obj);
        return Equals(obj as LogFileEntry);  
    } 
    public bool Equals(LogFileEntry other)
    {
        if(other == null) 
            return false;
        return _rows.SequenceEqual(other._rows);
    }
    #endregion
}

然后在你的字典里用这个:

var d = new Dictionary<LogFileEntry, int>();
var entry = new LogFileEntry(rows);
if( d.ContainsKey(entry) )
{
    d[entry] ++;
} 
else
{
    d[entry] = 1;
}

或者创建一个类似于@dasblinkenlight提出的自定义比较器,并使用以下

public class LogFileEntry 
{
}
public class LogFileEntryComparer : IEqualityComparer<LogFileEntry>{ ... }
var d = new Dictionary<LogFileEntry, int>(new LogFileEntryComparer());
var entry = new LogFileEntry(rows);
if( d.ContainsKey(entry) )
{
    d[entry] ++;
} 
else
{
    d[entry] = 1;
}

您看到问题的原因是,如果不提供自定义IEqualityComparer<string[]>或编写包装,字符串数组就不能用作字典中的键。

EDIT以下是自定义比较器的快速而肮脏的实现:

private class ArrayEq<T> : IEqualityComparer<T[]> {
    public bool Equals(T[] x, T[] y) {
        return x.SequenceEqual(y);
    }
    public int GetHashCode(T[] obj) {
        return obj.Sum(o => o.GetHashCode());
    }
}

以下是如何使用它:

var dd = new Dictionary<string[], int>(new ArrayEq<string>());
dd[new[] { "a", "b" }] = 0;
dd[new[] { "a", "b" }]++;
dd[new[] { "a", "b" }]++;
Console.WriteLine(dd[new[] { "a", "b" }]);

问题是数组相等就是引用相等。换句话说,它不取决于存储在数组中的值,它只取决于数组的标识。

的一些解决方案

  • 使用Tuple保存行数据
  • 使用匿名类型保存行数据
  • 创建一个自定义类型来保存行数据,如果是类,则重写Equals和GetHashCode
  • 创建IEqualityComparer的自定义实现,根据数组的值对其进行比较,并在创建时将其传递给字典