使用C#Dictionary解析日志文件

时间:2012-03-27 17:01:55

标签: c# dictionary

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

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

我要尝试做的是创建一个Dictionary对象来保存每个唯一条目,当我处理日志文件时,搜索Dictionary对象以查看是否已存在相同的值。

以下是我所拥有的代码的原始示例(正在进行中的工作,我希望我的所有语法都正确),但这些代码不起作用。由于某种原因,此脚本永远不会看到任何不同的条目(if语句永远不会通过):

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

第3行

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

因此,通过此,词典中将包含2个项目,第一行和第三行。

我也尝试过以下内容,nalso在日志文件文本中找不到任何变化。

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

有人可以帮我解决这个问题吗?我只是C#的新手,但这应该是相对简单的。和往常一样,我认为这应该是足够的信息来开始对话。如果您需要/需要更多细节,请告诉我。

3 个答案:

答案 0 :(得分:1)

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

编辑以下是自定义比较器的快速而又脏的实现:

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" }]);

答案 1 :(得分:1)

为字符串创建一个包装器,它实现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;
}

答案 2 :(得分:0)

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

一些解决方案

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