如何从字符串中获取第一个字符?

136

我想获取 std::str 的第一个字符。目前的方法 char_at()String::slice_chars 都是不稳定的。

我想到了以下方法,但似乎过于复杂,因为只需要获取单个字符而没有使用其余的向量:

let text = "hello world!";
let char_vec: Vec<char> = text.chars().collect();
let ch = char_vec[0];
7个回答

205

UTF-8并没有定义什么是“字符”,因此这取决于你想要什么。在这种情况下,char是Unicode标量值,因此&str的第一个char将介于1个到4个字节之间。

如果你只想要第一个char,那就不要收集到Vec<char>中,直接使用迭代器:

let text = "hello world!";
let ch = text.chars().next().unwrap();

或者,您可以使用迭代器的nth方法:

Alternatively, 您可以使用迭代器的 nth 方法。
let ch = text.chars().nth(0).unwrap();
请注意,在传递给nth 的索引之前的元素将从迭代器中消耗掉。

10
你可能还想考虑一下是否真的需要第一个字母。 - moveaway00
2
这个函数返回第n个代码单元,但是char_at返回从字节n开始的代码单元。后者更有用,因为大多数字符串操作都涉及字节索引。这相当于char_at(也是常数时间):text[i..].chars().next().unwrap() - user395760
4
我认为实际使用char_at略有风险,因为索引可能位于一个代码单元内。 - Matthieu M.
5
是的,码点索引方式无论如何都不能以O(1)的速度运行。然而,当有人要求第n个字符时,他们可能正在请求第n个码点或第n个图形簇,但不太可能是请求第n个字节。 - Matthieu M.
1
这个在第n个字符上的泛化效果如何,而不仅仅是第一个字符? - anon
显示剩余7条评论

10
如果您只想进行测试,您可以使用 starts_with() 方法:
"rust".starts_with('r')
"rust".starts_with(|c| c == 'r')

1
你能解释一下这两个例子做了什么吗? - MikeB
2
@MikeB 第一个比较了第一个字符与一个常量 - 所以你肯定需要事先知道你要找的是什么。 第二个将其传递给一个闭包,在闭包中可以对其进行操作。 - Darklighter

6
我编写了一个函数,它返回&str的开头和剩余部分:
fn car_cdr(s: &str) -> (&str, &str) {
    for i in 1..5 {
        let r = s.get(0..i);
        match r {
            Some(x) => return (x, &s[i..]),
            None => (),
        }
    }

    (&s[0..0], s)
}

使用方法如下:

let (first_char, remainder) = car_cdr("test");
println!("first char: {}\nremainder: {}", first_char, remainder);

输出结果如下:
first char: t
remainder: est

对于超过1个字节的字符,它可以正常工作。


3
看起来这样会更简单 - Shepmaster
Shepmaster - 你的版本确实更简单。但是,我担心chars()函数——在我看来,它似乎扫描整个字符串并将其解析为向量或其他东西,而我的代码只查看字符串的前4个字符,最多。但是,也许我误解了chars()函数的工作原理? - Sean
抱歉,我的意思是“前4个字节”,而不是“前4个字符”。 - Sean
5
@Sean,你可能已经知道了,但chars()函数返回一个迭代器。迭代器是惰性评估的(而且是零成本的,意味着编译器在编译时重写它们),因此它应该是相当高效的。 - Hutch Moore

3

我认为这相当简单明了

let text = "hello world!";
let c: char = text.chars().next().unwrap();

next() 函数从迭代器中获取下一个元素
在 Rust 中,“unwrap” 表示“给我计算的结果,如果出现错误,就会产生异常并停止程序。”


2

如何在不使用字符串的其余部分的情况下获取字符串的第一个字符:

let text = "hello world!";
let ch = text.chars().take(1).last().unwrap();

1
希望在类似于Haskell的情况下有类似于head函数tail函数的东西。
我写了这个函数,它像headtail一起工作(但不完全匹配实现)。
pub fn head_tail<T: Iterator, O: FromIterator<<T>::Item>>(iter: &mut T) -> (Option<<T>::Item>, O) {
    (iter.next(), iter.collect::<O>())
}

使用方法:

// works with Vec<i32>
let mut val = vec![1, 2, 3].into_iter(); 
println!("{:?}", head_tail::<_, Vec<i32>>(&mut val));
// works with chars in two ways
let mut val = "thanks! bedroom builds YT".chars();
println!("{:?}", head_tail::<_, String>(&mut val));
// calling the function with Vec<char>
let mut val = "thanks! bedroom builds YT".chars();
println!("{:?}", head_tail::<_, Vec<char>>(&mut val));

注意:head_tail函数在迭代器为空时不会引发panic!。如果这与Haskell的head/tail输出匹配,则在迭代器为空时将引发异常。使用可迭代特性可能也是更兼容其他类型的好方法。


-2

被接受的答案有点丑陋!

let text = "hello world!";

let ch = &text[0..1]; // this returns "h"

34
对于非ASCII数据,这个回答是完全错误的。请尝试使用&"日本語"[0..1] - Shepmaster
4
这在书中的那一章中简单介绍了("world would be a slice that contains a pointer to the 6th byte of s and a length value of 5",重点标出),稍后在更详细的内容中会有更详细的讲解。 - Shepmaster
7
这可能不是原帖作者想要的,但它帮助我将第一个字符作为字符串获取了。正是我想要的!感谢@FeFiFoFu :) - Prajwal
1
@Prajwal,这只能工作在ASCII编码的字符串中,因为每个字符都被视为1个字节。然而,如果你的字符串使用多个字节来编码一个字符,那么这种方法将无法有效地工作,并且Rust会在分割字符字节时出现错误。因此,这种方法并不是高度可靠和安全的选择。 - andersonjwan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接