寻找反转sprintf()函数输出的算法

时间:2008-08-13 14:25:58

标签: algorithm parsing logging printf normalizing

我正在开发一个需要解析日志文件的项目。我正在寻找一种快速算法,可以采用这样的群组消息:

  

P1的温度为35°F。

     

P1的温度为40°F。

     

P3的温度为35F。

     

伐木工停了。

     

记录器已启动。

     

P1的温度为40°F。

并以printf()的形式输出:

"The temperature at P%d is %dF.", Int1, Int2" 
{(1,35), (1, 40), (3, 35), (1,40)}

该算法必须足够通用,才能识别消息组中的几乎所有数据负载。

我尝试搜索这种技术,但我甚至不知道要搜索的正确术语。

10 个答案:

答案 0 :(得分:12)

我认为你可能会忽视并错过了fscanf()和sscanf()。这与fprintf()和sprintf()相反。

答案 1 :(得分:6)

概述:

naïve!! 算法以每列方式跟踪单词的频率,其中可以假设每行可以用分隔符分隔成列。

示例输入:

  

狗跳过月亮   猫跳过月亮   月亮跳过月亮   汽车跳过月球

频率:

Column 1: {The: 4}
Column 2: {car: 1, cat: 1, dog: 1, moon: 1}
Column 3: {jumped: 4}
Column 4: {over: 4}
Column 5: {the: 4}
Column 6: {moon: 4}

我们可以通过基于字段总数进行分组来进一步划分这些频率列表,但在这个简单方便的示例中,我们只使用固定数量的字段(6)。

下一步是迭代生成这些频率列表的行,让我们来看第一个例子。

  1. :符合一些手动波浪标准,算法决定它必须是静态的。
  2. dog :基于频率列表的其余部分似乎不是静态的,因此它必须是动态的而不是静态文本。我们遍历一些预定义的正则表达式并提出/[a-z]+/i
  3. over :与#1相同的交易;它是静态的,所以保持原样。
  4. :与#1相同的交易;它是静态的,所以保持原样。
  5. 月亮:与#1相同的交易;它是静态的,所以保持原样。
  6. 因此,只需从第一行开始,我们就可以将以下正则表达式组合在一起:

    /The ([a-z]+?) jumps over the moon/
    

    考虑:

    • 显然,只要有人确信频率列表是对整个数据的充分抽样,就可以选择扫描第一遍的部分或整个文档。

    • 误报可能会蔓延到结果中,它将取决于过滤算法(挥手),以提供静态和动态字段之间的最佳阈值,或者某些人类后期处理。

    • 总体思路可能很好,但实际实施肯定会影响该算法的速度和效率。

答案 2 :(得分:3)

感谢所有伟大的建议。 克里斯,是对的。我正在寻找一种通用的解决方案来规范化任何类型的文本。该问题的解决方案归结为动态地在两个或更多相似的字符串中找到模式。 几乎就像预测集合中的下一个元素一样,基于前两个:

1:珠穆朗玛峰高达30000英尺

2:K2高28000英尺

=>模式是什么? =>回答:

[name]是[数字]英尺高

现在,文本文件可以包含数百万行和数千种模式。我想非常非常快速地解析文件,找到模式并收集与每个模式相关的数据集。

我考虑过创建一些高级语义哈希来表示消息字符串中的模式。 我会使用一个标记器,给每个标记类型一个特定的“权重”。 然后我会对哈希进行分组并评估它们的相似性。分组完成后,我会收集数据集。

我希望,我不需要重新发明轮子,并且可以重复使用已有的东西。

克劳斯

答案 3 :(得分:2)

这取决于你想要做什么,如果你的目标是快速生成sprintf()输入,这是有效的。如果你试图解析数据,也许正则表达式也会这样做..

答案 4 :(得分:1)

你不会找到一个可以简单地接受任意输入的工具,从中猜出你想要的数据,并产生你想要的输出。这对我来说听起来像是强大的AI。

制作这样的东西,即使只是为了识别数字,也会变得非常毛茸茸。例如“123.456”一个或两个?这个“123,456”怎么样? “35F”是十进制数和“F”还是十六进制值0x35F?你将不得不构建一些能够以你需要的方式解析的东西。您可以使用正则表达式执行此操作,或者可以使用sscanf执行此操作,或者您可以通过其他方式执行此操作,但您必须编写自定义的内容。

但是,使用基本的正则表达式,您可以自己完成。它不会是魔术,但它不是那么多工作。这样的东西会解析你感兴趣的行并合并它们(Perl):

my @vals = ();
while (defined(my $line = <>))
{
    if ($line =~ /The temperature at P(\d*) is (\d*)F./)
    {
        push(@vals, "($1,$2)");
    }
}
print "The temperature at P%d is %dF. {";
for (my $i = 0; $i < @vals; $i++)
{
    print $vals[$i];
    if ($i < @vals - 1)
    {
        print ",";
    }
}
print "}\n";

此输出是

The temperature at P%d is %dF. {(1,35),(1,40),(3,35),(1,40)}

您可以为需要解析的每种类型的行执行类似的操作。您甚至可以从文件中读取这些正则表达式,而不是自己编写每个正则表达式。

答案 5 :(得分:1)

我不知道有任何具体工具可以做到这一点。我遇到类似问题时所做的就是尝试猜测正则表达式来匹配线条。

然后我处理了文件并只显示了不匹配的行。如果一行不匹配,则意味着该模式是错误的,应该进行调整或添加其他模式。

经过大约一个小时的工作,我成功地找到了大约20个模式来匹配10000多行。

在您的情况下,您可以先“猜测”一个模式是"The temperature at P[1-3] is [0-9]{2}F."。如果您重新处理文件删除任何匹配的行,它将保留“仅”:

  

记录器已停止。

     

记录器已启动。

然后您可以与"Logger (.+)."匹配。

然后,您可以优化模式并找到新模式以匹配整个日志。

答案 6 :(得分:0)

@John:我认为这个问题涉及一种实际识别日志文件中的模式并自动“猜测”适当的格式字符串和数据的算法。 *scanf家庭不能单独做到这一点,只有在首先识别出模式后才会有所帮助。

答案 7 :(得分:0)

@Derek Park:嗯,即使是强大的AI也不能确定它有正确的答案。

也许可以使用类似压缩的机制:

  1. 查找大而频繁的子字符串
  2. 查找大型,频繁的子字符串模式。 (即[pattern:1] [junk] [pattern:2])
  3. 要考虑的另一个项目可能是按edit-distance对行进行分组。对类似行进行分组应该将问题拆分为每个模块一个模式块。

    实际上,如果你设法写这个,让全世界知道,我想我们很多人都会喜欢这个工具!

答案 8 :(得分:0)

@Anders

  

嗯,即使是强大的AI也不能确定它有正确的答案。

我认为足够强大的AI通常可以从上下文中找出正确的答案。例如强AI可以识别出在这种情况下“35F”是温度而不是十六进制数。肯定有强大的AI无法回答的情况。这些是人类无法回答的情况(假设非常强AI)。

当然,这并不重要,因为我们没有强大的人工智能。 :)

答案 9 :(得分:0)

http://www.logparser.com转发到似乎相当活跃的IIS论坛。这是Gabriele Giuseppini的“Log Parser Toolkit”的官方网站。虽然我从未真正使用过这个工具,但我确实从亚马逊商城购买了这本书的廉价副本 - 今天的副本价格低至16美元。没有什么可以通过页面翻转死树界面。

看看这个论坛,我以前没有听说过http://www.lizardl.com/的“MS Log Parser,Log Parser Lizard的新GUI工具”。

当然,关键问题是GRAMMAR的复杂性。要使用任何类型的日志解析器作为常用术语,您需要准确知道要扫描的内容,您可以为其编写BNF。很多年前,我参加了一个基于Aho-and-Ullman的“龙书”的课程,完全理解的LALR技术可以为你提供最佳速度,前提是你有CFG。

另一方面,看起来你可能会找到类似AI的东西,这完全是复杂的不同顺序。