=========================================================================== 语法格式描述规范 =========================================================================== “语法说明书”的说明书:BNF、EBNF和ABNF到底是什么? =========================================================================== 我们学习各种语言或者配置的时候,经常会看到一些 BNF、EBNF 之类的语法格式描述规范,虽然我们只是想了解某个文件格式、或者学习编程语言是怎么被"教"给电脑的,但是不啃这块骨头可能没法尝尝里面骨髓的味道。这块的资料大部分都写的比较晦涩难懂,满屏的"范式"、"终结符"、"产生式"……会有点头大。 别担心,请把这份文档想象成一份关于 **"如何写一本给机器看的语法说明书"** 的说明书。我们要聊的 BNF、EBNF、ABNF,就是三种写这种说明书的主流"写法"或"格式"。 就像您可以用中文、英文或图示来写一份家具组装手册一样,BNF、EBNF 和 ABNF 也是写"计算机语言组装手册"的不同"语言",它们各有特点,用在不同的地方。 为什么电脑需要一本"语法说明书"? --------------------------------- 想象一下,您发明了一种新的棋类游戏,需要教给全世界的人。您肯定不会只说"车马炮随便走",对吧?您需要一本详尽的规则书: - **棋盘什么样?** (格子、线条) - **棋子有哪些?** (车、马、炮这些基本单位) - **每个棋子怎么走?** (马走日,象飞田) - **怎么算赢?** (将死对方) **电脑理解一种新的"语言"(无论是编程语言,还是 JSON/YAML 这种数据格式,或者是 HTTP 网络协议),和人类学习新游戏规则,本质上是一回事。** 它需要一本极度精确、毫无歧义的"规则书",告诉它: 1. 这种语言里, **最基本的、不可分割的积木块** 是什么?(比如数字 `0-9` ,字母 `a-z` ,符号 `+` `=`) 2. 这些积木块, **如何能像搭乐高一样,组合成有意义的句子** ?(比如 `数字 + 数字` 可以组成一个 `加法表达式`) **BNF、EBNF、ABNF,就是用来写这种"乐高搭建规则书"的三种主流"写作规范"。** 它们的核心目标都是: **用一套标准、无歧义的话,描述出一种语言所有合法句子的长相。** 现在,让我们一个个来认识它们。 第一种写法:BNF(巴科斯范式)—— "最基础的乐高搭建手册" -------------------------------------------------------- **一句话比喻:BNF 就像一本用最基础词汇写成的、事无巨细的乐高说明书。每个步骤都写得清清楚楚,但读起来可能有点啰嗦。** **它的由来:** 早在 1959 年,两位计算机科学家(约翰·巴科斯和彼得·诺尔)为了描述一种叫 ALGOL 60 的新编程语言,发明了 BNF。它的出现,第一次让"语法"这个东西能从模糊的人类语言,变成精确的数学公式。 BNF 怎么写?—— 认识四个核心"零件" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 写 BNF 说明书,只用四种"零件": 1. **终结符:就是"最小的积木块"** - **是什么:** 语言里 **不能再拆分** 的基本单位。它们是最终出现在代码或数据里的实际字符。 - **怎么表示:** 通常用 **双引号** 包起来。 - **例子:** - `"if"` (一个关键字) - `"+"` (一个加号) - `"0"` (一个数字字符) - **通俗理解:** 就像乐高套装里,一个 **单独的、最小颗粒的蓝色 2x4 积木块**。你不能再把它切成更小的乐高了。 2. **非终结符:就是"半成品组件"或"分类标签"** - **是什么:** 它 **不是** 最终出现在代码里的东西,而是一个 **中间概念** ,用来指代 **一组特定的积木组合方式** 。它需要被进一步"展开"或"解释"。 - **怎么表示:** 用 **尖括号** `< >` 包起来。 - **例子:** - `<数字>` (代表所有合法的数字) - `<加法表达式>` (代表所有合法的加法式子) - **通俗理解:** 就像乐高说明书里,画着一个用很多小积木拼好的 **"车轮组件"** 的图标。这个图标本身不是积木,但它代表了"一堆按特定方式拼好的积木"。或者像一个菜谱里的 **"步骤 A:制作面糊"** ,它本身不是食材,而是一系列操作的集合。 3. **产生式(规则):就是"组装步骤"** - **是什么:** **定义** 一个非终结符(半成品) **具体是怎么由其他零件(终结符或其他非终结符)组成的** 。这是一条核心的"组装指令"。 - **怎么表示:** 用 `::=` 符号(可以读作"定义为")。左边是非终结符,右边是它的组成方式。 - **例子:** `<数字> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"` - 这句话的意思是:一个 `<数字>` 可以是 `"0"` ,或者 `"1"` ,或者 `"2"` …… 一直到 `"9"` 中的任意一个。 - **通俗理解:** 就是乐高说明书里具体的一步:"步骤 3:把‘车轮组件’(非终结符)安装到‘车轴’(终结符)上。" 4. **选择符:就是"或者"** - **是什么:** 一个竖线 `|` ,表示"或者"。 - **例子:** 上面的 `"0" | "1" | ... | "9"` 就用到了。意思是"可以是 0,或者 1,或者...,或者 9"。 - **通俗理解:** 就像菜谱说"可用白糖或蜂蜜",二选一。 看一个完整的 BNF 例子:教电脑理解"整数加法" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 假设我们想定义一种只能做一位数整数加法的超简单语言,比如 `3+5` 。 用 BNF 可以这样写这本"说明书": :: <表达式> ::= <数字> | <表达式> "+" <数字> <数字> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" **我们来"读"一下这本说明书:** - **第一行规则 (`<表达式>`):** - 它说,一个 `<表达式>` 可以是两种东西之一( `|` ): 1. 单纯的一个 `<数字>` 。 比如 `3` 本身也是一个合法的表达式。 2. 一个 `<表达式>` 后面跟着一个加号 `"+"` ,再跟着一个 `<数字>` 。 这听起来有点绕,它其实是一种 **"递归"** 的定义。它允许我们把简单的表达式组合成更复杂的。 - 举例:如果 `3` 是一个 `<表达式>` (根据第 1 种情况),那么 `3 + 5` 就符合" `<表达式> "+" <数字>` "这个模式,所以 `3+5` 也是一个新的 `<表达式>` 。 - 更进一步, `3+5+2` 也可以,因为它可以看作是 `(3+5)` 这个 `<表达式>` 后面加上 `+ 2` 。 - **第二行规则 (`<数字>`):** - 这很简单,定义了什么是 `<数字>` :就是 0 到 9 这十个字符中的任意一个。 **BNF 的特点总结:** - **优点:** 极其严谨、基础,像数学定义,是另外两种范式的"老祖宗"。 - **缺点:** 写起来 **非常啰嗦** 。对于"可选"或"重复"的情况,必须引入新的非终结符和递归规则,让说明书变得很长,不易读。 - 比如,想表达"这里可以有一个空格,也可以没有",在 BNF 里就要多写一条规则,很麻烦。 --- 第二种写法:EBNF(扩展巴科斯范式)—— "带便利贴和快捷符号的升级版手册" ---------------------------------------------------------------------- **一句话比喻:EBNF 就像一本现代化的乐高说明书,增加了"重复此步骤 3 次"、"此零件可选"等快捷图标,让手册更薄、更易读。** **它的由来:** 因为 BNF 太啰嗦了,后来的人们在它的基础上,增加了一些方便的"快捷写法",形成了 EBNF。它更像是一种工程上的改良,为了让语法说明书对人类读者更友好。 EBNF 多了哪些"快捷图标"? ~~~~~~~~~~~~~~~~~~~~~~~~~ EBNF 保留了 BNF 的核心,但增加了三个超级有用的符号: 1. **可选符号 `[ ... ]` :表示"这个东西可以有,也可以没有"。** - **例子:** `["-"] <数字>` 表示一个数字前面可以有一个可选的负号。这可以描述 `5` 和 `-5` 。 - **BNF 的啰嗦写法:** 需要写成 `<有符号数字> ::= <数字> | "-" <数字>` ,多了一个非终结符。 2. **重复符号 `{ ... }` :表示"这个东西可以重复出现很多次,或者一次也不出现"。** - **例子:** `<多位数> ::= <数字> {<数字>}` 表示一个多位数,先有一个数字,后面可以跟着零个或多个额外的数字。这就能描述 `7` , `42` , `10086` 了。 - **BNF 的啰嗦写法:** 必须用递归: `<多位数> ::= <数字> | <多位数> <数字>` ,理解起来没那么直观。 3. **分组符号 `( ... )` :用来明确分组,常和选择符 `|` 一起用。** - **例子:** `("+" | "-") <数字>` 表示先是一个加号或减号,然后跟一个数字。括号明确了选择的范围。 - 有时也会用 `=` 代替 `::=` ,用 `;` 表示一条规则结束,看起来更清爽。 看一个完整的 EBNF 例子:描述一个简单的变量赋值语句 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 这次我们描述一个像 `age = 25;` 这样的语句。 :: (* 一个简单的赋值语句 *) 程序 = { 赋值语句 } ; 赋值语句 = 标识符 "=" 表达式 ";" ; 表达式 = 项 { ("+" | "-") 项 } ; 项 = 因子 { ("*" | "/") 因子 } ; 因子 = 数字 | 标识符 | "(" 表达式 ")" ; 标识符 = 字母 { 字母 | 数字 } ; 数字 = 数字 { 数字 } ; 字母 = "A" | "B" | ... | "Z" | "a" | "b" | ... | "z" ; 数字 = "0" | "1" | ... | "9" ; **我们来解读一下:** - **`{ 赋值语句 }`** :表示程序由 0 个或多个赋值语句组成。 - **`表达式` 的定义** :它说一个表达式首先是一个 `项` ,然后后面可以跟零个或多个 **(一个加号或减号,再加上另一个 `项` )** 。这完美地描述了 `1 + 2 - 3` 这样的运算顺序。 - **`标识符` 的定义** :它说标识符必须以一个 `字母` 开头,后面可以跟零个或多个 `字母` 或 `数字` 。这描述了像 `age` , `total_sum` , `x1` 这样的变量名。 看,用上了 `{ }` 和 `( | )` 之后,是不是比纯 BNF 清晰紧凑多了? **EBNF 的特点总结:** - **优点:** **极大地提升了可读性和简洁性** 。现在绝大多数编程语言(如 Python、C++)的官方语法文档,都采用 EBNF 或类似它的格式来书写。 - **缺点:** 它有很多"方言",不同地方用的具体符号可能有点小差异(比如注释用 `(* *)` 还是 `/* */` )。但核心思想一致。 --- 第三种写法:ABNF(增强巴科斯范式)—— "为网络电报定制的精准密码本" ------------------------------------------------------------------ **一句话比喻:ABNF 就像一份为发送电报或设计密码本而写的超级精确的指令集,特别关心"大小写"、"空格"、"二进制数"这些通信细节。** **它的由来:** BNF 和 EBNF 更多用于描述编程语言。而当我们需要定义 **互联网协议** (比如网页用的 HTTP、发邮件用的 SMTP、网址 URL)时,情况更特殊。这些协议需要在网络上精确传输,对大小写、空格、回车换行、甚至是二进制数据都有严格规定。IETF(互联网标准组织)就基于 BNF,量身打造了 ABNF。 ABNF 的独特之处在哪里? ~~~~~~~~~~~~~~~~~~~~~~~~ ABNF 为网络世界做了很多"增强": 1. **核心规则库:** 它预定义了一组"核心积木",就像给了你一盒标好名字的标准零件: - `ALPHA` = 任何字母 (A-Z, a-z) - `DIGIT` = 任何数字 (0-9) - `SP` = 空格 - `CRLF` = 回车换行 (Windows 系统的行尾,网络协议常用) - `HEXDIG` = 十六进制数字 (0-9, A-F) 2. **精确的数值表示:** 可以直接用不同进制定义一个字符! - `%d65` 或 `%x41` :表示十进制 65(十六进制 41)对应的字符,也就是大写字母 `A` 。 - `%x30-39` :表示十六进制 30 到 39 的所有字符,即数字 `0-9` 。 - 这保证了协议在任何电脑上解析的结果都一模一样。 3. **灵活的重度计数:** 可以精确指定一个东西重复多少次。 - `*` : 零次或多次(和 EBNF 的 `{ }` 一样)。 - `m*n` : 至少 `m` 次,至多 `n` 次。例如 `1*5DIGIT` 表示 1 到 5 位数字。 - `n` : 刚好 `n` 次。例如 `3DIGIT` 表示必须是 3 位数字。 4. **大小写不敏感:** 默认情况下,ABNF 认为 `A` 和 `a` 是等价的。这很适合网络协议(网址、域名通常不区分大小写)。如果想区分,需要用 `%x` 明确指定。 5. **不同的选择符:** 它用斜杠 `/` 来表示"或者",而不是竖线 `|` 。 看一个 ABNF 实战例子:一个 HTTP 网址的片段 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HTTP 协议就是用 ABNF 定义的。我们来看一个简化版的"网址路径"定义: :: ; 一个路径由多个路径段组成,以斜杠开头 路径 = "/" [ 路径段 *( "/" 路径段 ) ] 路径段 = *PCHAR ; PCHAR代表路径中允许的字符集合 PCHAR = 字母 / 数字 / "-" / "." / "_" / "~" / ":" / "@" **解读一下:** - **`[ 路径段 *( "/" 路径段 ) ]`** : - 最外层的 `[ ]` 表示整个这一块是 **可选** 的(所以路径可以只是一个单独的 `/` )。 - 里面是:一个 `路径段` ,后面跟着零个或多个 **(一个 `/` 加一个 `路径段` )** 。 - 这完美描述了像 `/` 、 `/api` 、 `/api/users` 、 `/home/index.html` 这样的路径。 - **`*PCHAR`** : `*` 表示零个或多个 `PCHAR` 字符。所以一个路径段可以是空,也可以是多个允许字符的组合。 **ABNF 的特点总结:** - **优点:** **为通信协议而生,极度精确,无歧义** 。特别擅长处理二进制、字符编码、网络传输的细微差别。 - **缺点:** **对非专业人士最不友好** ,语法看起来比较怪异。但如果您是做网络编程或看 RFC 文档的,必须掌握它。 --- 三种"写法"大比拼:一张表看懂怎么选 ----------------------------------- .. csv-table:: BNF、EBNF、ABNF 快速选择指南 :header: "比较项", "BNF(基础版)", "EBNF(增强易读版)", "ABNF(网络精准版)" :widths: 15, 28, 28, 29 "核心目的", "奠定理论基础,最严谨的定义方式", "让语法描述对人类更友好、更紧凑", "为互联网协议提供精确无歧义的定义" "就好比", "用最基础单词写的详细法律条文", "带图标的现代化用户手册", "摩尔斯电码密码本或工程图纸" "主要用在哪", "计算机科学教科书,语言理论", "**“实际编程语言的官方文档”** (如Python参考手册),配置文件格式定义", "**“互联网协议标准”** (RFC文档,如HTTP, URL, 电子邮件格式)" "关键特征", "只有`::=`、`|`、`< >`、`\"" \""`这几个符号,描述重复/可选很啰嗦", "引入了 `[ ]`、`{ }`、`( )` 等快捷符号,描述能力更强、更简洁", "有`%x`等数值定义、核心规则(如`DIGIT`)、精确重复计数(`m*n`),默认大小写不敏感" "您最可能在哪遇到", "学习编译原理时", "**“查看某编程语言或数据格式(如XML)的语法规则时”**", "**“阅读网络协议(如HTTP请求格式)的规范时”**" "给非技术朋友的建议", "知道它是老祖宗,了解基本概念即可", "**“这是最有用的!如果想看懂语法规则,重点学这个。”**", "除非接触网络协议开发,否则仅作了解" 总结与行动建议 -------------- 1. **它们不是三门语言,而是一门手艺的三种工具:** BNF、EBNF、ABNF 都是在做同一件事—— **形式化地描述语法** 。就像斧头、锯子、电钻都是木工工具一样,选哪个取决于你要做什么活儿。 2. **您的学习路径应该是:** - **第 1 步:理解核心思想。** 理解"终结符/非终结符/产生式"这个核心比喻(积木块/半成品/组装说明)。这是看懂一切语法描述的基础。 - **第 2 步:重点掌握 EBNF。** 因为它在实际技术文档(语言手册、格式标准)中出现频率最高,而且它的快捷符号( `[ ]` 、 `{ }` )非常直观,学会了就能看懂大部分语法图。 - **第 3 步:按需了解 ABNF。** 如果您的工作涉及到网络编程、协议分析,或者需要阅读 IETF 的 RFC 文档,那么 ABNF 就是必选项。 - **第 4 步:回顾 BNF。** 如果您对计算机科学理论感兴趣,可以回头看看 BNF,理解一切简洁的背后,最初是如何用严谨但繁琐的方式构建起来的。 3. **一个简单的测试:** 下次当您在任何技术文档里看到类似 `Syntax: name = value [; comment]` 这样的描述时,您就能反应过来,这用的是类似 EBNF 的风格: `name = value` 是必须的,而 `[; comment]` 这个分号和注释是可选的。 希望这份超长的"说明书"能帮您驱散对这些术语的迷雾。记住,它们只是工具,目的是为了让机器(和人类)更准确地理解规则。您不需要成为制造工具的大师,只需要学会识别和使用它们,就能在技术的世界里更从容地阅读那些重要的"规则书"了。