string - 切片包含 Unicode 字符的字符串

标签 string unicode rust slice

我有一段包含不同字节长度字符的文本。

let text = "Hello привет";

我需要在给定开始(包括)和结束(排除)字符索引的情况下截取字符串的一部分。我试过了

let slice = &text[start..end];

出现如下错误

thread 'main' panicked at 'byte index 7 is not a char boundary; it is inside 'п' (bytes 6..8) of `Hello привет`'

我想这是因为西里尔字母是多字节的,[..] 符号使用 byte 索引获取字符。如果我想使用 character 索引进行切片,我可以使用什么,就像我在 Python 中所做的那样:

slice = text[开始:结束] ?

我知道我可以使用 chars() 迭代器并手动遍历所需的子字符串,但有没有更简洁的方法?

最佳答案

代码点切片的可能解决方案

I know I can use the chars() iterator and manually walk through the desired substring, but is there a more concise way?

如果你知道确切的字节索引,你可以切片一个字符串:

let text = "Hello привет";
println!("{}", &text[2..10]);

这会打印“llo пр”。所以问题是找出确切的字节位置。您可以使用 char_indices() 轻松做到这一点迭代器(或者你可以使用 chars()char::len_utf8() ):

let text = "Hello привет";
let end = text.char_indices().map(|(i, _)| i).nth(8).unwrap();
println!("{}", &text[2..end]);

作为另一种选择,您可以先将字符串收集到 Vec<char> 中.然后,索引很简单,但是要将其打印为字符串,您必须重新收集它或编写自己的函数来完成。

let text = "Hello привет";
let text_vec = text.chars().collect::<Vec<_>>();
println!("{}", text_vec[2..8].iter().cloned().collect::<String>());

为什么这不简单?

如您所见,这两种解决方案都不是那么好。这是有意为之,原因有二:

作为str是一个简单的 UTF8 缓冲区,通过 unicode 代码点进行索引是一个 O(n) 操作。通常,人们期望 []运算符是 O(1) 操作。 Rust 使这种运行时复杂性显式化并且不试图隐藏它。在上面的两个解决方案中,您可以清楚地看到它不是 O(1)。

但更重要的原因:

Unicode 代码点通常不是有用的单位

Python 所做的(以及您认为自己想要的)并不是那么有用。这一切都归结为语言的复杂性以及 unicode 的复杂性。 Python 切片 Unicode 代码点。这就是 Rust char代表。它有 32 位大(少几位就足够了,但我们四舍五入为 2 的幂)。

但您真正想要做的是切片用户感知的字符。但这是一个明确定义松散的术语。不同的文化和语言将不同的事物视为“一字”。最接近的近似是“字素簇”。这样的集群可以由一个或多个 unicode 代码点组成。考虑这个 Python 3 代码:

>>> s = "Jürgen"
>>> s[0:2]
'Ju'

令人惊讶,对吧?这是因为上面的字符串是:

  • 0x004A拉丁文大写字母 J
  • 0x0075拉丁文小写字母 U
  • 0x0308结合分娩
  • ...

这是作为前一个字符的一部分呈现的组合字符的示例。 Python 切片在这里做了“错误”的事情。

另一个例子:

>>> s = "fire"
>>> s[0:2]
'fir'

也不是您所期望的。这次,fi实际上是连字 , 这是一个代码点。

Unicode 以令人惊讶的方式表现的例子还有很多。有关详细信息和示例,请参阅底部的链接。

因此,如果您想使用应该能够在任何地方使用的国际字符串,请不要进行代码点切片!如果您确实需要从语义上将字符串视为一系列字符,请使用字素簇。为此, crate unicode-segmentation 非常有用。


有关此主题的更多资源:

关于string - 切片包含 Unicode 字符的字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51982999/

相关文章:

rust - 了解 rust `Rc<RefCell<_>>`

ios - 快速将字符串转换为 double 型

java - 从 Java 中的子字符串中高效解析整数

sql - 什么是 SQL Server 对 MySQL 的 unicode_ci 排序规则的模拟?

java - GlassFish 4.0 中的 UTF-8 问题

rust - 结构是否可以引用具有泛型方法的特征对象,而不使结构本身泛型?

rust - 有没有办法检查字段的可变性?

scripting - 如何分割字符串并在ant脚本的for循环中使用它?

python - 在dynamic_rnn和LSTMCell中使用带有输入字符串的TensorFlow时出错

c++ - linux g++ 的 Unicode 问题