2023 年 07 月 25 日
使用正则表达式对文本进行分割通常是多快好省之举,但是在遇到一个有着多个模式的字符串,必须构造多个相应的正则表达式对其重复匹配,遇到该情况,我倾向于自行编写专用的解析程序。假设字符串
s
,其值可能为空或不少于 1
个空格,也可能为前后可能存在空格的 +
或
^+
。编写一个程序,识别 s
的值对应的模式。
Rust 的 &str
和 String
类型皆有
chars
方法,该方法返回字符串迭代器。例如,
fn main() {
let s = "你好!";
for c in s.chars() {
println!("{}", c);
}
}
或
fn main() {
let s = String::from("你好!");
for c in s.chars() {
println!("{}", c);
}
}
输出皆为
你
好
!
需要注意的是,Rust 以 UTF-8 作为字符串编码,上述程序表明 Rust 语言能够正确处理以多个字节表示单个字符的中文编码。
在遍历字符串的过程中,可以构造一个状态机,用它判断字符串对应的形式。状态机的所有状态可基于枚举类型予以定义,例如
enum Status {
, // 初始状态
Init, // 为空或不少于 1 个空格
Empty, // 前后可能存在空格的 +
Append, // 前后可能存在空格的 ^+
Prepend// 可能是 prepend 状态
MaybePrepend }
基于 Rust 的 match
语法可实现状态的迁移:
let s = " ^+ ";
let mut m = Status::Init;
for c in s.chars() {
match m {
Status::Init => {
match c {
' ' => m = Status::Empty,
=> panic!("Unknown text!")
_ }
},
Status::Empty => {
match c {
' ' => {},
'+' => m = Status::Append,
'^' => m = Status::MaybePrepend,
=> panic!("Unknown text!")
_ }
},
Status::Append => {
match c {
' ' => {},
=> panic!("Unknown text!")
_ }
},
Status::MaybePrepend => {
match c {
'+' => m = Status::Prepend,
=> panic!("Unknown text!")
_ }
},
Status::Prepend => {
match c {
' ' => {},
=> panic!("Unknown text!")
_ }
}
}
}
println!("{:?}", m);
上述代码输出为 Prepend
,即 m
的值为
Status::Prepend
。
上述用 Rust 编写的状态机,用 C 的 switch...case
语法亦可实现等价版本,并无困难之处,不再赘述。真正的难点在于,C
语言及其标准库不支持 UTF-8 编码,如何编写可对 UTF-8
编码的字符串以字符为单位的遍历程序?
GLib 提供了 UTF-8 编码的文本处理功能。我曾写过一篇文章对此有所介绍,详见「在 C 程序中处理 UTF-8 文本」。
GNU 项目的 libunistring 库亦能让 C 程序支持 UTF-8 编码的字符串,我也曾为此写过一篇文章,详见「C 程序严重的 Unicode」。
Rust 标准库对 UTF-8 编码的字符串提供的支持,顺应了 UTF-8 一统天下的潮流,C 语言用户则可通过一些第三方库解决该问题,但相较之下,Rust 更为简便。