#string #iterator #str #pattern-matching

no-std string_iter

一个过度设计的 &str 迭代器,考虑到零拷贝解析

1 个不稳定版本

0.1.0 2023 年 9 月 4 日

#2063 in Rust 模式


bevy_aoui_widgets 中使用

MIT/Apache

62KB
1.5K SLoC

StringIter

一个过度设计的 &str 迭代器,考虑到零拷贝解析,并注重人体工程学。


lib.rs:

一个过度设计的 &str 迭代器,考虑到零拷贝解析,并注重人体工程学。

用法

StringIter 提供了迭代和模式匹配方法,以及通常在字符串类型中找到的、对迭代器有意义的方法。

标准的 StringIter 在其 char&str 表示形式中都产生一个 char,允许轻松地以它的 &strCow<str> 形式存储。

  • 修剪
let mut iter = "  !#$@!foo&*  ".str_iter();
iter.trim();
assert_eq!(iter.as_str(), "!#$@!foo&*");
iter.trim_start_by(|x: char| !x.is_alphabetic());
assert_eq!(iter.as_str(), "foo&*");
iter.trim_end_by(|x: char| !x.is_alphabetic());
assert_eq!(iter.as_str(), "foo");
  • 预览
let mut iter = "bar".str_iter();
assert_eq!(iter.peek(), Some(('b', "b")));
assert_eq!(iter.peek_back(), Some(('r', "r")));
assert_eq!(iter.peekn(2), Ok("ba"));
assert_eq!(iter.peekn_back(2), Ok("ar"));
assert_eq!(iter.peekn(4), Err("bar"));
assert_eq!(iter.peekn_back(4), Err("bar"));
  • 迭代
let chars = [('😀', "😀"), ('🙁', "🙁"), ('😡', "😡"), ('😱', "😱")];
for (a, b) in "😀🙁😡😱".str_iter().zip(chars.into_iter()) {
    assert_eq!(a, b);
}
  • 前瞻
let mut iter = "蟹🦀a🚀𓄇ë".str_iter().look_ahead(2).strs();
assert_eq!(iter.next(), Some("蟹🦀"));
assert_eq!(iter.next(), Some("🦀a"));
assert_eq!(iter.next(), Some("a🚀"));
assert_eq!(iter.next(), Some("🚀𓄇"));
assert_eq!(iter.next(), Some("𓄇ë"));
assert_eq!(iter.next(), Some("ë"));
assert_eq!(iter.next(), None);
  • 按模式切片
let mut iter = "{{foo}bar}baz".str_iter();
let mut count = 0;
let s = iter.next_slice((|x| {
    match x {
        '{' => count += 1,
        '}' => count -= 1,
        _ => (),
    };
    count == 0
}).sep_with(Sep::Yield));
assert_eq!(s, Some("{{foo}bar}"));
assert_eq!(iter.as_str(), "baz");
  • 分割
let mut iter = "thisIsCamelCase"
    .str_iter()
    .into_substrs(|c: char| c.is_uppercase());
assert_eq!(iter.next(), Some("this"));
assert_eq!(iter.next(), Some("Is"));
assert_eq!(iter.next(), Some("Camel"));
assert_eq!(iter.next(), Some("Case"));
assert_eq!(iter.next(), None);

模式

我们在 trimslicesplit 中使用 Patterns

trim 中,模式匹配直到找到假值。

slicesplit 中,模式匹配直到找到真值。

有关处理边界情况的详细信息,请参阅 [Sep] 和 sep_with()

支持的模式

匹配第 n 个 char

  • ..isize

匹配前 n 个 char。这对于 trim 很有用。

匹配一个 char。

通过向前查看匹配一个 &str

  • &[char][char;N]

匹配集合中的任何字符。

  • char..=char

匹配范围内的字符,我们只支持包含范围以避免错误。

  • FnMut(char) ->FallibleBool

匹配使函数返回 true 的任何字符。

FallibleBool 可以是 boolOption<bool> 或 [Result<bool, E: Debug>]

  • (FnMut(&str) ->FallibleBool).期望(n)

通过向前查看匹配任何 &str,使其通过查看 nchar 返回 true。

  • (FnMut(char, &str) ->FallibleBool).期望(n)

通过向前查看匹配任何 &str,使其通过查看 nchar 返回 true。

char&str 中的第一个 char

通过间隔重复匹配。

一个将 match 模式转换为 Pattern 的宏。

你可以编写自己的模式类型!

示例

从一个字符串中获取 ascii 标识符

let foo = r#"  [email protected] "#;
let mut iter = foo.str_iter();
iter.trim_start();
let mut quotes = 0;
let slice = match iter.peek() {
    Some(('a'..='z'|'A'..='Z'|'_', _)) => {
        iter.next_slice(pat!(!'a'..='z'|'A'..='Z'|'0'..='9'|'_'))
    }
    _ => panic!("expected ident")
};
assert_eq!(slice, Some("ferris123"));

// note @ is still in the iterator
assert_eq!(iter.as_str(), "@crab.io ");

从一个字符串中获取字符串字面量 "foo"

let foo = r#"    "foo"  bar "#;
let mut iter = foo.str_iter();
iter.trim_start();
let mut quotes = 0;
let slice = iter.next_slice((|c| match c {
    '"' =>  {
        quotes += 1;
        quotes == 2
    }
    _ => false,
}).sep_with(Sep::Yield));
assert_eq!(slice, Some("\"foo\""));
assert_eq!(iter.as_str(), "  bar ");

性能

此 crate 的速度与 [str::chars()] 相当。

如果单独操作 char,则 [str::chars()] 更快。

但如果你需要将 char 转换回 UTF-8,则 StringIter 可能比 [str::chars()] 更快。

安全性

这个crate使用大量不安全代码来利用UTF-8的固有特性和绕过一些边界检查和UTF-8检查。

此外,如果给定无效的UTF-8输入,我们也不能保证内存安全。

如果您发现任何稳定性问题,请提交问题。此crate中使用了恶意迭代器。

映射迭代器与StringIter共享常规方法,在功能上是相同的。此crate中使用了恶意模式。方便地导出常用成员

use string_iter::prelude::*;

无运行时依赖