Rust - 字符串:str 与 String

news2025/1/11 20:50:18

在其他语言中,字符串通常都会比较简单,例如 “hello, world” 就是字符串章节的几乎全部内容了。

但是Rust中的字符串与其他语言有所不同,若带着其他语言的习惯来学习Rust字符串,将会波折不断。

所以最好先忘记脑中已有的关于字符串的知识,从头开始学习Rust字符串。

首先来看段逻辑简单的代码:

fn main() {
    let my_name = "Pascal";
    greet(my_name);
}

fn greet(name: String) {
    println!("Hello, {}!", name);
}

greet 函数接受一个字符串类型的 name 参数,然后打印到终端控制台中,非常好理解,那么这段代码能不能通过编译呢?

error[E0308]: mismatched types
 --> src/main.rs:3:11
  |
3 |     greet(my_name);
  |           ^^^^^^^
  |           |
  |           expected struct `std::string::String`, found `&str`
  |           help: try using a conversion method: `my_name.to_string()`

error: aborting due to previous error

编译后发现报错了。编译器提示 greet 函数需要一个 String 类型的字符串,却传入了一个 &str 类型的字符串。思考分析这个错误,它表明的信息是:我们写下的 let my_name = “Pascal”; 语句,my_name并不是 String 类型,而是 “&str” 类型。

相信如果按照其他编程语言的思路,此时就像遇到了无法理解的现象,我们需要抛开这种成见,从头开始学习Rust字符串的原理。

在学习字符串之前,先来看看什么是切片。

(一)Slice(切片)

如果你有其他编程语言的经验更好理解切片。

切片并不是 Rust 独有的概念,在 Go 语言中就非常流行。Rust切片它允许我们引用集合中部分连续的元素序列,而不是引用整个集合。即“从一个大蛋糕中切出一小块来”。

我们以数组为例,从a数组中引用“ [2, 3] ”,语句是这么写的:

let a = [1, 2, 3, 4, 5];
let slice = &a[1..3];
println!("{:?}", slice); // Rust 使用 {:?} 来打印数组切片
//执行结果:[2, 3]

请先不要纠结切片的语法,接下来我们会详细说明。

而对于 Rust 字符串的切片而言,从原理上讲即 “对 String 类型中某一部分的引用”,语句是这么写的:

let s = String::from("hello world");

let hello = &s[0..5];
let world = &s[6..11];

hello 没有引用整个 String s,而是引用了 s 的一部分内容,通过 [0…5] 的方式来指定。

这就是创建切片的语法,使用方括号包括的一个序列:[开始索引…终止索引],其中开始索引是切片中第一个元素的索引位置,而终止索引是最后一个元素后面的索引位置,也就是这是一个 右半开区间 [a, b)

在切片数据结构内部会保存开始的位置和切片的长度,其中长度是通过 终止索引 - 开始索引 的方式计算得来的。

对于 let world = &s[6…11]; 来说,world 是一个切片,该切片的指针指向 s 的第 7 个字节(索引从 0 开始, 6 是第 7 个字节),且该切片的长度是 5 个字节。

在使用 Rust 的 …range 序列语法 时,如果我们想从索引 0 开始,可以使用如下的方式,这两个写法是等效的:

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

let slice = &s[0..2];
let slice = &s[..2];

同样的,如果我们的的切片想要包含 String 的最后一个字节,则可以这样使用:

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

let len = s.len();

let slice = &s[4..len];
let slice = &s[4..];

我们也可以截取完整的 String 切片:

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

let len = s.len();

let slice = &s[0..len];
let slice = &s[..];

在对字符串使用切片语法时需要格外小心,切片的索引必须落在字符之间的边界位置,也就是 UTF-8 字符的边界。

例如中文在 UTF-8 中占用三个字节,下面的代码就会崩溃:

 let s = "中国人";
 let a = &s[0..2];
 println!("{}",a);  //索引是一个右半开区间 [a, b),[0..2]显然拿不到完整的字符

注意:slice 是一种引用,所以它没有所有权。

这点并不难理解,结合上面我们已经提到的例子,slice 它允许我们引用集合中部分连续的元素序列,而不是引用整个集合。

而引用并不涉及 所有权转移,所以它并没有所有权。

(二)字符串Slice:&str

现在我们已经了解了Rust中的切片。接下来我们要引出Rust中很重要的一个概念:在Rust中,字符串切片的类型标识就是 &str,而我们说的“字符串字面量”类型标识也是 &str

现在,我们就能回过头再审视一下我们最初写下的这段报错的代码:

fn main() {
    let my_name = "Pascal";
    greet(my_name);
}

fn greet(name: String) {
    println!("Hello, {}!", name);
}

编译器提示 greet 函数需要一个 String 类型的字符串,却传入了一个 &str 类型的字符串。

即:my_name的本质上其实是字符串字面量(&str类型),他并不是一个 动态字符串(String类型)。

也就是说,下面两行代码是写法不同但本质相同的代码:

let my_name = "Pascal";
let my_name: &str = "Pascal";

也许你会有一个问题,既然 my_name 是一个字符串切片引用,它引用的来源是谁呢,是从谁身上切的?

在 Rust 中,字符串字面量(如我们的 "Pascal")是在编译时由编译器处理的。当我们在代码中写入一个字符串字面量时,编译器会将其视为一个静态分配的字符串。这些字符串字面量通常存储在程序的只读数据段(在内存中)。

当你执行 let my_name = "Pascal"; 时,my_name 是一个对字符串字面量的引用。这个引用是一个字符串切片(&str),它指向存储在只读内存段中的 “Pascal” 字符串。

字符串切片 &str 是一个引用,它不拥有数据,只是指向数据。

切片指向了程序可执行文件中的某个点,这也是为什么字符串字面量是不可变的,因为 &str 是一个引用,引用是不能改变它的数据的。

根据这些内容,我们小小修改一下代码,就可以执行成功了:

fn main() {
    let my_name = "Pascal";
    greet(my_name);
}

fn greet(name: &str) {
    println!("Hello, {}!", name);
}
//Hello, Pascal!

所有权、引用、借用、slice 这些概念都是为了让 Rust 程序在编译时确保了内存安全。

(三)Rust字符串的定义

顾名思义,字符串是由字符组成的连续集合,但是在上一节中我们提到过,Rust 中的字符是 Unicode 类型,因此每个字符占据 4 个字节内存空间,但是在字符串中不一样,字符串是 UTF-8 编码,也就是字符串中的字符所占的字节数是变化的(1 - 4),这样有助于大幅降低字符串所占用的内存空间。

Rust 在语言级别上,只有一种字符串类型: str

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

str 类型是硬编码进可执行文件,也无法被修改,但是 String 则是一个可增长、可改变且具有所有权的 UTF-8 编码字符串(动态字符串),当 Rust 用户提到字符串时,往往指的就是 Strin 类型和 &str 字符串切片类型,这两个类型都是 UTF-8 编码

除了 String 类型的字符串,Rust 的标准库还提供了其他类型的字符串,例如 OsString,OsStr,CsString 和CsStr 等,注意到这些名字都以 String 或者 Str 结尾了吗?

  • OsString 拥有字符串数据,而 OsStr 是其对应的借用类型。
  • CsString 拥有字符串数据,而 CsStr 是其对应的借用类型。

(四)动态字符串:String

(1)String 与 &str 的转换

在之前的代码中,已经见到好几种从 &str 类型生成 String 类型的操作:

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

那么如何将 String 类型转为 &str 类型呢?答案很简单,取引用即可:

fn main() {
    let s = String::from("hello,world!");
    say_hello(&s);
    say_hello(&s[..]);
    say_hello(s.as_str()); //s.as_str() 创建了一个s字符串的引用
}

fn say_hello(s: &str) {
    println!("{}",s);
}

//结果:
//hello,world!
//hello,world!
//hello,world!
(2)字符串索引

在其它语言中,使用索引的方式访问字符串的某个字符或者子串是很正常的行为,但是在 Rust 中就会报错:

let s1 = String::from("hello");
let h = s1[0];
//报错:`String` cannot be indexed by `{integer}`

这是因为 Rust 强调内存安全和数据完整性。在 Rust 中,字符串是以 UTF-8 编码存储的,这意味着一个字符可能由多个字节组成。直接通过字节索引访问可能会导致字符串在逻辑上被错误地分割,从而破坏字符的完整性。

而要在 Rust 中安全地访问字符串中的字符或子串,我们可以使用以下几种方法,简单了解一下:

  1. 使用 chars() 方法:这个方法返回一个迭代器,迭代器中的每个元素都是字符串中的一个 Unicode 字符。你可以通过迭代器来访问每个字符。
let s = "hello";
for c in s.chars() {
    println!("{}", c);
}
  1. 使用 char_indices() 方法:这个方法返回一个迭代器,迭代器中的元素是 (byte_index, char) 形式的元组,其中 byte_index 是字符在字符串中的字节索引。
let s = "hello";
for (i, c) in s.char_indices() {
    println!("{}: {}", i, c);
}
  1. 使用 get() 方法:这个方法允许你通过字节索引来安全地访问字符串中的字符。如果索引有效,它会返回 Some(&char),否则返回 None。
let s = "hello";
if let Some(c) = s.get(0) {
    println!("{}", c);
} else {
    println!("Index out of bounds");
}
  1. 使用切片语法:如果你需要访问子串,可以使用切片语法 &s[start…end]。这里的 start 和 end 是字节索引,但 Rust 会确保切片操作不会在字符边界上切割。
let s = "hello";
let slice = &s[0..2]; // "he"
println!("{}", slice);
  1. 使用 split_off() 方法:如果你需要从字符串中分离出一部分,可以使用 split_off() 方法,它会返回一个新的 String,包含从指定索引开始的所有字符。
let mut s = String::from("hello");
let part = s.split_off(2); // s 现在是 "he", part 是 "llo"
println!("s: {}, part: {}", s, part);
(3)字符串操作

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

(a)追加

在字符串尾部可以使用 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);
}

//运行结果:
//追加字符串 push_str() -> Hello rust
//追加字符 push() -> Hello rust!
(b)插入

可以使用 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);
}

//运行结果:
//插入字符 insert() -> Hello, rust!
//插入字符串 insert_str() -> Hello, I like rust!
(c)替换

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

  • replace
  • replacen
  • replace_range

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");
    println!("{}",new_string_replace);
}

//运行结果:
//I like RUST. Learning RUST is my favorite!

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);
    println!("{}",new_string_replace);
}

//运行结果:
//I like RUST. Learning rust is my favorite!

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");
    println!("{}",new_string_replace);
}
//运行结果:
//I like Rust!
(d)删除

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

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

/*
p1 = Some(
   '!',
)
p2 = Some(
   '文',
)
string_pop = "rust pop 中"
*/

PS:dbg!() 宏是一个非常有用的调试工具。它允许你在运行时打印变量的值,而不需要添加任何额外的打印语句。

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

//string_remove 占 18 个字节
//string_remove = "试remove方法"

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

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

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

//string_truncate = "测"

clear —— 清空字符串

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

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

//string_clear = ""
(e)连接

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

使用 + 或者 += 操作符连接字符串时,要求操作符右边的参数必须为字符串的切片引用(Slice)类型。

其实当调用 + 的操作符时,相当于调用了 std::string 标准库中的 add() 方法,这里 add() 方法的第二个参数是一个引用的类型。因此我们在使用 + 时, 必须传递切片引用类型,不能直接传递 String 类型。

+ 是返回一个新的字符串,所以变量声明可以不需要 mut 关键字修饰

fn main() {
    let string_append = String::from("hello ");
    let string_rust = String::from("rust");
    let result = string_append + &string_rust; // &string_rust会自动解引用为&str
    let result = result + "!"; // `result + "!"` 中的 `result` 是不可变的

    println!("连接字符串 + -> {}", result);
}

//连接字符串 + -> hello rust!

使用 format! 连接字符串

format! 这种方式适用于 String&strformat! 的用法与 print! 的用法类似。

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

//hello rust!

(五)String与&str总结

在 Rust 中,String&str 都是处理字符串数据的类型,但它们之间的关系和用途有所不同。以下是它们之间的关系和区别:

  1. 所有权与借用
    • String 拥有其数据,它是一个动态分配在堆上的可变字符串类型。这意味着 String 可以增长和缩小,并且可以被修改。
    • &str 是一个字符串切片,它是一个对字符串数据的不可变引用。它不拥有数据,而是借用了其他字符串数据的一部分或全部。
  2. 内存管理
    • String 需要管理其在堆上的内存,因此它需要负责分配和释放内存。这使得 String 可以动态地改变其大小。
    • &str 只是一个引用,它不需要管理内存,因为它指向的数据可能来自 String、字符串字面量或其他数据源。
  3. 可变性
    • String 是可变的,你可以更改其内容,如添加、删除或修改字符。
    • &str 是不可变的,你不能更改它指向的数据。如果你需要修改数据,你必须在 String 上进行操作。
  4. 使用场景
    • 当你需要一个可以修改的字符串时,使用 String
    • 当你需要读取或处理字符串数据,但不需要修改它时,使用 &str
  5. 转换
    • 你可以从 String 创建一个 &str 引用,只需通过引用 String 实例即可,如 &my_string
    • 你也可以从字符串字面量创建一个 &str 引用,因为字符串字面量本身就是 &str 类型。
    • &str 创建 String 可以通过 to_string() 方法或 String::from() 函数,这涉及到从不可变引用到可变数据的转换,可能需要复制数据。
  6. 生命周期
    • String 拥有其数据,因此它不受生命周期的限制。
    • &str 是一个引用,它依赖于数据的生命周期。如果 &str 引用的数据超出了作用域,那么 &str 引用将不再有效。

总的来说,String&str 在 Rust 中提供了灵活的字符串处理机制,允许你根据需要选择最合适的类型。String 提供了可变性和动态内存管理,而 &str 提供了对字符串数据的不可变引用,这两者在不同的场景下都非常有用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2159746.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MMD模型一键完美导入UE5-VRM4U插件方案(一)

1、下载pmx模型 1、去模之屋官网下载MMD模型,模之屋 2、下载完成得到pmx和Texture文件 2、下载并启用VRM4U插件 1、下载VRM4U插件, VRM4U,点击Latest下载对应引擎版本 2、将插件放到Plugins目录,然后

GB28181语音对讲协议详解

GB28181-2016语音对讲流程如下图1所示: 图1.语音对讲流程。 其中, 信令 1 、2 、 3 、 4 为语音广播通知、 语音广播应答消息流程; 信令 5 、 1 2 、 1 3 、 1 4 、 1 5 、 1 6 为 S I P 服务器接收到客户端的呼叫请求通过 B 2 B UA 代理方式建立语音流接收者与媒…

DevExpress WPF中文教程:如何解决行焦点、选择的常见问题?

DevExpress WPF拥有120个控件和库,将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序,这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 无论是Office办公软件…

【HarmonyOS】应用权限原理和封装

背景 在项目中,避免不了需要调用系统资源和系统能力,比如:日历读写、摄像头等。因此,需要了解对系统资源访问权限的申请方式方法。 授权方式 包括两种授权方式,分别是system_grant(系统授权) 和 user_grant(用户授权)…

ruoyi源码解析学习 - 微服务版 - ruoyi-gateway

com.ruoyi.gateway 今天简单看看若依的gateway的配置模块干了啥 最近面试很多外包公司,都对低代码平台有点要求,这些代码虽说用起来不费劲,但是其中还是有很多细节能让我学习学习的。(微服务版,上次搞jeecgboot的笔试…

R包安装教程,如何安装rjags和infercnv

一.介绍 在数据分析过程中,R语言因其强大的统计分析能力和丰富的包生态系统,成为众多研究人员和数据科学家的首选工具。本文将详细介绍如何在R环境中安装两个重要的R包——rjags和infercnv。rjags用于与JAGS(Just Another Gibbs Sampler&…

热斑黄斑光伏发电板 红外黄斑检测图像数据集内含最高温度信息 1200张,jpg格式。

热斑黄斑光伏发电板 红外黄斑检测图像数据集 内含最高温度信息 1200张,jpg格式。 热斑黄斑光伏发电板红外黄斑检测图像数据集介绍 数据集名称 热斑黄斑光伏发电板红外黄斑检测图像数据集(Hot Spot and Yellow Spot Detection in Photovoltaic Panels I…

Linux下进程通信与FIFO操作详解

Linux下进程通信与FIFO操作详解 一、命名管道(FIFO)概述1.1 命名管道的特点1.2 创建命名管道二、命名管道的操作2.1 打开命名管道2.2 读写命名管道2.3 关闭命名管道三、命名管道的使用实例3.1 命名管道的创建和通信过程3.1.1 发送方(writer)3.1.2 接收方(reader)3.2 运行…

java项目之健身房管理系统源码(springboot)

风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的健身房管理系统。项目源码以及部署相关请联系风歌,文末附上联系信息 。 项目简介: 健身房管理系统的主要使用…

Java框架学习(Spring)(tx)(03)

简介:以本片记录在尚硅谷学习ssm-spring-tx时遇到的小知识 详情移步:想参考的朋友建议全部打开相互配合学习! 视频: 057-spring-tx-编程式和声明式事务理解_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1AP411s7D7?p5…

第九节 Opencv自带颜色表操作

知识点:Look Up lTable(LUT)查找表 了解LUT查找表的作用与用法,代码实现与API介绍 -applyColorMap(src,dst,COLORMAP) -src表示输入图像 -dst表示输出图像 匹配到的颜色LUT,Opencv支持13种…

Android OpenGLES2.0开发(一):艰难的开始

生而为人,本质上,都是孤独的! 引言 我一直觉得OpenGL ES是一块硬骨头,每次用到GLSurfaceView作为Camera的预览视图时,总是去网上找现成的代码。CtrlC和CtrlV之后总有一种沾沾自喜的感觉,但是你要让我改里面…

JavaScript --- 字符串常用方法(1)

chartAt(索引)&#xff0c;返回索引对应字符 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" …

Java | Leetcode Java题解之第433题最小基因变化

题目&#xff1a; 题解&#xff1a; class Solution {public int minMutation(String start, String end, String[] bank) {int m start.length();int n bank.length;List<Integer>[] adj new List[n];for (int i 0; i < n; i) {adj[i] new ArrayList<Intege…

chsharp文件如何查找在unity中使用的 位置?

在 Unity 中&#xff0c;C 脚本文件&#xff08;.cs 文件&#xff09;被用于控制游戏对象的行为。要查找某个 C 文件在 Unity 项目中被使用的位置&#xff0c;你可以通过以下几种方法&#xff1a; 1. 查找依赖项&#xff08;References&#xff09; Unity 提供了一个工具&#…

物联网助力智慧交通:优势与前景

智慧交通是当今城市发展的必然趋势&#xff0c;而物联网技术在交通运输领域的应用正是为实现智慧交通建设提供了前所未有的机遇和优势。物联网作为连接和控制物理世界的重要技术手段&#xff0c;在交通领域的应用极大地改善了交通系统的效率、安全性和环保性。 首先&#xff0c…

LabVIEW软件出现Bug如何解决

在LabVIEW开发中&#xff0c;程序出现bug是不可避免的。无论是小型项目还是复杂系统&#xff0c;调试与修复bug都是开发过程中的重要环节。下文介绍如何有效解决LabVIEW软件中的bug&#xff0c;包括常见错误类型、调试工具、错误处理机制。 1. 常见Bug类型分析 在LabVIEW中&am…

使用vite+react+ts+Ant Design开发后台管理项目(二)

前言 本文将引导开发者从零基础开始&#xff0c;运用、react、react-router、react-redux、Ant Design、less、tailwindcss、axios等前沿技术栈&#xff0c;构建一个高效、响应式的后台管理系统。通过详细的步骤和实践指导&#xff0c;文章旨在为开发者揭示如何利用这些技术工具…

C语言 | Leetcode C语言题解之第429题N叉树的层序遍历

题目&#xff1a; 题解&#xff1a; #define MAX_LEVE_SIZE 1000 #define MAX_NODE_SIZE 10000int** levelOrder(struct Node* root, int* returnSize, int** returnColumnSizes) {int ** ans (int **)malloc(sizeof(int *) * MAX_LEVE_SIZE);*returnColumnSizes (int *)mal…

如何利用 opencv 进行 ROI(感兴趣)获取和实现 VR(虚拟现实) 演播室的播放

我是从事医疗软件的开发的。 经常需要从拍摄的医疗视频中获取出病理区域。并计算病理区域的周长和面积。 用 opencv 的术语,这就是感兴趣区域的获取。 (因为都是实时视频,所以速度很关键。代码效率很重要) 有时,需要标注出病理区域,并将非病理区域从视频中去除掉。 如果将…