我想把&str
的第一个字母大写。这是一个简单的问题,我希望得到一个简单的解决方案。直觉告诉我这样做:
let mut s = "foobar";
s[0] = s[0].to_uppercase();
但是&str
不能这样索引,我唯一能做的就是把&str
转换成一个迭代器,再把迭代器转换成一个向量,向量的第一项是大写的,这就创建了一个迭代器,我把它索引进去,创建了一个Option
,然后把向量转换成一个迭代器,再把它转换成String
,再把它转换成&str
。
let s1 = "foobar";
let mut v: Vec<char> = s1.chars().collect();
v[0] = v[0].to_uppercase().nth(0).unwrap();
let s2: String = v.into_iter().collect();
let s3 = &s2;
有没有比这更简单的方法,如果有,是什么?如果没有,为什么铁 rust 要这样设计?
类似问题
9条答案
按热度按时间f45qwnt81#
为何如此复杂?
让我们逐行分析
我们已经创建了一个以UTF-8编码的字符串。UTF-8允许我们以一种非常紧凑的方式对Unicode的1,114,112个code points进行编码,如果你来自世界上一个主要使用ASCII(1963年创建的标准)字符的地区。UTF-8是一种 * 可变长度 * 编码,这意味着单个码位可能是take from 1 to 4 bytes。较短的编码是为ASCII保留的,但many Kanji take 3 bytes in UTF-8除外。
这就创建了一个
char
字符的向量。字符是一个32位数字,直接Map到一个码位。如果我们从纯ASCII文本开始,我们的内存需求就增加了四倍。如果我们有一堆来自the astral plane的字符,那么我们可能还没有使用那么多。这将获取第一个码位并请求将其转换为大写变体。不幸的是,对于我们这些从小说英语的人来说,这里有not always a simple one-to-one mapping of a "small letter" to a "big letter"。边注:我们称之为大写字母和小写字母是因为在过去,一个字母盒位于另一个字母盒的上方。
当一个代码点没有对应的大写变体时,这段代码会出现混乱。我不确定是否存在。当一个代码点有一个包含多个字符的大写变体时,它也可能在语义上失败,比如德语的
ß
。注意,ß在真实的世界中可能永远不会大写。这是我能一直记住和搜索的唯一例子。截至2017-06-29,事实上,德语拼写的官方规则已经更新,这样both "ẞ" and "SS" are valid capitalizations!这里我们将字符转换回UTF-8,并需要新的分配来存储它们,因为原始变量存储在常量内存中,以便在运行时不占用内存。
现在我们引用
String
。这是个简单的问题
不幸的是,这不是真的。也许我们应该奋进把世界转换成Esperanto?
我认为
char::to_uppercase
已经正确处理了Unicode。是的,我当然希望如此。不幸的是,Unicode并不适用于所有情况。感谢huon指出Turkish I,其中大写(****)和小写(i)版本都有一个点。也就是说,没有一个正确的大写字母
i
;它还取决于源文本的locale。为什么需要所有数据类型转换?
因为当你担心正确性和性能时,你所使用的数据类型是很重要的。
char
是32位的,字符串是UTF-8编码的,它们是不同的东西。索引可以返回多字节Unicode字符
这里可能有一些不匹配的术语。
char
* 是 * 多字节Unicode字符。如果逐字节地对字符串进行 * 切片 * 是可能的,但如果不在字符边界上,标准库将出现混乱。
索引字符串以获取字符的方法从未被实现的原因之一是,很多人将字符串误用为ASCII字符数组。索引字符串以 set 字符永远不会是高效的--你必须能够用同样是1-4字节的值来替换1- 4字节,这会导致字符串的其余部分来回跳动。
to_uppercase
可以返回大写字符如上所述,
ß
是单个字符,当大写时,它变成两个字符。解决方案
另请参阅trentcl's answer,其中只包含大写ASCII字符。
原始
如果我必须写代码,它看起来像:
但我可能会在www.example.com上搜索uppercase或unicodecrates.io,让比我聪明的人来处理。
改善
说到“比我聪明的人”,Veedrac指出,在访问第一个大写代码点之后,将迭代器转换回切片可能更有效,这允许剩余字节的
memcpy
。yqyhoc1h2#
有没有比这更简单的方法,如果有,是什么?如果没有,为什么铁 rust 要这样设计?
好吧,是的,也不是。正如另一个答案所指出的,你的代码是不正确的,如果你给予它一些类似于的东西,你会感到恐慌。所以用Rust的标准库做这件事比你最初想象的还要难。
然而,Rust旨在鼓励代码重用,并使引入库变得容易。因此,大写字符串的惯用方法实际上是相当令人满意的:
gk7wooem3#
如果您能够将输入限制为仅包含ASCII字符串,那么这并不特别复杂。
从Rust 1.23开始,
str
有了一个make_ascii_uppercase
方法(在旧的Rust版本中,它是通过AsciiExt
trait提供的)。这意味着你可以相对容易地将ASCII字符串切片大写:这会将
"taylor"
转换为"Taylor"
,但不会将"édouard"
转换为"Édouard"
。(playground)请谨慎使用。
fcwjkofz4#
我是这样做的:
如果不是ASCII字符串:
huus2vyu5#
观察员办事处进一步采取的办法:
或
也可以使用Unicode字符,例如
"😎foobar"
eqqqjvef6#
由于方法
to_uppercase()
返回一个新字符串,因此您应该能够像这样添加字符串的剩余部分。这在Rust版本1.57+中进行了测试,但在支持切片的任何版本中都可能工作。
ruarlubt7#
下面这个版本比@Shepmaster的改进版慢一点,但也更符合 * 习惯 *:
pcww981p8#
这就是我解决这个问题的方法,注意我必须在转换为大写之前检查self是否不是ascii。
输出量
tjjdgumg9#
受get_mut示例的启发,我编写了如下代码: