• rust学习——操作字符串、字符串转义、操作UTF8-字符串 (操作中文字符串)


    操作字符串

    在这里插入图片描述

    由于 String 是可变字符串,下面介绍 Rust 字符串的修改,添加,删除等常用方法:

    追加 (Push)

    在字符串尾部可以使用 push() 方法追加字符 char,也可以使用 push_str() 方法追加字符串字面量。这两个方法都是在原有的字符串上追加,并不会返回新的字符串。由于字符串追加操作要修改原来的字符串,则该字符串必须是可变的,即字符串变量必须由 mut 关键字修饰

    示例代码如下:

    fn main() {
        let mut s = String::from("Hello ");
    
        s.push_str("rust");
        println!("追加字符串 push_str() -> {}", s);
    
        s.push('!');
        println!("追加字符 push() -> {}", s);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    代码运行结果:

    追加字符串 push_str() -> Hello rust
    追加字符 push() -> Hello rust!
    
    • 1
    • 2

    插入 (Insert)

    在这里插入图片描述

    可以使用 insert() 方法插入单个字符 char,也可以使用 insert_str() 方法插入字符串字面量,与 push() 方法不同,这俩方法需要传入两个参数,第一个参数是字符(串)插入位置的索引,第二个参数是要插入的字符(串),索引从 0 开始计数,如果越界则会发生错误。由于字符串插入操作要修改原来的字符串,则该字符串必须是可变的,即字符串变量必须由 mut 关键字修饰

    示例代码如下:

    fn main() {
        let mut s = String::from("Hello rust!");
        s.insert(5, ',');
        println!("插入字符 insert() -> {}", s);
        s.insert_str(6, " I like");
        println!("插入字符串 insert_str() -> {}", s);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    代码运行结果:

    插入字符 insert() -> Hello, rust!
    插入字符串 insert_str() -> Hello, I like rust!
    
    • 1
    • 2

    替换 (Replace)

    在这里插入图片描述

    如果想要把字符串中的某个字符串替换成其它的字符串,那可以使用 replace() 方法。与替换有关的方法有三个。

    1、replace

    该方法可适用于 String&str 类型。replace() 方法接收两个参数,第一个参数是要被替换的字符串,第二个参数是新的字符串。该方法会替换所有匹配到的字符串。该方法是返回一个新的字符串,而不是操作原来的字符串

    示例代码如下:

    fn main() {
        let string_replace = String::from("I like rust. Learning rust is my favorite!");
        let new_string_replace = string_replace.replace("rust", "RUST");
        dbg!(new_string_replace);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    代码运行结果:

    new_string_replace = "I like RUST. Learning RUST is my favorite!"
    
    • 1
    2、replacen

    该方法可适用于 String&str 类型。replacen() 方法接收三个参数,前两个参数与 replace() 方法一样,第三个参数则表示替换的个数。该方法是返回一个新的字符串,而不是操作原来的字符串

    示例代码如下:

    fn main() {
        let string_replace = "I like rust. Learning rust is my favorite!";
        let new_string_replacen = string_replace.replacen("rust", "RUST", 1);
        dbg!(new_string_replacen);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    代码运行结果:

    new_string_replacen = "I like RUST. Learning rust is my favorite!"
    
    • 1
    3、replace_range

    该方法仅适用于 String 类型。replace_range 接收两个参数,第一个参数是要替换字符串的范围(Range),第二个参数是新的字符串。该方法是直接操作原来的字符串,不会返回新的字符串。该方法需要使用 mut 关键字修饰

    示例代码如下:

    fn main() {
        let mut string_replace_range = String::from("I like rust!");
        string_replace_range.replace_range(7..8, "R");
        dbg!(string_replace_range);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    代码运行结果:

    string_replace_range = "I like Rust!"
    
    • 1

    删除 (Delete)

    在这里插入图片描述

    与字符串删除相关的方法有 4 个,他们分别是 pop()remove()truncate()clear()。这四个方法仅适用于 String 类型。

    1、 pop —— 删除并返回字符串的最后一个字符

    该方法是直接操作原来的字符串。但是存在返回值,其返回值是一个 Option 类型,如果字符串为空,则返回 None。 示例代码如下:

    fn main() {
        let mut string_pop = String::from("rust pop 中文!");
        let p1 = string_pop.pop();
        let p2 = string_pop.pop();
        dbg!(p1);
        dbg!(p2);
        dbg!(string_pop);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    代码运行结果:

    p1 = Some(
       '!',
    )
    p2 = Some(
       '文',
    )
    string_pop = "rust pop 中"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2、 remove —— 删除并返回字符串中指定位置的字符

    该方法是直接操作原来的字符串。但是存在返回值,其返回值是删除位置的字符串,只接收一个参数,表示该字符起始索引位置。remove() 方法是按照字节来处理字符串的,如果参数所给的位置不是合法的字符边界,则会发生错误。

    示例代码如下:

    fn main() {
        let mut string_remove = String::from("测试remove方法");
        println!(
            "string_remove 占 {} 个字节",
            std::mem::size_of_val(string_remove.as_str())
        );
        // 删除第一个汉字
        string_remove.remove(0);
        // 下面代码会发生错误
        // string_remove.remove(1);
        // 直接删除第二个汉字
        // string_remove.remove(3);
        dbg!(string_remove);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    代码运行结果:

    string_remove 占 18 个字节
    string_remove = "试remove方法"
    
    • 1
    • 2

    3、truncate —— 删除字符串中从指定位置开始到结尾的全部字符

    该方法是直接操作原来的字符串。无返回值。该方法 truncate() 方法是按照字节来处理字符串的,如果参数所给的位置不是合法的字符边界,则会发生错误。

    示例代码如下:

    fn main() {
        let mut string_truncate = String::from("测试truncate");
        string_truncate.truncate(3);
        dbg!(string_truncate);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    代码运行结果:

    string_truncate = "测"
    
    • 1

    4、clear —— 清空字符串

    该方法是直接操作原来的字符串。调用后,删除字符串中的所有字符,相当于 truncate() 方法参数为 0 的时候。

    示例代码如下:

    fn main() {
        let mut string_clear = String::from("string clear");
        string_clear.clear();
        dbg!(string_clear);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    代码运行结果:

    string_clear = ""
    
    • 1

    连接 (Concatenate)

    在这里插入图片描述

    1、使用 + 或者 += 连接字符串

    使用 + 或者 += 连接字符串,要求右边的参数必须为字符串的切片引用(Slice)类型。其实当调用 + 的操作符时,相当于调用了 std::string 标准库中的 add() 方法,这里 add() 方法的第二个参数是一个引用的类型。因此我们在使用 +, 必须传递切片引用类型。不能直接传递 String 类型。+ 是返回一个新的字符串,所以变量声明可以不需要 mut 关键字修饰

    示例代码如下:

    fn main() {
        let string_append = String::from("hello ");
        let string_rust = String::from("rust");
        // &string_rust会自动解引用为&str
        let result = string_append + &string_rust;
        let mut result = result + "!"; // `result + "!"` 中的 `result` 是不可变的
        result += "!!!";
    
        println!("连接字符串 + -> {}", result);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    代码运行结果:

    连接字符串 + -> hello rust!!!!
    
    • 1

    add() 方法的定义:

    fn add(self, s: &str) -> String
    
    • 1

    因为该方法涉及到更复杂的特征功能,因此我们这里简单说明下:

    fn main() {
        let s1 = String::from("hello,");
        let s2 = String::from("world!");
        // 在下句中,s1的所有权被转移走了,因此后面不能再使用s1
        let s3 = s1 + &s2;
        assert_eq!(s3,"hello,world!");
        // 下面的语句如果去掉注释,就会报错
        // println!("{}",s1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    selfString 类型的字符串 s1,该函数说明,只能将 &str 类型的字符串切片添加到 String 类型的 s1 上,然后返回一个新的 String 类型,所以 let s3 = s1 + &s2; 就很好解释了,将 String 类型的 s1&str 类型的 s2 进行相加,最终得到 String 类型的 s3

    由此可推,以下代码也是合法的:

    let s1 = String::from("tic");
    let s2 = String::from("tac");
    let s3 = String::from("toe");
    
    // String = String + &str + &str + &str + &str
    let s = s1 + "-" + &s2 + "-" + &s3;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    String + &str返回一个 String,然后再继续跟一个 &str 进行 + 操作,返回一个 String 类型,不断循环,最终生成一个 s,也是 String 类型。

    s1 这个变量通过调用 add() 方法后,所有权被转移到 add() 方法里面, add() 方法调用后就被释放了,同时 s1 也被释放了。再使用 s1 就会发生错误。这里涉及到所有权转移(Move)的相关知识。

    2、使用 format! 连接字符串

    format! 这种方式适用于 String&strformat! 的用法与 print! 的用法类似,详见格式化输出

    示例代码如下:

    fn main() {
        let s1 = "hello";
        let s2 = String::from("rust");
        let s = format!("{} {}!", s1, s2);
        println!("{}", s);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    代码运行结果:

    hello rust!
    
    • 1

    字符串转义

    我们可以通过转义的方式 \ 输出 ASCII 和 Unicode 字符。

    fn main() {
        // 通过 \ + 字符的十六进制表示,转义输出一个字符
        let byte_escape = "I'm writing \x52\x75\x73\x74!";
        println!("What are you doing\x3F (\\x3F means ?) {}", byte_escape);
    
        // \u 可以输出一个 unicode 字符
        let unicode_codepoint = "\u{211D}";
        let character_name = "\"DOUBLE-STRUCK CAPITAL R\"";
    
        println!(
            "Unicode character {} (U+211D) is called {}",
            unicode_codepoint, character_name
        );
    
        // 换行了也会保持之前的字符串格式
        // 使用\忽略换行符
        let long_string = "String literals
                            can span multiple lines.
                            The linebreak and indentation here ->\
                            <- can be escaped too!";
        println!("{}", long_string);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    输出

    What are you doing? (\x3F means ?) I'm writing Rust!
    Unicode character ℝ (U+211D) is called "DOUBLE-STRUCK CAPITAL R"
    String literals
                            can span multiple lines.
                            The linebreak and indentation here -><- can be escaped too!
    
    • 1
    • 2
    • 3
    • 4
    • 5

    当然,在某些情况下,可能你会希望保持字符串的原样,不要转义:

    fn main() {
        println!("{}", "hello \\x52\\x75\\x73\\x74");
        let raw_str = r"Escapes don't work here: \x3F \u{211D}";
        println!("{}", raw_str);
    
        // 如果字符串包含双引号,可以在开头和结尾加 #
        let quotes = r#"And then I said: "There is no escape!""#;
        println!("{}", quotes);
    
        // 如果还是有歧义,可以继续增加,没有限制
        let longer_delimiter = r###"A string with "# in it. And even "##!"###;
        println!("{}", longer_delimiter);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    输出

    hello \x52\x75\x73\x74
    Escapes don't work here: \x3F \u{211D}
    And then I said: "There is no escape!"
    A string with "# in it. And even "##!
    
    • 1
    • 2
    • 3
    • 4

    操作 UTF-8 字符串

    前文提到了几种使用 UTF-8 字符串的方式,下面来一一说明。

    字符

    如果你想要以 Unicode 字符的方式遍历字符串,最好的办法是使用 chars 方法,例如:

    for c in "中国人".chars() {
        println!("{}", c);
    }
    
    • 1
    • 2
    • 3

    输出如下

    中
    国
    人
    
    • 1
    • 2
    • 3

    字节

    这种方式是返回字符串的底层字节数组表现形式:

    for b in "中国人".bytes() {
        println!("{}", b);
    }
    
    • 1
    • 2
    • 3

    输出如下:

    228
    184
    173
    229
    155
    189
    228
    186
    186
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    获取子串

    想要准确的从 UTF-8 字符串中获取子串是较为复杂的事情,例如想要从 holla中国人नमस्ते 这种变长的字符串中取出某一个子串,使用标准库你是做不到的。 你需要在 crates.io 上搜索 utf8 来寻找想要的功能。

    可以考虑尝试下这个库:utf8_slice

    use utf8_slice;
    fn main() {
        let s = "The 🚀 goes to the 🌑!";
    
        let rocket = utf8_slice::slice(s, 4, 5);
        // 结果是 "🚀"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    练习题
    练习题答案

  • 相关阅读:
    SSD程序
    [Codeforces] number theory (R1600) Part.10
    Internet Download Manager6.41提速下载器安装下载教程
    【WINDOWS / DOS 批处理】8.3命名规则
    Android布局转图片Bitmap
    尚硅谷设计模式学习(四)工厂模式
    基于CNN卷积神经网络的TensorFlow+Keras深度学习的人脸识别
    R基本数据类型
    【学习方法论】学习的三种境界、三种习惯、三个要点,三个心态
    Pycharm中出现ImportError:DLL load failed:找不到指定模块的解决方法
  • 原文地址:https://blog.csdn.net/e891377/article/details/133923652