12个不稳定版本 (3个重大变更)
0.3.0 | 2021年12月9日 |
---|---|
0.2.0 | 2021年12月7日 |
0.1.1 | 2021年12月5日 |
0.0.9 | 2021年12月3日 |
0.0.4 | 2017年3月12日 |
2231 在 解析器实现 中排名
每月下载量 466
115KB
3K SLoC
xml_oxide
Rust XML解析器实现,以流式方式解析符合W3C规范的任何有效XML。
特性
- 它使用类似常量的内存来处理大型XML文件
- 足够快,适用于大多数用例。它可以在大约23秒内解析1GB的XML文件(内存中)
- 支持XML 1.0中的命名空间
- 它仅支持UTF-8编码
- 它是一个非验证解析器,执行重要的有效性检查
- 目前,它忽略处理指令、DTD/DOCTYPE中的有效性,并将它们作为原始字符串解析。它检查包括这些实体在内的总体有效性。(它甚至解析DOCTYPE中的注释以实现这一点)
- 它可以解析非有效文档(请报告为错误)
- 较大的实体以块的形式解析以保持内存使用量低:字符数据、CDATA部分、注释、空白
- 当前,块大小默认为8KB,不可配置。内部缓冲区为16KB。如果您有一个大于缓冲区的元素标签或DOCTYPE声明,它可以回溯并分配更多内存以进行解析操作。测试使用1字节块大小。
不安全的使用
unsafe
用于函数std::str::from_utf8_unchecked
。它在已经使用std::str::from_utf8
检查为有效UTF8字符串的字节切片上使用。虽然未测试性能提升。- 不再使用RefCell。有趣的是,只需将
RefCell<Vec<u8>>
更改为circular::Buffer
即可通过Rust借用检查。我将保留此注释作为参考。RefCell
的使用是因为Rust对在条件循环中使用可变项过于严格。希望非词法生命周期会随着时间的推移变得更好。
待办事项
- 由于命名空间规范对名称中使用":"施加了约束,因此提供了
namespace-aware=false
选项来解析其他有效的XML 1.0文档。 - 更多测试
- 解析每个实体(包括DTD),以便能够利用合规性测试套件。
示例用法
在此示例中,统计了StartElement
和EndElement
事件。注意,您可以在tests
目录下找到更多示例。
StartElement
还包括空标签。通过is_empty
进行检查。- 像
&
或<
这样的引用实体在自己的事件中出现(不在Characters
中)。 - 解析字符/数值和预定义实体引用。自定义实体定义以原始形式传递。
- 查看sax::Event以了解所有可用事件类型
use std::fs::File;
use xml_oxide::{sax::parser::Parser, sax::Event};
fn main() {
println!("Starting...");
let mut counter: usize = 0;
let mut end_counter: usize = 0;
let now = std::time::Instant::now();
let f = File::open("./tests/xml_files/books.xml").unwrap();
let mut p = Parser::from_reader(f);
loop {
let res = p.read_event();
match res {
Ok(event) => match event {
Event::StartDocument => {}
Event::EndDocument => {
break;
}
Event::StartElement(el) => {
//You can differantiate between Starting Tag and Empty Element Tag
if !el.is_empty {
counter = counter + 1;
// print every 10000th element name
if counter % 10000 == 0 {
println!("%10000 start {}", el.name);
}
}
}
Event::EndElement(el) => {
end_counter += 1;
if el.name == "feed" {
break;
}
}
Event::Characters(_) => {}
Event::Reference(_) => {}
_ => {}
},
Err(err) => {
println!("{}", err);
break;
}
}
}
println!("Start event count:{}", counter);
println!("End event count:{}", end_counter);
let elapsed = now.elapsed();
println!("Time elapsed: {:.2?}", elapsed);
}
历史 & 信用
我试图在2017年指定一个类似于Java SAX库的推送解析器接口并实现它。这个想法是提供一个可以由社区中的多个实现使用的接口。它工作着(尽管速度较慢),但主要问题是推送解析器在Rust中不便于使用。经过长时间的思考和对Rust的进一步了解,我决定实现一个拉取解析器。目前,SAX(拉取)接口只是一个enum
及其行为(如每个调用分割字符的可能性)。
如果您想使用xml_sax
接口实现另一个解析器,我们可以讨论改进接口。目前,它已集成到这个crate中。
pulldown-cmark
中的Why a pull parser?部分是一个很好的解释。
当前接口受到了quick-xml
、xml-rs
和Java库的启发。
nom
是一个很棒的库。它只是你最初尝试时可能会做的事情的结晶和更好的版本(我知道)。它还展示了Rust中组合性的力量。
依赖关系
~1.2–1.9MB
~38K SLoC