为什么这么复杂?
让我们逐行分解它
let s1 = "foobar";
我们创建了一个以
UTF-8 编码的文本字符串。 UTF-8 允许我们以相对紧凑的方式编码
Unicode 的 1,114,112
代码点,如果你来自一个主要使用
ASCII 字符的地区,则这种方式非常适合你。UTF-8 是一种
可变长度编码,这意味着单个代码点可能需要
1 到 4 个字节。较短的编码保留给 ASCII,但是
许多汉字在 UTF-8 中占用 3 个字节。
let mut v: Vec<char> = s1.chars().collect();
这将创建一个字符向量。字符是一个32位数字,直接映射到代码点。如果我们从ASCII纯文本开始,我们的内存需求将增加四倍。如果我们有许多来自
天界的字符,那么可能我们并没有使用太多额外的内存。
v[0] = v[0].to_uppercase().nth(0).unwrap();
这会获取第一个代码点并要求将其转换为大写变体。不幸的是,对于我们这些以英语为母语的人来说,并不总是存在“小写字母”到“大写字母”的简单一对一映射。顺便说一下:我们称它们为大写和小写
因为在过去的日子里,一个字母盒在另一个字母盒上方。
链接1。
当一个字符没有相应的大写变体时,此代码将会抛出错误。实际上我不确定这种情况是否存在。另外,当一个字符有多个大写变体时(比如德语中的ß
),此代码也可能在语义上失败。请注意,在现实世界中,ß
可能永远不会被大写,这只是我经常记得并搜索的例子。事实上,截至2017年6月29日,德语拼写的官方规则已更新,因此两种大写形式"ẞ"和"SS"都是有效的!
let s2: String = v.into_iter().collect();
在这里,我们将字符转换回UTF-8,并需要新的分配来存储它们,因为原始变量存储在常量内存中,以便在运行时不占用内存。
let s3 = &s2;
现在我们引用那个String
。
这是一个简单的问题
不幸的是,这并不是真的。也许我们应该努力将世界转化为Esperanto?
我假设char::to_uppercase
已经正确处理Unicode了。
是的,我当然希望如此。不幸的是,在某些情况下,Unicode并不足够。感谢
huon for pointing out指出了
土耳其语I,其中大写(
İ)和小写(
i)版本都有一个点。也就是说,字母
i
的大小写取决于源文本的
locale。
为什么需要所有数据类型转换?
因为在关注正确性和性能时,您正在使用的数据类型非常重要。一个char
是32位,而字符串是UTF-8编码的。它们是不同的东西。
索引可能返回多字节的Unicode字符
可能存在一些术语不匹配的情况。一个
char
是一个多字节的Unicode字符。
如果逐字节进行,则可以对字符串进行
切片,但如果不在字符边界上,则标准库将会出现故障。
没有实现通过索引字符串以获取字符的原因之一是因为很多人误用字符串作为ASCII字符数组。索引字符串以
设置字符永远不可能是高效的——你必须能够用一个也是1-4个字节的值替换1-4个字节,这会导致字符串的其余部分相当不稳定。
to_uppercase
可以返回大写字符。
如上所述,
ß
是一个单独的字符,当它被大写时,变成了
两个字符。
解决方案
请参见
trentcl's answer,它只将ASCII字符转换为大写。
原始内容
如果我必须编写代码,它看起来像:
fn some_kind_of_uppercase_first_letter(s: &str) -> String {
let mut c = s.chars();
match c.next() {
None => String::new(),
Some(f) => f.to_uppercase().chain(c).collect(),
}
}
fn main() {
println!("{}", some_kind_of_uppercase_first_letter("joe"));
println!("{}", some_kind_of_uppercase_first_letter("jill"));
println!("{}", some_kind_of_uppercase_first_letter("von Hagen"));
println!("{}", some_kind_of_uppercase_first_letter("ß"));
}
我建议在 crates.io 上搜索 uppercase 或 unicode,让比我更聪明的人来处理它。
改进
说到“比我更聪明的人”,Veedrac 指出,在访问第一个大写字符代码点后,将迭代器转换回切片可能更有效率。这样可以使用 memcpy
复制其余字节。
fn some_kind_of_uppercase_first_letter(s: &str) -> String {
let mut c = s.chars();
match c.next() {
None => String::new(),
Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
}
}
ß
大写。提示:它不是一个单独的字符。即使问题陈述本身也可能很复杂。例如,将姓氏von Hagen
的第一个字母大写是不正确的。这都是生活在一个拥有数千年分歧文化和不同惯例的全球世界的方面,我们试图将所有这些压缩到8个位和2行代码中。 - Shepmasterchar::to_uppercase
确实可以解决这个问题,但是你只获取了第一个码点(使用nth(0)
),而没有获取所有组成大写字母的码点。 - user395760