本文 "注:" 语法为译者批注 注: ABNF是各类RFC中常用的数据定义语法,比如RFC5322,RFC8259
因特网技术规范通常需要定义一种正式的语法, 并且可以自由地使用作者认为有用的任何符号。
这些年来,一个被称为扩充BNF(ABNF)的 巴科斯范式 (BNF) 修改版, 已在许多因特网规范中被广泛使用。
它平衡了紧凑性和简单性并具有合理的表现能力。在阿帕网 (Arpanet) 早期,每个规范都包含了各自的 ABNF 定义。
这包含了 email 规范,[RFC733] 以及后来的 [RFC822],都成为了定义 ABNF 的常见引用。
现在的文档将这些定义分隔开,允许选择性引用。可以预见的是,它还提供了一些修改和增强。
标准 BNF 和 ABNF 之间的区别涉及: 命名规则,重复 (repetition),选择 (alternative),顺序独立,和值范围。
附录 B 提供了对几种因特网规范常见的核心词汇分析器的规则定义和编码。
它提供了便利的方式并独立于文档正文中定义的元语言。
注: 元语言 元语言是指讨论或研究语言本身时所使用的语言或符号,比如使用中文学习英语,则中文为元语言 注 ARPANET 是美国国防高级研究计划局开发的世界上第一个运营的数据包交换网络,是全球互联网的鼻祖。
一个规则的名称就是很简单的名称本身,也就是一序列字符,以字母符号开始,后面接着的是任意字母,数字,和连字符('-')的组合。
注意:
规则名称是大小写不敏感的。
名称 <rulename>
, <Rulename>
, <RULENAME>
, 以及 <rUlENamE>
指的都是同一个规则。
与原始 BNF 不一样的是,尖括号 ("<", ">") 不是必要的.
可以使用尖括号以助于识别规则名.
这通常用于在自由形式的文档中使用的规则名称引用,或者用于区分部分没有空白符组成一个字符串,
比如在下面讨论的 重复规则 中所示。
规则是通过以下序列定义:
name = elements crlf
注:"CRLF"表示回车换行(ascii \r\n) 注: 终结符 它们不能被分解成更小的单位
<name>
是规则的名称,<elements>
是一个或多个规则名称或终结符 (terminal),<crlf>
是行结束指示符(回车后紧跟着换行)。
等于符号用于将规则的定义和名称分隔开。
elements
构成了一个或多个规则名(以及/或者)值定义的序列,并组合了本文档中定义的各种操作符,比如选择规则和重复规则。
出于视觉考虑,规则定义是左对齐的。当一个规则需要多行的时候,后续的行是缩进的。
左对齐和缩进与 ABNF 规则的首行相对,而没必要与文档的左边距相配。
规则被解析为终结值字符串,有时也称作字符。
在 ABNF 中,一个字符只是一个非负整数。
在特定环境中, 会指定特殊的值到字符集(比如ASCII编码)的映射(编码)
终结值是由一个或多个数字字符组成,并且这是字符的解释被明确指出.
当前定义类以下基本终结值:
b = binary
d = decimal
x = hexadecimal
注 数值字符可按下面的方式指定:先是一个百分号“%”,紧跟着基数,再其后是这个数值或数值串 注:(b表示二进制, d表示十进制, x表示十六进制)基数, 相当于Python语言中,二进制前会添加0b,十六进制前添加0x
因此:
CR = %d13
CR = %x0D
注: CR表示回车符号,在ascii编码中为13号,十六进制表示为0D
分别指定了 [US-ASCII] 中回车的十进制和十六进制的表示形式。
出于紧凑性考虑,值可以组成一个串联的字符串,使用点号 (".") 在值中指示出字符的分隔。
因此:
CRLF = %d13.10
注: CR = %d13 注: LF = %d10
ABNF 允许直接使用字面文本字符串,使用引号包围。因此:
command = "command string"
字面文本字符串可理解为一个串联的可打印的字符。
注意:
ABNF 是大小写不敏感的并且属于 US-ASCII 字符集
因此:
rulename = "abc"
以及:
rulename = "aBc"
会匹配 "abc", "Abc", "aBc", "abC", "ABc", "aBC", "AbC", 和"ABC"。
想要指定大小写敏感的规则,那么需要单独地指定每一个字符。
比如:
rulename = %d97 %d98 %d99
或者
rulename = %d97.98.99
注: ascii字符集中, a为97,而A为66
仅会匹配小写形式的abc。
终结值字符的外部表示将根据存储或传输环境中的约束而变化
因此,同样基于 ABNF 的语法可能有多种外部编码,比如一个用于 7-bit US-ASCII 环境,另一个用于二进制 8-bit 环境,还有一个使用 16-bit Unicode 编码,这些情况都是不同的。
编码细节已超出了 ABNF 所讨论的范围,然而附录 B 提供的 7-bit US-ASCII 环境在因特网规范中相当常见。
将语法和外部编码分隔开,目的是为了对于同样的语法可以选择使用不同的编码环境。
语法:
Rule Rule
一个规则可以通过排列一系列规则名,定义一个简单的,有序的值(比如,连续字符的串联)。
比如:
foo = %x61 ; a
bar = %x62 ; b
mumble = foo bar foo
注 ';' 号后为注释 注 ascii 编码中, a的十六进制表示为61
因此,规则 <mumble>
匹配小写字符串 "aba"。
直线空白(LWSP):串联是 ABNF 解析模型的核心。
连续字符(值)串是根据 ABNF 中定义的规则解析的。
对于因特网规范,由于某些历史原因,
允许直线空白(space和tab符)能够自由地,隐式地散落在主要结构中,比如界定符或原子字符
注意:
本篇 ABNF 规范没有为指定空白提供隐式的规范。
任何想要允许在分隔符或字符串片段上使用线性空白的语法必须显式地指定它。
在“核心”规则中提供这样的空白常常是有用的,这些规则会在更高级别的规则中使用。
“核心”规则可能形成词法分析器,或者简单地成为主要规则集的一部分。
语法:
Rule1 / Rule2
由斜杠 ("/") 分隔的元素是可选择的
因此:
foo / bar
可选 <foo>
或者 <bar>
。
注意:
用引号包围的字符串是一种指定可选择元素的特殊形式,
它被解释为一个非终结值,用于表示包含组合字符串的集合,
它是有顺序的,但可以使用任意大小写混合的形式。
语法:
Rule1 =/ Rule2
有时在分段中指定一个选择列表是比较方便的。
也就是说,初始规则可能匹配一个或多个选择,后续的规则定义将会添加到选择集中。
对于其他继承自同一父级规则集的独立规范来说这是相当有用的,例如参数列表常常就使用这种方式。
ABNF 允许通过以下结构实现增量定义:
oldrule =/ additional-alternatives
所以以下规则集
ruleset = alt1 / alt2
ruleset =/ alt3
ruleset =/ alt4 / alt5
与以下等同
ruleset = alt1 / alt2 / alt3 / alt4 / alt5
`
语法
%c##-##
使用连字符('-')可以指定可替代值的范围,以此紧凑地指定可选数值的范围
因此:
DIGIT = %x30-39
相当于:
DIGIT = "0" / "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9"
注 应该是 %x0-9 与下面的规则等价
串联的数字和数字值范围不能在同一个字符串中指定。
数字可能使用点号('.')来串联或使用连字符('-')来指定一个范围。
因此,想要在行结束符 (end-of-line) 序列间指定一个可打印的字符,
那么这样的规范可以是:
char-line = %x0D.0A %x20-7E %x0D.0A
注: 等价于
char-line = %x0D %x0A %x20-7E %x0D %x0A
语法:
(Rule1 Rule2)
圆括号中的元素可作为一个独立地元素对待,其内容是严格有序的。
因此,
elem (foo / bar) blat
匹配 (elem foo blat) 或 (elem bar blat),而
elem foo / bar blat
匹配 (elem foo) 或 (bar blat)。
注: 可以将括号当成改变优先级或者减少阅读误解的符号
注意:
强烈建议使用分组记号,而不是依赖于对选择的正确阅读,特别是在选择由多个规则名或字面量组成的时候。
因此,推荐使用下面的形式:
(elem foo) / (bar blat)
这能更好地避免读者的误解。
序列组记号也用在自由文本中,以便在文中设置元素序列。
语法:
*Rule
一个元素前的 "*" 操作符指的就是重复。完整的格式为:
<a>*<b>element
其中 <a>
和 <b>
是可选十进制数,表示至少有 <a>
个至多 <b>
个 element。
<a>
默认值是 0 , <b>
默认值是无穷,所以 *<element>
允许任意个 <element>
,包括零;
1*<element>
至少一个<element>
;
3*3<element>
只允许存在 3 个<element>
;
1*2<element>
允许一个或两个<element>
。
语法
nRule
规则格式:
<n>element
等同于
<n>*<n>element
这也就是说,会出现 <n>
个 <element>
。
因此,2DIGIT 就是一个 2位数,
而 3ALPHA 就是有三个字母的字符串。
语法
[RULE]
可选元素序列使用方括号包围:
[foo bar]
等同于
*1(foo bar).
注: 即 0到1个foo bar
, 即空或 foo bar
语法
; Comment
注释由一个分号开始,一直持续到行结束。
这是在规范中包含有用注释的一种简单方法。
上面描述的多个机制具有以下的优先级,以下规则,从上至下,优先级降低:
Rule name, prose-val, Terminal value
Comment
Value range
Repetition
Grouping, Optional
Concatenation
Alternative
注: 即:
规则名(<name>),终结值, prose-val
注释(';')
范围值(%c##-##)
重复('*Rule')
分组('(Ruel1 Rule2)'),可选('Rule1 / Rule2')
串联('Rule1 Rule2')
选择('[RULE]')
在使用选择操作符的时候如果随意混合串联可能会导致误解。
同样,推荐使用分组操作符来完成显式的串联分组。
注意:
- 这个语法需要一种相对严格的规则格式。因此,一个规范包含的规则集版本可能需要预处理来确保它能够被一个 ABNF 解析器解析。
- 这个语法使用了 附录 B 中提供的规则。
rulelist = 1*( rule / (*c-wsp c-nl) )
rule = rulename defined-as elements c-nl
; continues if next line starts
; with white space
rulename = ALPHA *(ALPHA / DIGIT / "-")
defined-as = *c-wsp ("=" / "=/") *c-wsp
; basic rules definition and
; incremental alternatives
elements = alternation *c-wsp
c-wsp = WSP / (c-nl WSP)
c-nl = comment / CRLF
; comment or newline
comment = ";" *(WSP / VCHAR) CRLF
alternation = concatenation
*(*c-wsp "/" *c-wsp concatenation)
concatenation = repetition *(1*c-wsp repetition)
repetition = [repeat] element
repeat = 1*DIGIT / (*DIGIT "*" *DIGIT)
element = rulename / group / option /
char-val / num-val / prose-val
group = "(" *c-wsp alternation *c-wsp ")"
option = "[" *c-wsp alternation *c-wsp "]"
char-val = DQUOTE *(%x20-21 / %x23-7E) DQUOTE
; quoted string of SP and VCHAR
; without DQUOTE
num-val = "%" (bin-val / dec-val / hex-val)
bin-val = "b" 1*BIT
[ 1*("." 1*BIT) / ("-" 1*BIT) ]
; series of concatenated bit values
; or single ONEOF range
dec-val = "d" 1*DIGIT
[ 1*("." 1*DIGIT) / ("-" 1*DIGIT) ]
hex-val = "x" 1*HEXDIG
[ 1*("." 1*HEXDIG) / ("-" 1*HEXDIG) ]
prose-val = "<" *(%x20-3D / %x3F-7E) ">"
; bracketed string of SP and VCHAR
; without angles
; prose description, to be used as
; last resort
本文档不涉及安全问题。
- [RFC733] Crocker, D., Vittal, J., Pogran, K., 和 D. Henderson,“ARPA 网络文本消息格式标准”,RFC 733,1977 年 11 月。
- [RFC822] Crocker, D.,“ARPA 因特网文本消息格式标准”,STD 11, RFC 822,1982 年 8 月。
致谢
ABNF 语法原本指定在 RFC 733 中。SRI International 的 Ken L. Harrenstien 负责了重新将 BNF 编码为扩展的 BNF 的工作,这使得它的表示形式更加小且易于理解。
这个项目的开始只是为了剔除那些被 non-email 规范编写者反复引用的 RFC 822 的一部分,也就是扩展的 BNF 的描述。与其简单地将现有的文本转换成单独的文档,工作组还选择仔细考虑过去 15 年中所提供的现有规范和相关规范的不足和好处,并因此进行改进。这使得这个项目变成了比最初设想的更加具有雄心的项目。有趣的是,结果与最初的结果并没有很大的不同,尽管诸如移除列表符号这样的决定令人吃惊。
本规范“分开的”版本是 DRUMS 工作组的一部分,Jerome Abela, Harald Alvestrand, Robert Elz, Roger Fajman, Aviva Garrett, Tom Harsch, Dan Kohn, Bill McQuillan, Keith Moore, Chris Newman, Pete Resnick, 和 Henning Schulzrinne 做出了巨大额贡献。
还要特别感谢 Julian Reschke 将标准版本的草案转换成 XML 源格式。
ABNF 的核心 ABNF
本附录包含了一个常用的基础规则。基础规则名称采用大写形式。注意这些规则仅在采用 7-bit ASCII 或字符集是 7-bit ASCII 超集的情况下才是合法的 ABNF。
某些基础规则名称采用大写形式,比如 SP, HTAB, CRLF, DIGIT, ALPHA,等等。
ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
BIT = "0" / "1"
CHAR = %x01-7F
; 除了NUL的 7-bit US-ASCII 字符,
CR = %x0D
; 回车键
CRLF = CR LF
; 互联网标准的换行符
CTL = %x00-1F / %x7F
; controls
DIGIT = %x30-39
; 0-9
DQUOTE = %x22
; " (双引号)
HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
HTAB = %x09
; 水平制表符
LF = %x0A
; 换行
LWSP = *(WSP / CRLF WSP)
; 使用直线空白规则(lwsp)允许行只包含wsp,这条规则在邮件headers中不再合法,并且已在其他环境中导致了互操作问题
; 定义邮件标题时不要使用,在其他上下文中要小心使用。
OCTET = %x00-FF
; 8 bits 数
SP = %x20
VCHAR = %x21-7E
; 可见(可打印)字符
WSP = SP / HTAB
; white space
在外部,数据表示为“网络虚拟 ASCII”(也就是,7-bit US-ASCII 占用一个 8-bit 字段),
高比特位 (8th) 设置为 0。
字符串值使用“网络字节顺序”,较高值的字节在左边表示并且首先通过网络发送。
注: 参考字节序-大端小端序