rust字符串

这篇具有很好参考价值的文章主要介绍了rust字符串。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

字符串类型

诸位在入门rust的时候,要认真,因为字符串类型在rust中有好几种,一不小心就搞混了类型,导致代码编译报错。好在有强大的rust-analyzer和vscode帮助我们。我们直接通过一段代码来开始认识rust的字符串类型。

fn main() {
    let s = "Hello, Rust string!";      // s是&str类型
    println!("{s}");

    let s = "Hello, Rust string!".to_string();  // s是String类型
    println!("{s}");
    
    let s1 = &s;                    // &String类型
    println!("{s1}");
    
    let s2 = &s[0..5];              // &str类型
    println!("{s2}");

    // let s1 = s[0..5];       error 这应该是什么类型?是str吗?那么如何使用str类型?
}

字符串切片引用类型(&str)

首先,我们还是从字符串字面值来谈起,在rust中,字符串字面值常量的类型是&str,这个类型称之为“字符串切片引用”。字符串字面值是特殊的,它实际上存储在可执行程序的只读内存段中(rodata)。通过&str可以引用rodata中的字符串。同样,对于在堆上存放的字符串String类型,也可以通过&str来引用其中的部分。就和python的切片类似。但是如果想要直接使用str类型,是不行的,只能通过Box<str>来使用。例如上面通过切片引用&s[0..5]来使用s的第1个字节到第5个字节的内容。索引下标是从0开始的,范围是区间[0, 5),注意这个区间是左闭右开的。例如:

let s = String::from("hello");
let slice = &s[2..5];
println!("{slice}");

这段代码中的slice是&str类型,切片引用了s的第3个字节到第5个字节的内容。即输出llo,在rust的切片中,下标也不能超过字符串长度的边界,否则会导致运行时错误。例如:

let s = String::from("hello");
let slice = &s[2..8];
println!("{slice}");

这段代码在执行的时候会直接panic,异常信息如下所示:

thread 'main' panicked at 'byte index 8 is out of bounds of `hello`', src/main.rs:23:18
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

它告诉我们索引8超出了"hello"的界限。我们再次尝试让起始索引越界。

let s = String::from("hello");
let slice = &s[-1..5];
println!("{slice}");

这次将无法通过编译,编译时报错如下。

rust要求索引必须是usize类型的,这意味着索引不能是负数。另外,如果起始索引是0,可以简写为&s[..3];同样如果终止索引是String的最后一个字节,那么可以简写为&s[1..];因此如果要引用整个String,那么可以简写为&s[..]。下面是一个简单的演示,它们每组都是等价的。

let slice = &s[0..3];
println!("{slice}");
let slice = &s[..3];
println!("{slice}");

let len = s.len();
let slice = &s[1..len];
println!("{slice}");
let slice = &s[1..];
println!("{slice}");

let slice = &s[0..len];
println!("{slice}");
let slice = &s[..];
println!("{slice}");

最后,字符串切片引用的索引必须落在字符之间的边界位置,但是由于rust的字符串是UTF-8编码的,因此必须要小心。后文会讲述如何取出UTF-8编码的一个字符。切片类型是对集合的部分引用,不仅有字符串切片引用,其它的集合类型也有。

字符串类型(String)

Rust 中的字符是 Unicode 类型,因此每个字符占据 4 个字节内存空间,但是在字符串中不一样,字符串是 UTF-8 编码,也就是字符串中的字符所占的字节数是变化的(1 - 4)

Rust 在语言级别,只有一种字符串类型: str,它通常是以引用类型出现 &str,也就是上文提到的字符串切片引用。虽然语言级别只有上述的 str 类型,但是在标准库里,还有多种不同用途的字符串类型,其中使用最广的即是 String 类型。

前面说个,str类型被存放在了rodata区域,无法被修改。而String是一个可增长,可变且具有所有权的utf-8编码的字符串。

String和&str的相互转换

前文已经看到了如何将&str转为String,例如:

let s = String::from("hello");
let s = "hello".to_string();

将String转为&str前面也演示了,例如:

let s = String::from("hello");
let slice = &s[..2];
println!("{slice}");    // 直接打印,没有解引用。

其中实际上还有一个问题,可能有部分读者已经注意到了,那就是我们直接打印了slice这个切片引用,而没有解引用。实际上这是因为deref 隐式强制转换,这是由编译器帮我们完成的。而且你不能直接使用str类型。如果手动解引用,会引起编译错误。

不能使用字符串索引

由于rust的字符串类型是utf-8编码的,如果允许使用索引来取出字符串中的某个字符,那么这将牺牲一部分性能,而rust期望索引操作的时间复杂度是O(1)。因此,你不能像python那样使用索引去访问第n个字符。当然rust提供了其它方式来获取其中某个字符。例如chars方法。

操作字符串

操作字符串,主要是针对String类型来进行的。无非就是涉及到增删改查。

创建String字符串

通过官方文档可以得知,我们可以有下面三种方式从字符串字面值来创建一个新的String字符串。

let s = "Hello".to_string();
let s = String::from("world");
let s: String = "also this".into();

追加

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

fn main() {
    let mut s = String::from("Hello ");
    s.push('r');
    println!("追加字符 push() -> {}", s);

    s.push_str("ust!");
    println!("追加字符串 push_str() -> {}", s);
}

插入

可以使用 insert() 方法插入单个字符 char,也可以使用 insert_str() 方法插入字符串字面量。例如:

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);
}

这俩方法需要传入两个参数,第一个参数是字符(串)插入位置的索引,第二个参数是要插入的字符(串),索引从 0 开始计数,如果越界则会发生错误。它们都是在原字符串上做修改,因此原字符串必须可变。

替换

  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);
        let s = "12345";
        let new_s = s.replace("3", "t");
        dbg!(new_s); 
    }
    

    dbg!是rust提供的调试使用的宏,方便rust使用者进行打印输出。它会打印出其所在的文件,代码行,变量名。非常便于调试。

  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);
    }
    
  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);
    }
    

    这个方法在字符串中的字符都是由ASCII组成的时候,是非常有用的。但是如果存在非ASCII编码的字符时,就需要计算出正确的utf-8字符的起始位置和结束位置,否则会造成运行时错误

删除

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

  1. pop

    删除并返回字符串的最后一个字符(按字符处理,不是字节),该方法是直接操作原来的字符串。但是存在返回值,其返回值是一个 Option 类型,如果字符串为空,则返回 None。我们在这里都是用dbg!这个宏来打印的,他能够将Option<char>类型打印出来,后面我们再来介绍Option类型。

    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);
    }
    
  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);
    }
    
  3. truncate

    删除字符串中从指定位置开始到结尾的全部字符,该方法是直接操作原来的字符串。无返回值。该方法 truncate() 方法是按照字节来处理字符串的,如果参数所给的位置不是合法的字符边界,则会发生错误。例如:

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

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

    fn main() {
        let mut string_clear = String::from("string clear");
        string_clear.clear();
        dbg!(string_clear);
    }
    

连接

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

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

    fn main() {
        let string_append = String::from("hello ");
        let string_rust = String::from("rust");
        // // &string_rust会自动解引用为&str,这是因为deref coercing特性。这个特性能够允许把传进来的&String,在API执行之前转成&str。
        let result = string_append + &string_rust;
        let mut result = result + "!";
        result += "!!!";
    
        println!("连接字符串 + -> {}", result);
    }
    

    这里出现了deref coercing这个特性,这是为了使用起来更方便,但是个人认为deref coercing是一个不一致性设计。另外一点是add函数的函数原型是fn add(self, s: &str) -> String,self 是 String 类型的字符串,因此string_append在经过连接运算(+)之后,转移了所有权,导致string_append被释放。

  2. 使用 format! 连接字符串

    format! 这种方式适用于 String 和 &str,和C/C++提供的sprintf函数类似。例如:

    fn main() {
        let s1 = "hello";
        let s2 = String::from("rust");
        let s = format!("{} {}!", s1, s2);
        println!("{}", s);
    }
    

    format宏使用起来更加方便,而且你可以自由的连接多个字符串,并且由于宏使用的是引用,因此不会发送所有权的转移。

标准库中String和&str有非常多的方法,可以在rust官方文档中进行查看。rust的官方文档编写的算是非常Nice的,几乎每个函数都有例子。
rust字符串
在这里,你能几乎能找到关于rust的一切。最常用的可能就是标准库,cargo书册和编译错误索引表。

关于字符串的其他部分

  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
        );
    }
    
  2. 将一个物理行字符串拆分到多行

    使用\连接两行(多行)为一个物理行。

    fn main() {
        let long_string = "String literals
                        can span multiple lines.
                        The linebreak and indentation here \	
                        can be escaped too!";
        println!("{}", long_string);
    }
    

    这段代码输出如下所示:
    rust字符串
    String literals和can span multiple lines.是分开的两行,而The linebreak and indentation here和can be escaped too!由\连接为一个物理行。

  3. 原始字符串

    现在的大多数语言中都提供了原始字符串的语法,rust也不例外,在字符串前面加上小写字母r,那么字符串在被使用的时候将不会发生转义。

    fn main() {
        println!("{}", "hello \x52\x75\x73\x74");           // 输出hello Rust
        let raw_str = r"Escapes don't work here: \x3F \u{211D}";    // 原始字符串
        println!("{}", raw_str);        // 输出Escapes don't work here: \x3F \u{211D}
    }
    
  4. 字符中出现双引号

    在C/C++中,字符串中出现双引号的时候,需要进行转义;rust提供了r#这种方式来解决这个问题,当然你也可以使用转义的方式。

    fn main() {
        // 如果字符串包含双引号,可以在开头和结尾加 #
        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);
    }
    

字符串和字符数组

由于rust的字符串是uft-8编码的,而String类型不允许以字符为单位进行索引。有时候会不方便。String提供了chars()方法遍历字符以及bytes()方法遍历字节。另外就是,如果需要从String中获取子串是比较困难的,标准库没有提供相关的方法。 你需要在 crates.io 上搜索 utf8 来寻找想要的功能。可以使用utf8_slice这个库。当然了另一种解决方案就是使用字符数组,这样就可以非常方便的进行操作了。文章来源地址https://www.toymoban.com/news/detail-431605.html

参考资料

  1. rust圣经
  2. rust标准库String
  3. rust标准库str

到了这里,关于rust字符串的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • python教程 入门学习笔记 第4天 数据类型 获取数据类型 字符串拼接

    数据类型 1、能直接处理的基本数据类型有5个:整型、浮点型、字符串、布尔值、空 1)整型(int)=整数,例如0至9,-1至-9,100,-8180等,人数、年龄、页码、门牌号等 没有小数位的数字,是整型 2)浮点型(float)=小数,例如金额、身高、体重、距离、长度、π等 精确到小

    2024年02月14日
    浏览(54)
  • 【Python零基础学习入门篇③】——第三节:Python的字符串类型

    ⬇️⬇️⬇️⬇️⬇️⬇️ ⭐⭐⭐Hello,大家好呀我是陈童学哦,一个普通大一在校生,请大家多多关照呀嘿嘿😁😊😘 🌟🌟🌟 技术这条路固然很艰辛,但既已选择,该当坚毅地走下去,加油! 🌤️PUA: ” 你所看到的惊艳都曾平庸历练 **“**🚀🚀🚀 🍉🍉🍉 最后让我

    2024年02月04日
    浏览(46)
  • 联表查询的时候外键id是字符串

    2024年02月09日
    浏览(40)
  • Rust学习-字符串

    Rust核心语言只有一种字符串类型:str,字符串 slice,通常以被借用的形式出现,str。 它们是一些储存在别处的 UTF-8 编码字符串数据的引用。 比如字符串字面量被储存在程序的二进制输出中,即可执行程序的只读内存段中(rodata),字符串 slice 也是如此。 String 是由标准库提

    2024年02月16日
    浏览(40)
  • rust字符串

    诸位在入门rust的时候,要认真,因为字符串类型在rust中有好几种,一不小心就搞混了类型,导致代码编译报错。好在有强大的rust-analyzer和vscode帮助我们。我们直接通过一段代码来开始认识rust的字符串类型。 首先,我们还是从字符串字面值来谈起,在rust中,字符串字面值常

    2024年02月02日
    浏览(33)
  • Rust 笔记:Rust 语言中的字符串

    Rust 笔记 Rust 语言中的字符串 作者 : 李俊才 (jcLee95):https://blog.csdn.net/qq_28550263?spm=1001.2101.3001.5343 邮箱 : 291148484@163.com 本文地址 :https://blog.csdn.net/qq_28550263/article/details/130876665 【介绍】:本文介绍 Rust 语言中的字符和字符串的用法。 上一节:《 Rust 语言中使用 vector(向

    2024年02月06日
    浏览(53)
  • rust 字符串(String)详解

    rust中的 String ,是一个非常常用的 crate ,它的底层涉及到了rust中的所有权概念,不过这不是本章的内容,如果对rust所有权概念感兴趣的,可以查看另一篇文章:rust所有权 本文的目的还是介绍 String 的基本用法,以及有哪些常用的函数可以使用 字符串,也就是由一系列字符

    2024年02月03日
    浏览(47)
  • 30天拿下Rust之字符串

    概述         在Rust中,字符串是一种非常重要的数据类型,用于处理文本数据。Rust的字符串是以UTF-8编码的字节序列,主要有两种类型:str和String。其中,str是一个对字符数据的不可变引用,更像是对现有字符串数据的“视图”,而String则是一个独立、可变更的字符串实

    2024年03月12日
    浏览(47)
  • 【编程】Rust语言入门第4篇 字符串

    Rust 中的字符是 Unicode 类型,因此每个字符占据 4 个字节内存空间,但字符串不一样,字符串是 UTF-8 编码,也就是字符串中的字符所占的字节数是变化的(1 - 4)。 常见的字符串有两种: str,通常是引用类型, str ,即字符串字面常量,字符串切片。 std::string::String 类型 str 的变

    2024年02月20日
    浏览(56)
  • rust里如何判断字符串是否相等呢?

    在 Rust 中,有几种方法可以判断字符串是否相等。下面是其中几种常见的方法: 使用 == 运算符:可以直接使用 == 运算符比较两个字符串是否相等。例如: 这段代码会输出 “字符串不相等”,因为 str1 和 str2 不是相同的字符串。 2. 使用 eq 方法:字符串类型实现了 PartialEq

    2024年02月14日
    浏览(62)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包