C++ string类(1)—初始化、容量操作、迭代器

目录

前言

一、string类

二、初始化

1、无参或带参

2、用字符串变量初始化

3、用字符串初始化

4、指定数量字符

三、容量操作

1、size

2、push_back

3、append​编辑

4、+=运算符

5、reserve

6、resize

四、迭代器

1、正向迭代器

2、反向迭代器 

3、const迭代器 (正向反向)

五、OJ练习

反转字符

找出字符串中出现一次的字符 


前言

string类模板如下,为什么会有好几个呢?

这些不同的string类模板是为了处理不同的字符编码和字符集。每个模板都专门用于处理特定类型的字符数据。

  1. std::string:这是最常见的string类模板,用于处理ASCII字符集。它使用单字节字符表示,适用于大多数常规字符串操作。

  2. std::wstring:这是宽字符版本的string类模板,用于处理Unicode字符。它使用wchar_t类型来表示字符,适用于需要处理多语言字符集的情况。

  3. std::u16string:这是用于处理UTF-16编码的字符串的模板。UTF-16使用16位编码表示字符,适用于处理较大的字符集,如大部分Unicode字符。

  4. std::u32string:这是用于处理UTF-32编码的字符串的模板。UTF-32使用32位编码表示字符,适用于处理包含所有Unicode字符的字符集。

这些不同的string类模板提供了对不同字符编码和字符集的支持,以便在处理不同类型的文本数据时能够正确地表示和操作字符。通过选择适当的string类模板,可以确保在不同的应用场景中正确处理和操作字符数据。

 

我们先来了解一下计算机存储字符的方式: 

计算机中都是二进制形式,无法直接存储字母和符号,这时就需要一个映射表,ASCll就诞生了。

 为了可以在计算机上显示多个国家的文字,unicode诞生了

 统一码(Unicode),也叫万国码、单一码,由统一码联盟开发,是计算机科学领域里的一项业界标准,包括字符集、编码方案等。

统一码是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。

Unicode字符编码方案分为UTF-8、UTF-16和UTF-32,它们被设计用于在计算机系统中表示和处理不同范围的Unicode字符。

  1. UTF-8:UTF-8是一种变长编码方案,它使用1到4个字节来表示不同的Unicode字符。它是最常用的Unicode编码方案之一,因为它可以兼容ASCII字符集,并且在表示常见字符时比较节省空间。UTF-8适用于在存储和传输文本数据时节省空间的情况,特别是在互联网和计算机网络中广泛使用。

  2. UTF-16:UTF-16是一种定长或变长编码方案,它使用16位编码来表示Unicode字符。对于大部分常见的Unicode字符,UTF-16使用16位编码表示,但对于一些较少使用的字符,它需要使用两个16位编码来表示。UTF-16适用于需要处理较大字符集的情况,如多语言文本处理和国际化应用。

  3. UTF-32:UTF-32是一种定长编码方案,它使用32位编码来表示Unicode字符。每个Unicode字符都使用32位编码表示,无论字符是否常见。UTF-32适用于需要处理包含所有Unicode字符的字符集的情况,如某些特定领域的文本处理和字符级操作。

这些不同的Unicode编码方案提供了不同的权衡和适用性,根据具体的需求和应用场景,可以选择适当的编码方案来表示和处理Unicode字符。

其中,UTF-8使用的最多 。

我们中国也有针对汉字的GBK编码,对一些生僻字提供了支持。

一、string类

文档介绍 

  • 字符串是表示字符序列的类,标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
  • string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。
  • string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
  • 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。
总结:
  • string是表示字符串的字符串类
  • 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
  • string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;
  • 不能操作多字节或者变长字符的序列。
在使用string类时,必须包含#include头文件string以及using namespace std;

二、初始化

  1. 默认构造函数 string():创建一个空字符串对象。

  2. 复制构造函数 string(const string& str):通过复制另一个字符串对象 str 来创建一个新的字符串对象。

  3. 子字符串构造函数 string(const string& str, size_t pos, size_t len = npos):从字符串对象 str 的指定位置 pos 开始,创建一个新的字符串对象,长度为 len。如果未提供 len 参数,默认创建到字符串末尾的子字符串。

  4. 从C-String构造函数 string(const char* s):从以空字符结尾的C字符串 s 创建一个新的字符串对象。

  5. 从序列构造函数 string(const char* s, size_t n):从C字符串 s 的前 n 个字符创建一个新的字符串对象。

  6. 填充构造函数 string(size_t n, char c):创建一个包含 n 个字符 c 的新字符串对象。

  7. 范围构造函数 template <class InputIterator> string(InputIterator first, InputIterator last):通过迭代器范围 [first, last) 中的字符创建一个新的字符串对象。这个构造函数可以接受不同类型的迭代器,例如指针、容器的迭代器等。

1、无参或带参

既可以无参初始化, 也可以带参初始化。

int main()
{string s1;string s2("hello world");string s3 = "hello";return 0;
}

 我们也可以通过[ ]操作符访问字符串某个位置。 

#include<string>
int main()
{string s2("hello world");for (size_t i = 0; i < s2.size(); ++i) {s2[i]++;}cout << s2 << endl;return 0;
}

我们可以通过流插入运算符<<输出string类对象。 

这是因为string类中对流插入运算符<<进行了重载,流提取也进行了重载。

int main()
{string s2;cin >> s2;for (size_t i = 0; i < s2.size(); ++i) {s2[i]++;}cout << s2 << endl;return 0;
}

 

2、用字符串变量初始化

用一个字符的指定位置开始指定字符个数为另一个字符初始化。

int main()
{string s3 = "hello";string s4(s3, 2, 3);cout << s4 << endl;return 0;
}

如果取字符的个数超过字符总长度,则取到末尾即可。 

int main()
{string s3 = "hello";string s4(s3, 2, 3);cout << s4 << endl;string s5(s3, 2, 10);cout << s5 << endl;return 0;
}

 

第三个参数取字符整数可以缺省,那么从指定位置开始取到末尾结束。 

int main()
{string s3 = "hello";string s4(s3, 2, 3);cout << s4 << endl;string s5(s3, 2);cout << s5 << endl;return 0;
}

3、用字符串初始化

也可以把要赋值的字符串直接放在第一个参数位置,第二个参数为赋值字符个数。 

int main()
{string s7("hello world", 5);cout << s7 << endl;string s8("hello world", 5 , 6);cout << s8 << endl;return 0;
}
  • 第一个构造函数中,除字符串如果只有一个参数,则默认从字符串第一个字符开始,赋值第二个参数大小长度。
  • 第二个构造函数中,除字符串如果有两个参数,第一个参数为指定起始位置,第二个参数为指定的初始化长度,长度大于实际字符串的长度,则截断为实际字符串的长度。

 

4、指定数量字符

int main()
{string s9(10, '$');cout << s9 << endl;return 0;
}

三、容量操作

1、size

size()与length()底层实现原理完全相同,都用于获取字符串 有效字符长度, 引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
int main()
{string s1("hello world");cout << s1.size() << endl;cout << s1.length() << endl;return 0;
}

下面两个了解即可 :

  • max_size()函数返回一个无符号整数,表示字符串对象可以容纳的最大字符数。这个值通常取决于系统的限制,因此可能会因操作系统和编译器而异。
  • capacity()函数返回一个无符号整数,表示字符串对象当前分配的内存空间大小。这个值可能大于字符串实际包含的字符数,因为字符串类通常会预留一些额外的空间以便进行扩展。 
int main()
{string s1("hello world");cout << s1.max_size() << endl;cout << s1.capacity() << endl;return 0;
}

 64位下输出结果:

2、push_back

添加单个字符到字符串末尾 

int main()
{string s1("hello");s1.push_back(' ');s1.push_back('!');cout << s1 << endl;return 0;
}

3、append

我们还可以使用append在字符串末尾添加单个字符或字符串 ,这种是最常用的形式。

int main()
{string s1("hello");s1.push_back(' ');s1.push_back('!');cout << s1 << endl;s1.append("world");cout << s1 << endl;
}

 

4、+=运算符

我们还可以使用+=运算符在字符串末尾添加字符或字符串,+=底层是调用push_back或append,在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。

int main()
{string s1("hello");s1 += ' ';s1 += '!';s1 += "world";cout << s1 << endl;
}

 

5、reserve

先看下面代码: 

int main()
{size_t sz = s.capacity();cout << "making s grow:\n";cout << "capacity changed: " << sz << '\n';for (int i = 0; i < 100; ++i){s.push_back('c');if (sz != s.capacity()){sz = s.capacity();cout << "capacity changed: " << sz << '\n';}}
}

上述演示了如何使用push_back()函数向字符串对象中添加字符,并观察字符串的容量变化

  • 首先,在main()函数中声明了一个size_t类型的变量sz,并将其初始化为字符串对象s的容量(假设s是之前已经定义的字符串对象)。
  • 然后,通过使用cout对象和<<运算符,输出一条提示信息"making s grow:"。
  • 接下来,使用一个循环,从0到99,逐个向字符串对象s中添加字符'c'。在每次添加字符之后,通过比较之前记录的容量sz和当前字符串对象s的容量,来判断容量是否发生了变化。
  • 如果容量发生了变化,将新的容量值赋给sz,并使用cout对象和<<运算符输出一条提示信息"capacity changed:",以及新的容量值。

这里输出的容量不包括 \0,也就是实际容量要再加1.

在监视中可以看到,,实际上将字符存入了_Buf数组中。

        在C++的实现中,std::string类通常使用两个数组来存储字符串的字符。当字符串的长度小于等于15个字符时,字符串的字符被存储在一个名为_Buf的内部固定大小数组中,该数组长度为16。这样可以避免动态内存分配,提高性能。

        当字符串的长度超过15个字符时,字符串的字符将被存储在一个名为_Ptr的动态分配的数组中,该数组的长度将根据需要进行动态调整。这样可以容纳更长的字符串,并且可以根据需要动态分配内存。

        这种设计可以在字符串较短时节省内存,并在字符串较长时提供足够的存储空间。具体的实现可能会因编译器和标准库的不同而有所差异,但这种区分短字符串和长字符串的策略是常见的优化技术之一

vs下string的结构:string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定string中字符串的存储空间:
  • 当字符串长度小于16时,使用内部固定的字符数组来存放
  • 当字符串长度大于等于16时,从堆上开辟空间
union _Bxty
{ // storage for small buffer or pointer to larger onevalue_type _Buf[_BUF_SIZE];pointer _Ptr;char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;
  • 这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内
  • 部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。
  • 其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量
  • 最后:还有一个指针做一些其他事情。
  • 故总共占16+4+4+4=28个字节。

 

输出查看一下string类对象s的大小 

string s;cout << sizeof(s) << endl;

 可以看到是28,那么它在堆上起始就是大小32。

观察扩容情况可以发现从大小32开始,每次1.5倍扩容。 

 

在已知所需空间大小时,我们可以使用reserve提前开辟空间,减少扩容,提高效率。 

 

使用reverse一次性开辟100个字符空间。 

int main()
{string s;s.reserve(100);size_t sz = s.capacity();cout << "making s grow:\n";cout << "capacity changed: " << sz << '\n';for (int i = 0; i < 100; ++i){s.push_back('c');if (sz != s.capacity()){sz = s.capacity();cout << "capacity changed: " << sz << '\n';}}
}

这时就不需要一次次扩容了。 

 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。

6、resize

resize将字符串大小调整为n个字符的长度。

  • 如果n小于当前字符串长度,则将当前值缩短到前n个字符,删除第n个字符以外的字符。
  • 如果n大于当前字符串长度,则通过在末尾插入尽可能多的字符来扩展当前内容,以达到n的大小。
  • 如果指定了填充字符c,则新元素被初始化为c的副本,否则,它们是值初始化的字符(空字符)。
int main()
{// 扩容string s1("hello world");s1.reserve(100);cout << s1.size() << endl;cout << s1.capacity() << endl;// 扩容+初始化string s2("hello world");s2.resize(100);cout << s2.size() << endl;cout << s2.capacity() << endl;return 0;
}

resize(size_t n) resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。

reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。

四、迭代器

1、正向迭代器

begin指向字符串第一个字符,end指向字符串最后一个字符的后一个位置,功能和指针类似。

int main()
{string s1("hello world");string::iterator it = s1.begin();while (it != s1.end()) {cout << *it << " ";++it;}return 0;
}

 

范围for底层就是调用迭代器实现的。 

int main()
{string s1("hello world");string::iterator it = s1.begin();while (it != s1.end()) {cout << *it << " ";++it;}cout << endl;for (auto ch : s1) {cout << ch << " ";}cout << endl;
}

 

2、反向迭代器 

rbegin指向字符串最后一个字符,rend指向字符串的第一个字符的前一个位置。 

 

int main()
{string::reverse_iterator rit = s1.rbegin();while (rit != s1.rend()) {cout << *rit << " ";++rit;}cout << endl;return 0;
}

3、const迭代器 (正向反向)

  const正向反向迭代器只能遍历和读数据。

int main()
{string s1("hello world");string::const_iterator it = s1.begin();while (it != s1.end()) {cout << *it << " ";++it;}cout << endl;string::const_reverse_iterator rit = s1.rbegin();while (rit != s1.rend()) {cout << *rit << " ";++rit;}cout << endl;cout << s1 << endl;return 0;
}

这时可以借助auto自动推导类型。 

int main()
{//string::const_iterator it = s1.begin();auto it = s1.begin();while (it != s1.end()) {cout << *it << " ";++it;}cout << endl;//string::const_reverse_iterator rit = s1.rbegin();auto rit = s1.rbegin();while (rit != s1.rend()) {cout << *rit << " ";++rit;}cout << endl;return 0;
}

 

五、OJ练习

反转字符

917. 仅仅反转字母 - 力扣(LeetCode)

采用快速排序的思路。 

class Solution {
public:string reverseOnlyLetters(string s) {size_t begin = 0, end=s.size()-1;while(begin<end){while(begin<end&&!isalpha(s[begin]))++begin;while(begin<end&&!isalpha(s[end]))--end;swap(s[begin],s[end]);++begin;--end;}return s;}
};

找出字符串中出现一次的字符 

 387. 字符串中的第一个唯一字符 - 力扣(LeetCode)

采用计数排序的思路。 

class Solution {
public:int firstUniqChar(string s) {int count[26]={0};for(auto ch:s){count[ch-'a']++;}for(int i=0;i<s.size();i++){if(count[s[i]-'a']==1)return i;}return -1;}
};

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c++面试题

1.static的使用 1&#xff09;修饰局部变量&#xff1a;在函数内部使用static修饰局部变量&#xff0c;会使它成为静态局部变量。静态局部变量只会被初始化一次&#xff0c;且只有在第一次调用该函数时才会被初始化&#xff0c;之后每次调用该函数时都会保留上一次的值.从原来…

quickapp_快应用_父子组件传值

目录 页面级组件自定义组件(子组件)引入自定义组件(子组件)父组件给子组件传值子组件给父组件进行传值父组件调用子组件的方法 页面级组件 在pages中定义的组件被称为页面级组件。 页面级组件(等同于Vue页面)&#xff0c;通过路由配置可以进行页面跳转。 自定义组件(子组件)…

ESP32-Web-Server编程-简单的照片浏览器

ESP32-Web-Server编程-简单的照片浏览器 概述 从本节开始我们开始制作一些有趣的多媒体 Web 的示例。 当你希望在网页上展示一些广告、照片&#xff0c;或者你的开发板带摄像头&#xff0c;能够采集一些图片&#xff0c;这时你希望可以通过手头的浏览器查看图片&#xff0c;…

Node.js【文件系统模块、路径模块 、连接 MySQL、nodemon、操作 MySQL】(三)-全面详解(学习总结---从入门到深化)

目录 Node.js 文件系统模块&#xff08;二&#xff09; Node.js 文件系统模块&#xff08;三&#xff09; Node.js 文件系统模块&#xff08;四&#xff09; Node.js 路径模块 Node.js 连接 MySQL Node.js nodemon Node.js 操作 MySQL Node.js 应用 Node.js 文件系统模块…

Python----练习:使用面向对象实现报名系统开发

第一步&#xff1a;分析哪些动作是由哪些实体发出的 学生提出报名 学生提供相关资料 学生缴费 机构收费 教师分配教室 班级增加学生信息 于是&#xff0c;在整个过程中&#xff0c;一共有四个实体&#xff1a;学生、机构、教师、班级&#xff01;在现实中的一个具体的实…

如何进行卷积特征可视化

大家好啊&#xff0c;我是董董灿。 之前写过很多关于卷积算法的文章&#xff1a;5分钟理解什么是卷积的特征提取。总的来说&#xff0c;卷积算法的本质是一个特征提取器。 那么既然卷积神经网络在图像分类、图像检测、图像分割以及其他领域有这么好的表现&#xff0c;卷积到底…

Java 不要在父类的构造方法里面调用可以被子类重写的方法

不要在父类的构造方法(代码块)里面调用可以被子类重写的方法 我们从第一天学习Java开始&#xff0c;就对Java的类初始化顺序牢记于心。但是在实际开发过程中&#xff0c;似乎很难能接触这一部分的应用。在这之前&#xff0c;我也认为它只是面试中八股文而已&#xff0c;直到最…

聊一聊大模型 | 京东云技术团队

事情还得从ChatGPT说起。 2022年12月OpenAI发布了自然语言生成模型ChatGPT&#xff0c;一个可以基于用户输入文本自动生成回答的人工智能体。它有着赶超人类的自然对话程度以及逆天的学识。一时间引爆了整个人工智能界&#xff0c;各大巨头也纷纷跟进发布了自家的大模型&#…

Python 潮流周刊#29:Rust 会比 Python 慢?!

△请给“Python猫”加星标 &#xff0c;以免错过文章推送 你好&#xff0c;我是猫哥。这里每周分享优质的 Python、AI 及通用技术内容&#xff0c;大部分为英文。本周刊开源&#xff0c;欢迎投稿[1]。另有电报频道[2]作为副刊&#xff0c;补充发布更加丰富的资讯。 &#x1f43…

算法基础--双指针

前面已经写了两篇关于算法方面的文章&#xff0c;这几天想了下&#xff0c;决定把这个算法整理成一个系列&#xff0c;除了是帮助自己巩固算法知识外&#xff0c;还能够把自己总结的每种算法的套路保存下来并分享给大家&#xff0c;这样以后即使是哪天想要重拾起来&#xff0c;…

简单可行的SeruatV4的安装方案

目前Seurat的版本从V4升级到了V5&#xff0c;由于一些变化&#xff0c;导致当年取巧&#xff0c;使用获取数据的方法都无法在V5中使用。 建议在操作前重启下Rstudio&#xff08;或更确切的说是R&#xff09;&#xff01;&#xff01;&#xff01; 那么如何确保自己能够安装V4的…

【字符串匹配】【KMP算法】Leetcode 28 找出字符串中第一个匹配项的下标☆

【字符串匹配】【KMP算法】Leetcode 28 找出字符串中第一个匹配项的下标 &#xff08;1&#xff09;前缀和后缀&#xff08;2&#xff09;前缀表&#xff08;最长相同的前缀和后缀的长度&#xff09;&#xff08;3&#xff09;匹配过程示意&#xff08;4&#xff09;next数组的…

matlab 点云放缩变换

目录 一、算法原理二、代码实现三、结果展示四、相关链接本文由CSDN点云侠原创,原文链接。爬虫网站自重。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT。 一、算法原理 缩放可以独立应用于三个坐标轴,如将点 ( x , y , z ) ( x

[LeetCode周赛复盘] 第 374 场周赛20231203

[LeetCode周赛复盘] 第 374 场周赛20231203 一、本周周赛总结100144. 找出峰值1. 题目描述2. 思路分析3. 代码实现 100153. 需要添加的硬币的最小数量1. 题目描述2. 思路分析3. 代码实现 100145. 统计完全子字符串1. 题目描述2. 思路分析3. 代码实现 100146. 统计感冒序列的数…

使用Linux docker方式快速安装Plik并结合内网穿透实现公网访问

文章目录 1. Docker部署Plik2. 本地访问Plik3. Linux安装Cpolar4. 配置Plik公网地址5. 远程访问Plik6. 固定Plik公网地址7. 固定地址访问Plik 本文介绍如何使用Linux docker方式快速安装Plik并且结合Cpolar内网穿透工具实现远程访问&#xff0c;实现随时随地在任意设备上传或者…

C语言扫雷游戏

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、扫雷游戏的分析和设计1.1扫雷游戏的功能说明1.2数据结构的分析1.3文件结构设计 二、扫雷游戏的代码实现总结 前言 详细介绍扫雷游戏的思路和实现过程。 一…

高校人员信息管理系统C++

代码&#xff1a;https://mbd.pub/o/bread/ZZeZk5lx 一、基本内容论述 1、问题描述 某高校有四类员工&#xff1a;教师、实验员、行政人员、教师兼行政人员&#xff1b;共有的信息包括&#xff1a;编号、姓名、性别、年龄等。其中&#xff0c;教师还包含的信息有&#xff1a;所…

堆排序(C语言)

前言 在上一篇内容&#xff1a;大小堆的实现&#xff08;C语言&#xff09;&#xff0c;我们实现了关于创建大小堆的各函数与实现。但是如果突然要使用一个堆排序但是此时并没有一个现成的堆&#xff0c;这就需要花费时间去新建实现堆的插入删除这些操作从而实现一个堆&#xf…

51单片机应用从零开始(十)·指针

指针 C语言指针是一种保存变量地址的数据类型。它可以让程序直接访问内存中的数据&#xff0c;而不需要通过变量名来访问。指针变量存储的是一个地址&#xff0c;这个地址指向内存中的某个位置&#xff0c;该位置存储了一个值。 在C语言中&#xff0c;可以使用&运算符取得一…

Endnote加入新的style(参考文献格式)

1. 下载模板 可以从官网上下载模板&#xff0c;比如某些常见的期刊都有自己的模板&#xff0c;还有写中文论文的话有专门的GBT7714。 2. 示范 以下图MDPI为例&#xff0c;下载下来是一个ens文件。 双击打开此文件 file -> save as 输入保存的名字&#xff0c;我这里保…