为什么对二进制数据使用词法分析器/解析器如此错误?


13

我经常使用lexer / parsers而不是解析器组合器,并且看到从未参加过解析类的人问起解析二进制数据的问题。通常,数据不仅是二进制的,而且是上下文相关的。这基本上导致只有一种令牌,即字节令牌。

有人可以解释为什么对于没有参加语法课程但有理论基础的CS学生来说,用词法分析器/语法分析器解析二进制数据如此错误并且足够清晰。


我的猜测是,词法分析器可能找不到小于字节/字的令牌。如果需要,Erlang对解析二进制文件提供了出色的支持:user.it.uu.se/~pergu/papers/JFP_06.pdf
Dave Clarke

3
我认为您的假设不正确。显然,非上下文无关的数据会带来问题(通常可以绕开),但是您可以为二进制单词提供语法。您可能将无法使用流行的解析器生成器,因为这些生成器假定为文本输入。不过,这是另一个问题。
拉斐尔

@GuyCoder:对于文法许多经典的例子使用二进制字母,如S0S10S
拉斐尔

1
28

5
@GuyCoder:另一个程序生成的所有数据都可以用语法描述。但是,它可能不是上下文无关的。
拉斐尔

Answers:


10

原则上,没有错。

在实践中,

  • 我知道的大多数非文本数据格式都不是上下文无关的,因此不适合常见的解析器生成器。最常见的原因是它们具有长度字段,该字段给出了必须存在生产的次数。

    显然,拥有非上下文无关的语言永远不会阻止解析器生成器的使用:我们解析该语言的超集,然后使用语义规则将其简化为我们想要的。如果结果是确定性的,则该方法可用于非文本格式。问题在于,由于大多数二进制格式都允许嵌入任意数据,因此需要找到除计数之外的其他东西来进行同步。长度字段告诉您它是多少。

    然后,您可以开始玩一些技巧,例如使一个手动编写的词法分析器能够通过解析器的反馈来处理该问题(例如,C的lex / yacc处理使用这种技巧来处理typedef)。但是接下来我们要讲第二点。

  • 大多数非文本数据格式都非常简单(即使它们不是上下文无关的)。当忽略上述计数时,语言是常规的,最糟糕的是LL1,因此非常适合于手动解析技术。对于手动解析技术(例如递归下降),处理计数很容易。


“语言是常规的”如果我将“但也与上下文相关”的含义用于表示二进制数据是一种语法,那么我将在答案中进行澄清。这确实是部分问题;人们一旦想到您便会想到语法或常规语言。何况解析器。
盖伊编码器

7

让我们将数据分为三类:人类可读的数据(通常是文本,从书籍到程序的不同),打算由计算机读取的数据和其他数据(解析图像或声音)。

对于第一类,我们需要将它们处理为计算机可以使用的东西。由于解析器通常可以很好地捕获人类使用的语言,因此我们通常使用解析器。

第三类数据的示例是您要解析为文本的一本书的页面的扫描图像。对于此类,您几乎总是需要有关输入的非常具体的知识,因此您需要一个特定的程序来对其进行解析。标准的解析技术不会带您到这里。

您的问题是关于第二类的:如果我们拥有二进制数据,那么它几乎总是一个计算机程序的产品,该数据将用于另一个计算机程序。这也立即意味着负责创建数据的程序会选择数据的格式。

计算机程序几乎总是以清晰的格式产生数据。如果我们解析某些输入,那么我们实际上是在试图弄清输入的结构。对于二进制数据,此结构通常非常简单,并且易于计算机解析。

换句话说,弄清楚您已经知道输入结构的输入的结构通常会很浪费。由于解析不是免费的(需要时间,并且会增加程序的复杂性),所以这就是为什么对二进制数据使用词法分析器/解析器“太错了”的原因。


2
这是一个很好的观点,但我觉得它不能回答问题。
拉斐尔

LANGSEC: Language-theoretic Security提供了一个有趣的观点。其中一篇文章讨论了“怪异的机器”:形成系统输入处理功能的已知格式的临时解析器。它们可能实际上未按预期工作。由于错误的假设,有缺陷的机器将在给定的特殊输入的情况下执行意外状态转换,从而执行不可能进行的计算。这将创建攻击向量。使用形式语法将产生可证明正确的算法。
Matheus Moreira

0

a+b×(cd)+e(+ a (* b (- c d)) e)a b c d - * + e +。常用的数学符号比Lisp(需要更多的括号,但免费获取可变的arities,因此需要较少的符号来表示使用大arities的表达式)或RPL(从不需要括号)的冗余度更高。这种冗余对于计算机几乎没有用,而在实际中(即数据中可能存在错误的地方),通常将纠错逻辑与数据的功能含义分开,例如使用适用于任意数据的纠错码。字节序列,无论它们代表什么。

二进制格式通常设计为紧凑的,这意味着很少的简单语言功能(例如,可以由上下文无关的语法表示的平衡括号)。此外,将数据的二进制表示形式规范化(即每个对象具有单个表示形式)通常很有用。这排除了有时多余的功能,例如括号。具有较少冗余的另一个不那么值得推荐的结果是,如果每个输入在语法上都是正确的,则可以节省错误检查。

反对非平凡的二进制数据解析器的另一个因素是,许多二进制格式被设计为可以通过低级代码进行解析,这些低级代码喜欢在恒定内存中以很少的开销进行操作。当适用于允许元素的任意重复时,首选固定大小。一种格式,例如TLV,它允许从左到右的解析器首先为对象分配正确的内存量,然后读取对象的表示形式。从左到右进行解析是一个优势,因为它允许在不使用中间缓冲区的情况下对数据进行处理。


前两段的意义是什么?即使没有冗余,也需要解析器。同样,第一段是错误的:有些例子中所有单词都被允许,但您可以解析以获得结构(例如,RNA的二级结构预测)。
拉斐尔

@Raphael非平凡的解析器通常意味着冗余(是的,正如您所指出的,有一些例外)。我没有考虑过既不是为人类也不是为计算机设计的语言,这是一个有趣的例子。前两段讨论了二进制格式和人类可读格式之间的典型差异(典型的含义是,如果您要查找异常,则会找到它们)。
吉尔(Gilles)'所以
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.