【C++要笑着学】编码的由来 | basic_string模板类 | string类的常用接口讲解 | 学会查文档(一)

简介: 好久不见!前段时间比较忙,更新频率有所减缓。好在现在快忙完了,我又有时间更文咯,还希望大伙能多多支持!我将会呈现出更多高质量的博客给大家!

💭 写在前面



안녕하세요 ,我是柠檬叶子C


     好久不见!前段时间比较忙,更新频率有所减缓。好在现在快忙完了,我又有时间更文咯,还希望大伙能多多支持!我将会呈现出更多高质量的博客给大家!


     本章将介绍 string 类,在 string 讲解之前会补充一些知识点,比如 basic_string 和字符编码等。string 类的东西太多了,全部介绍一遍似乎不太现实,所以我们将介绍 string 常用的接口函数。如果以后需要用到一些接口函数,要学会查文档,现查现用的技能。


Ⅰ.  string的前置知识


0x00  引入 - 学习string类的原因

C语言中的字符串,是以 \0 为结尾的一些字符的集合。


为了方便操作,C标准库 中提供了一些 str 系列的库函数。


但是这些库函数与字符串是分离开的,不太符合面向对象的思想。


而且底层空间需要用户自己管理,一不小心还会造成越界访问,让人很不爽。  


在工作中为了方便大多会使用 string 类,很少有人去使用 C 标准库中的字符串操作函数。


0x01  认识string类

📚 概念: 简单来说,string 就是一个管理字符串的类。


(下面的知识点会比较干,稍作了解一下即可,感兴趣可以查看 sting 类的文档)


🔍 资料: string - C++ Reference(string类的文档介绍)


① 字符串是表示字符序列的类。


② 标准的字符串提供了对此类对象的支持,其接口类似于标准字符容器的接口,


但添加了专门用于操作单字节字符串的设计特性。


③ string 类是使用 char,即作为它的字符类型,使用它的默认 char_traits 和分配器类型。


  (关于模板的更多信息,可以参阅 basic_string)


④ string类是 basic_sting 模板类的一个实例,它使用 char 来实例化 basic_string 模板类,


    并用 char_traits 和 allocator 作为 basic_string 的默认参数。


   (关于更多的模板信息请参考 basic_sting )


⑤ 注意,这个类独立于所使用的编码来处理字节。


    如果用来处理多字节或变长字符(如UTF-8)的系列,这个类的所有成员(如长度或大小)


    以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。


🔺 总结:


① string 是表示字符串的字符串类。


② 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作 string 的常规操作。


③ string在底层上实际是:basic_string 模板类的别名:


typedef basic_string<char, char_traits, allocator>string;

④ 不能操作多字节或者变长字符的序列。


📁 头文件: <string>


#include <string>       // 使用string类时,需引入头文件 <string>
using namespace std;    // 展开std

0x02  basic_string 模板类

在正式开始讲解 string 之前,我们还要介绍一下我们刚才提到的 basic_string 模板类。

314f03934a2803f429974d16c711b1ae_0d8fdfaafabf41e98b4dd1fad3961269.png


从文档中可以看出,string 的原生类并不是直接定义了一个 string 类,而是定义出了一个类模板。


而 string 是用 typedef 出来的,它其实是 basic_string<char> 这个玩意。


💬 代码演示:我们先用 C 格式字符串构造一个 string 类对象:


#include <iostream>
#include <string>
using namespace std;
int main(void)
{
  string s1("Hello,String!");
  return 0;
}

既然我们知道了它是  basic_string<char>  ,我们来猜想一下它在库里面是如何定义的。


💭 猜想:这个类模板可能是这么定义的


template<class T>
class basic_string {
private:
  T* _str;
  // ...
};

❓  思考:这时候我们思考一个问题,这里为什么需要模板?


有人会想,管理字符串不就是一个 char 嘛?搞一个模板 T 出来干什么?

52bfdc5e9d7ac4f9cabef5f0ee6f1de1_15dfbd4e52954c4c923bb01348cfb0cd.png


这和编码有关系,这里稍微补充一下关于编码的知识。


0x03  香蕉君为你讲解: 编码表的由来(听故事)

我们知道,计算机是漂亮国的人发明的,


所以早期在计算机上自然是只需要显示英文的。


英文的显示非常简单,  英文字母一组合就是英文单词了,

24be420dae43187a700fe26b54e24163_3521bbd81e734e6d8f668b90f0406bc2.png

大写字母 + 小写字母,再加上一些标点符号,


顶多也就一百多个字符,所以出现了一套 ASCII 码。


比如说你写了一个 'a' ,你想在计算机中存储,因为计算机只有二进制,


虽然  和  只能表示两种状态,但是多个  和  一组合就可以表示出很多状态了。


为了能够记录这些  自♂由的  字符,于是就建立了一个映射。


值映射符号,建立映射关系   ,于是就产生了编码表:

66c92f9cec85f1712a72708c6fe42180_03bad74836b3408a95dc37371d6bc3d6.png

( ASCII 码表 )


"ASCII ((American Standard Code for Information Interchange): 美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。"


比如说我们要表示 hello,我们在数组中存储这个单词:


char str1[] = "hello";

实际在内存当中,我存的不是    ,而是它们每个字母(字符)在表中对应的ASCII码值。

246e0bdef80494364dc9c52c41fc75e4_da7fa46d8d034695a7d38f8fec66834a.png

👈 可以通过监视观察一下


存储之后如果我们要把它打印出来,就是通过对照着 ASCII 来 "读" 这个数组的。


这,就是编码表!


我们开头说了,计算机是漂亮国的人发明的,早期在计算机上只需要显示英文,


但是后来计算机开始流行,其他国家也要用,但是你这个是英文的,


虽然英文是世界通用语言,但也不是所有人都懂英文啊。


为了能让电脑更好地普及,这时候就有人搞出了 Unicode。


📚 概念:Unicode - 表示全世界文字的编码表


"统一码(Unicode),也叫万国码、单一码,是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。"

58a9683a85c1fbc944c95e2122a2aca0_73e8eef733694902b2a801f6225088a7.png

(如果你不知道并且感兴趣的话可以问问度娘)


中国文化博大精深,岂是ASCII码的256个字符能表示得了的:


"秦代的《仓颉》《博学》《爰历》三篇共3300字,汉代扬雄的《训纂篇》有5340字,许慎的《说文解字》就有9353字,晋代作品《字林》有12824字,后魏杨承庆的《字统》有13734字,唐代孙强增字本《玉篇》有22561字,到了宋代,司马光修《类篇》,里面有31319字,清代的《康熙字典》则收录了47000多字,1915年欧阳博存的《中华大字典》有48000多字,1959年,日本诸桥辙次的《大汉和词典》收字49964个,1971年张其昀主编的《中文大辞典》有49888字。"


这么多汉字都要表示,如果还是拿值和汉字一一对应,那还得了?


utf-8,常见的汉字都用2个字节去编,


一些生僻的可以用3个或者4个……(我们常说的 utf-8   utf-16   utf-32 )


你可以理解为,中文有了一套自己的规则,去找到值对应的汉字。

char str2[] = "吃饭";

dc51490094857de2eb9d9a9600d647e3_5a1896887c814acca3d0574fbe3b47f4.png


存的时候存的是 utf-8 对应的值,你要用的时候它就会拿这个表去查。


要对应的上,如果对应不上就会出现我们熟知的乱码。


所以建议在Linux或服务器下统一把编码设置为 utf-8,不然出现乱码会很恶心。

b08ecde103b6d3259d6a6b9dda8ae350_bc1b7902c15f4378807c45ffb3e556d3.png

这里显示的是 GB2312,这又是什么呢?


我们已经知道,Unicode 是世界通用编码,而我们中国也有一套自己的编码方式。


📚 概念:GBK —— 中文自己量身定做的编码表。


"GBK全称《汉字内码扩展规范》(GBK即“国标”、“扩展”汉语拼音的第一个字母,英文名称:Chinese Internal Code Specification)"


如果你打开一个文本,Windows 下一般默认的编码是GBK,Linux 下默认的就是 utf-8 。


GBK包括所有的汉字,包括简体和繁体。而这里显示的 GB2312 则只包括简体汉字。

bba23569e46158fdf75b81485cac8b7f_4204eeb0af9e4a7c9eb0038a26092062.png

其实,读音相同的字是编到一起的,不信我们来看一下:

int main(void)
{
  char str2[] = "吃饭";
  str2[1] += 1;
  str2[3] += 1;
  str2[1] += 1;
  str2[3] += 1;
  str2[1] += 1;
  str2[3] += 1;
  return 0;
}

5412770e779ec2aeced38713d57fa728_d439dbab83484a2893ee0326b5e795eb.png


知道这个特性之后有什么用呢,我瞎举个例子。


大家打游戏的时候难免爆粗口,队友太菜,嘴臭骂脏话,


你有没有把脏话发出去的时候,有些脏字变成了星号?  


想这些常见的不文明词汇,系统会有一个脏话词库,把我们的国骂都放进去。


你发了一段话,聊天系统就会检查是否和脏话词库匹配,如果匹配就会把它隐藏成 *

d96c117e82efc9347e79090281b6b715_6c7b7aad33644e9088d63f3eba603515.png

(守望先锋游戏日常)


英雄联盟:草***,我上路**团战****个臭*****非要**


守望先锋:滚**亲的**东西我**消灭金,你在*叫什么?


虽然打 "我操" ,打不出来,但是人家打 卧槽、硪糙、我焯,这样的词,就能发出来了,


"wocao" 同音的字是在一个范围里的,我们利用这个特性,把跟这个范围组合的都给你屏蔽掉,


这样的话你打同音字、谐音字骂人,也能做到给你屏蔽。


编码的知识就讲到这里,有一丢丢扯的了,


下面我们继续回到  basic_string 模板类 的讲解。


💡 回到刚才的问题 —— string 需要模板的原因:


刚才介绍了编码,现在大家应该能理解为什么 string 需要模板了。


string 不仅有 char,还有 wchar_t (2个字节),这就是 string 需要 basic_sting 的原因。


template<class T>
class basic_string {
private:
    T* _str;
    // ...
};

所以这个 T 不一定是 char,还有可能是 wchar_t,这和编码有关系。


基于编码的原因,有些字符串就会用两个字符去表示一个字,


所以就有了 wchar_t —— 宽字节。


0x04 其它字符编码的string

📚 概念:宽字节是一种扩展的存储方式,unicode 编码的字符一般以 wchar_t 类型存储。


💬 我们 sizeof wchar_t 看看:

#include <iostream>
using namespace std;
int main(void)
{
  cout << sizeof(char) << endl;
  cout << sizeof(wchar_t) << endl;
  return 0;
}

cf5bcc3cc182b7baa12de27442ee8b45_e9f92f12b85b42f59e79259bb5f96f84.png

wchar_t 是两个字节,是为了更好地表示字符。


如果涉及 Windows 编程,Windows 下的很多接口都是用的 Unicode,


这时它的字符串不是 char*,而是 wchar_t* ,这时候就涉及到转码,


如果想要存储 wchar_t* ,就最好用 wstring —— 专门处理宽字符的。


📚 概念:wstring 就是每个字符都是一个 wchar_t 的:


不仅仅有 string 和 wstring!


还有 u16string(存16个比特位)、还有u32string(存32个比特位)

6b43f933d7ec0fa4160c8089450b7f87_c3673a3387974421853f7b4ad184557c.png

🔍 这里就不逐个讲解了,感兴趣可以查文档:


u16string - C++ Reference
u32string - C++ Reference


🔺 结论:


本章主要学习 string,现阶段基本用的都是 string (里面存 char)


如果碰到有些地方是 wchar_t 就要使用与之对应的 wstring 了,


其他也一样,比如有些地方字符串编码是 utf-32,这时候你就可能要用 u32string 去存储了。


因为有的库或API只支持UTF-16编码的字符,


而且有的API使用UTF-16编码的字符时执行速度会快一些。


(你如果坚持使用UTF-8,它内部需要将UTF-8转换成UTF-16,速度可能会慢)


Ⅱ.  string类的常用接口


(constructor) 函数名称 功能说明
string()                           (重点) 构造空的string类对象,即空字符
string(const char* s)      (重点) 用C-string来构造string类对象
string(size_t n, char c)     string类对象中包含n个字符c
string(const string& s)   (重点) 拷贝构造函数

0x00  引入 - 正式开始学习string

铺垫结束!前置知识我们已经讲完了,现在我们正式开始学习 string。


我们知道了,字符串里的每一个字符都是 char 了,那平时我们怎么用它呢?


注意,本篇博客我们只只介绍常用的接口!


0x01  string类对象的常见构造

我们还是以查看文档的方式去学习。

75500ef6caf82a36d38e909f2ee05466_55367ac56ac449e7a38b34aaf25cf695.png

🔍 查看文档:string::string - C++ Reference


📚 重点掌握这几个:


string()                      // 构造空的string类对象,即非空字符串。
string(cosnt char* s)         // 用C-string来构造string类对象
string(size_t n, char c)      // string类对象中包含n个字符c
string(const string&s)        // 拷贝构造函数

💬 代码演示:


#include <iostream>
#include <string>     // 要包含头文件string
using namespace std;  // 为了方便练习,就随便展开命名空间了。
void StringTest() {
  /* 无参 */
  string s1;                      // 构造空的string类对象s1
  /* 带参 */
  string s2("HelloString!\n");    // 用C格式字符串构造string类对象s2
  /* 拷贝构造 */
  string s3(s2);                  // 拷贝构造s3
}

🔨 学会看文档:

fcbc2181d5417df4d51cf82ab484594d_b260170d27b74a52bc909efdd540585a.png

#include <iostream>
#include <string> 
using namespace std; 
int main(void)
{
  string s1("abcdef123456");
  string s2(s1, 2, 6);       // 从第2个位置开始,初始化6个字符
  cout << s2 << endl;
  return 0;
}

9e8041b727af71cb50dc07fd6257ac12_23d80272e8f44395be0debb072e01175.png


再仔细观察,我们发现长度 len 缺省值给的是 npos:


string (
    const string& str, 
    size_t pos,         // 起始位置
    size_t len = npos   // 长度(缺省值给的npos)
    );

也就是说,如果你不指定初始化多少个字符,他会默认按 npos 个来。

300770867de4da9eda72fbccdbb356f0_3e1908951dd44bf2a697db286f346c5a.png


这个 npos 你可以理解为是 string 里的一个静态变量,它是 -1。


当你没有指定 len 时,因为 npos 是 -1,而又是 size_t 无符号整型,他长度将会是整形的最大值。


也就是说,如果你不传,它就相当于取整型最大值 2147483647,


这很大,非常的大,所以也就相当于取所有字符串了。


💬 如果不指定 len:


int main(void)
{
  string s1("abcdef123456");
  string s2(s1, 2);       // 从第2个位置开始,初始化npos个长度的字符
  cout << s2 << endl;
  return 0;
}

dd8c34531a954fb42a377be44f3c44ce_0a820518674b405092eb9306f6ebb910.png


相关文章
|
3天前
|
编译器 C语言 C++
类和对象的简述(c++篇)
类和对象的简述(c++篇)
|
3天前
|
编译器 C++
㉿㉿㉿c++模板的初阶(通俗易懂简化版)㉿㉿㉿
㉿㉿㉿c++模板的初阶(通俗易懂简化版)㉿㉿㉿
|
1月前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
68 19
|
1月前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
51 13
|
1月前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
53 5
|
1月前
|
存储 算法 搜索推荐
【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】
1. **相关排序和查找算法的原理**:介绍直接插入排序、直接选择排序、冒泡排序和顺序查找的基本原理及其实现代码。 2. **C++ 类与成员函数的定义**:讲解如何定义`Array`类,包括类的声明和实现,以及成员函数的定义与调用。 3. **数组作为类的成员变量的处理**:探讨内存管理和正确访问数组元素的方法,确保在类中正确使用动态分配的数组。 4. **函数参数传递与返回值处理**:解释排序和查找函数的参数传递方式及返回值处理,确保函数功能正确实现。 通过掌握这些知识,可以顺利地将排序和查找算法封装到`Array`类中,并进行测试验证。编程要求是在右侧编辑器补充代码以实现三种排序算法
40 5
|
1月前
|
Serverless 编译器 C++
【C++面向对象——类的多态性与虚函数】计算图像面积(头歌实践教学平台习题)【合集】
本任务要求设计一个矩形类、圆形类和图形基类,计算并输出相应图形面积。相关知识点包括纯虚函数和抽象类的使用。 **目录:** - 任务描述 - 相关知识 - 纯虚函数 - 特点 - 使用场景 - 作用 - 注意事项 - 相关概念对比 - 抽象类的使用 - 定义与概念 - 使用场景 - 编程要求 - 测试说明 - 通关代码 - 测试结果 **任务概述:** 1. **图形基类(Shape)**:包含纯虚函数 `void PrintArea()`。 2. **矩形类(Rectangle)**:继承 Shape 类,重写 `Print
48 4
|
1月前
|
设计模式 IDE 编译器
【C++面向对象——类的多态性与虚函数】编写教学游戏:认识动物(头歌实践教学平台习题)【合集】
本项目旨在通过C++编程实现一个教学游戏,帮助小朋友认识动物。程序设计了一个动物园场景,包含Dog、Bird和Frog三种动物。每个动物都有move和shout行为,用于展示其特征。游戏随机挑选10个动物,前5个供学习,后5个用于测试。使用虚函数和多态实现不同动物的行为,确保代码灵活扩展。此外,通过typeid获取对象类型,并利用strstr辅助判断类型。相关头文件如&lt;string&gt;、&lt;cstdlib&gt;等确保程序正常运行。最终,根据小朋友的回答计算得分,提供互动学习体验。 - **任务描述**:编写教学游戏,随机挑选10个动物进行展示与测试。 - **类设计**:基类
33 3
|
3月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
90 2
|
3月前
|
安全 编译器 C++
【C++11】可变模板参数详解
本文详细介绍了C++11引入的可变模板参数,这是一种允许模板接受任意数量和类型参数的强大工具。文章从基本概念入手,讲解了可变模板参数的语法、参数包的展开方法,以及如何结合递归调用、折叠表达式等技术实现高效编程。通过具体示例,如打印任意数量参数、类型安全的`printf`替代方案等,展示了其在实际开发中的应用。最后,文章讨论了性能优化策略和常见问题,帮助读者更好地理解和使用这一高级C++特性。
113 4