96 个版本 (43 个稳定版)
8.0.0-alpha2 | 2024 年 5 月 5 日 |
---|---|
7.1.3 | 2023 年 1 月 15 日 |
7.1.1 | 2022 年 3 月 14 日 |
7.1.0 | 2021 年 11 月 4 日 |
0.2.0 | 2015 年 3 月 24 日 |
#2 在 解析器实现
7,798,705 每月下载量
用于 14,776 个 crate (1,956 直接使用)
660KB
13K SLoC
nom,逐字节吃掉数据
nom 是一个用 Rust 编写的解析器组合库。它的目标是提供构建安全解析器的工具,而不牺牲速度或内存消耗。为此,它广泛使用 Rust 的 强类型 和 内存安全 来生成快速且正确的解析器,并提供函数、宏和特性来抽象大部分容易出错的底层代码。
nom 很乐意从您的文件中提取字节 :)
示例
十六进制颜色 解析器
use nom::{
bytes::complete::{tag, take_while_m_n},
combinator::map_res,
sequence::Tuple,
IResult,
Parser,
};
#[derive(Debug, PartialEq)]
pub struct Color {
pub red: u8,
pub green: u8,
pub blue: u8,
}
fn from_hex(input: &str) -> Result<u8, std::num::ParseIntError> {
u8::from_str_radix(input, 16)
}
fn is_hex_digit(c: char) -> bool {
c.is_digit(16)
}
fn hex_primary(input: &str) -> IResult<&str, u8> {
map_res(
take_while_m_n(2, 2, is_hex_digit),
from_hex
).parse(input)
}
fn hex_color(input: &str) -> IResult<&str, Color> {
let (input, _) = tag("#")(input)?;
let (input, (red, green, blue)) = (hex_primary, hex_primary, hex_primary).parse(input)?;
Ok((input, Color { red, green, blue }))
}
fn main() {
println!("{:?}", hex_color("#2F14DF"))
}
#[test]
fn parse_color() {
assert_eq!(
hex_color("#2F14DF"),
Ok((
"",
Color {
red: 47,
green: 20,
blue: 223,
}
))
);
}
文档
如果您在开发解析器时需要任何帮助,请在 IRC(Libera、Geeknode、OFTC)上 ping geal
,前往 Libera IRC 上的 #nom-parsers
,或在 Gitter 聊天室。
为什么使用 nom
如果您想编写
二进制格式解析器
nom 从一开始就被设计用来正确解析二进制格式。与通常的手写 C 解析器相比,nom 解析器具有相同的速度,没有缓冲区溢出漏洞,并为您处理常见模式
- TLV
- 位级解析
- 在调试宏中的十六进制查看器,便于数据分析
- 适用于网络格式和大型文件的流解析器
示例项目
文本格式解析器
虽然 nom 最初是为二进制格式制作的,但它很快就能很好地处理文本格式。从基于行的格式如 CSV 到更复杂的嵌套格式如 JSON,nom 都能管理,并提供有用的工具
- 快速不区分大小写的比较
- 识别转义字符串
- 可以在 nom 解析器中嵌入正则表达式来简洁地表示复杂的字符模式
- 特别关注正确处理非 ASCII 字符
示例项目
编程语言解析器
虽然编程语言解析器通常为了更大的灵活性和性能而手动编写,但 nom 可以(并且已经成功地)用作语言的原型解析器。
nom 将让您快速入门,提供强大的自定义错误类型,您可以使用 nom_locate 来精确定位错误的行和列。无需单独的标记化、词法分析和解析阶段:nom 可以自动处理空白解析,并在原地构建抽象语法树(AST)。
示例项目
流格式
虽然许多格式(及其处理它们的代码)假设它们可以将完整数据放入内存中,但还有一些格式,我们一次只能获取部分数据,例如网络格式或大文件。nom 已经设计为对部分数据进行正确处理:如果没有足够的数据来决定,nom 会告诉你需要更多的数据,而不是默默地返回错误的结果。无论您的数据是全部还是分块提供的,结果都应该是一样的。
它允许您为您的协议构建强大、确定性的状态机。
示例项目
解析器组合器
解析器组合器是解析器的一种方法,与 lex 和 yacc 等软件非常不同。您不需要在单独的文件中编写语法并生成相应的代码,而是使用非常小且具有特定目的的函数,例如“取5个字节”或“识别单词‘HTTP’”,并将它们组合成有意义的模式,例如“识别‘HTTP’,然后是一个空格,然后是一个版本”。生成的代码很小,看起来就像您使用其他解析器方法编写的语法。
这有几个优点
- 解析器小且易于编写
- 解析器组件易于重用(如果它们足够通用,请将其添加到 nom 中!)
- 解析器组件易于单独测试(单元测试和基于属性的测试)
- 解析器组合代码看起来接近您会编写的语法
- 您可以根据当前所需的数据构建部分解析器,并忽略其余部分
技术特性
nom 解析器适用于
- 字节导向:基本类型是
&[u8]
,解析器尽可能在字节数组切片上工作(但不仅限于它们) - 位导向:nom 可以将字节数组切片作为位流来寻址
- 字符串导向:相同类型的组合器也可以应用于 UTF-8 字符串
- 零复制:如果解析器返回其输入数据的一个子集,它将返回该输入的切片,而无需复制
- 流式处理:nom 可以在部分数据上工作,并在需要更多数据以产生正确结果时检测到这一点
- 描述性错误:解析器可以聚合一系列错误代码,并带有指向受指责输入切片的指针。这些错误列表可以通过模式匹配来提供有用的消息。
- 自定义错误类型:您可以为解析器返回的错误提供特定类型以提高错误
- 安全解析:nom 利用 Rust 的安全内存处理和强大的类型,并且解析器通常会使用真实世界数据模糊测试和测试。到目前为止,通过模糊测试发现的唯一缺陷是在 nom 之外编写的代码中
- 速度:基准测试表明,nom 解析器通常优于许多解析器组合库,如 Parsec 和 attoparsec,一些正则表达式引擎,甚至是手写的 C 解析器
一些基准测试可在 GitHub 上找到。
Rust 版本要求(MSRV)
nom 的 7.0 系列支持 Rustc 版本 1.56 或更高。
当前政策是,这仅在下一个主要 nom 版本中更新。
安装
nom 可在 crates.io 上找到,并可以像这样包含在您的 Cargo 启用项目中
[dependencies]
nom = "7"
有几个编译功能
alloc
:默认启用。如果禁用,nom可以在没有内存分配器的no_std
构建中运行。如果启用,将可用分配的组合子(如many0
)std
:默认启用,同时也会启用alloc
。如果禁用,nom可以在no_std
构建中运行
您可以通过以下方式配置这些功能
[dependencies.nom]
version = "7"
default-features = false
features = ["alloc"]
相关项目
使用 nom 编写的解析器
以下是一些已知使用nom的项目列表(非详尽列表)
- 文本文件格式:Ceph Crush、Cronenberg、Email、XFS运行时统计、CSV、FASTA、FASTQ、INI、ISO 8601日期、类似于libconfig的配置文件格式、网络存档、PDB、proto文件、Fountain剧本标记、vimwiki & vimwiki_macros、Kconfig语言、Askama模板
- 编程语言:PHP、基本计算器、GLSL、Lua、Python、SQL、Elm、SystemVerilog、Turtle、CSML、Wasm、伪代码、MeiliSearch过滤器、PotterScript
- 接口定义格式:Thrift
- 音频、视频和图像格式:GIF、MagicaVoxel .vox、MIDI、SWF、WAVE、Matroska (MKV)、JPEG/HEIF/HEIC/MOV/MP4的Exif/Metadata解析器
- 文档格式:TAR、GZ、GDSII
- 加密格式:X.509
- 网络协议格式:Bencode,D-Bus,DHCP,HTTP,URI,IMAP(alt),IRC,Pcap-NG,Pcap,Pcap + PcapNG,IKEv2,NTP,SNMP,Kerberos v5,DER,TLS,IPFIX / Netflow v10,GTP,SIP,SMTP,Prometheus
- 语言规范:BNF
- 其他格式:Game Boy ROM,ANT FIT,版本号,Telcordia/Bellcore SR-4731 SOR OTDR 文件,MySQL 二进制日志,URI,假名,Wordle 结果,NBT
想要使用 nom
创建新的解析器?尚未实现的格式列表可在此处找到。
想要在此处添加您的解析器?创建一个拉取请求!
贡献者
nom 是多年来许多贡献者的工作成果,感谢您的帮助!