32.字符数组和string

32.面向对象关联知识点-字符数组和string


32.1 知识点

知识回顾:字符数组

  • 字符数组就是类型为字符 char 的一维数组
  • 聚合初始化与单独初始化方式
  • 直接将字符数组打印可能出现问题
  • 使用字符串字面量赋值时,会在尾部自动添加 \0 作为结束符
  • 未初始化的元素在打印时可能产生未定义行为
// 方式一:聚合初始化
char charArr[5] = { 'H', 'E', 'L', 'L', 'O' };
char charArr2[] = { 'H', 'E', 'L', 'L', 'O' };

// 方式二:单独初始化
char charArr3[5];
charArr3[0] = 'H';
charArr3[1] = 'E';

// 直接打印会出问题

// 可以使用字符串对字符数组进行赋值
char charArr4[] = "Hellow World"; // string
cout << charArr4 << endl;
// 原本看起来只有12个字符的字符串,但是长度却有13个
int length = sizeof(charArr4) / sizeof(char);
cout << length << endl;
// 对字符数组用字符串去赋值时,会默认在尾部加上一个 '\0' 的转义字符,代表字符串的结束

char charArr5[5];
charArr5[0] = 'A';
charArr5[1] = 'B';
// charArr5[2] = 'X';
// charArr5[3] = 'C';
// charArr5[4] = '\0';
// 如果不对字符数组后面的内容进行初始化,并且其中没有 '\0',   
// 在打印时就不知道何时结束,就会不停地往后读取。  
// 因为没有明确的终止符,程序会根据内存中是否存在 '\0' 停止打印,  
// 可能打印出多余字符,甚至是乱码,产生未定义行为  
cout << charArr5[2] << endl;
cout << charArr5 << endl;
// 关于“烫”字符  
// 在内存中,汉字使用多字节编码(如 UTF-8 或 GBK)  
// 当程序读取到这些内存内容时,会尝试解释它们  
// 可能形成合法的汉字编码,导致控制台上显示出汉字或其他非预期字符  
cout << "****************************" << endl;

字符数组公共方法

  • C++ 中内置了一些关于字符数组的 C 语言方法
  • strcat_sstrcpy_sstrcmpstrlen 等函数的使用规则相同:目标长度不足会报错
// 1. strcat_s(字符数组, 字符数组长度, 字符串)
//    将一个字符串连接到另一个字符数组中
// 操作前:str1 = "123"(长度3),str2 = "456"(长度3),str1 可用空间为7(sizeof(str1)=10)
char str1[10] = "123";
char str2[10] = "456";
strcat_s(str1, sizeof(str1), str2);
cout << str1 << endl;  // 输出:123456

// 2. strcpy_s(字符数组, 字符数组长度, 字符串)
//    将一个字符串复制到另一个字符数组中
// 操作前:str1 = "123456"(长度6),目标"7777777"(长度7),str1 可用空间为4
strcpy_s(str1, sizeof(str1), "7777777");
cout << str1 << endl;  // 输出:7777777

// 3. strcmp(字符数组1, 字符数组2)
//    比较两个字符数组是否相同,0 代表相同,非0代表不同
cout << strcmp(str1, "7777777") << endl;  // 输出:0

// 4. strlen(字符数组)
//    得到字符数组实际长度(不包含 '\0')
cout << strlen(str1) << endl;  // 输出:7

string 相关方法

  • C++ string 类提供更丰富、更安全的字符串操作
  • 可使用 +appendsize/lengthemptyclearcapacity 等成员函数
// 1. 拼接
string string1 = "123";
string string2 = "456";
string1.append(string2);
cout << string1 << endl;  // 输出:123456

// 2. 返回字符串长度
cout << string1.size() << endl;    // 输出:6
cout << string1.length() << endl;  // 输出:6

// 3. 判断字符串是否为空
string string3 = "1";
cout << string3.empty() << endl;   // 输出:0(false)

// 4. 清空字符串
string3.clear();
cout << string3 << endl;           // 输出:(空字符串)

// 5. 返回容量
string3 = "1234567890123456789";
cout << string3.capacity() << endl;  // 输出:31(不同环境可能不同)

// 6. 在指定位置插入字符串
string3.insert(1, "哈哈");
cout << string3 << endl;  // 输出:1哈哈234567890123456789
string3.insert(23, "嘻嘻嘻");
cout << string3 << endl;  // 输出:1哈哈234567890123456789嘻嘻嘻

// 7. 从指定索引位置起删除固定个数字符
cout << string3.size() << endl;     // 输出:26
string3.erase(23, 6);               // 修正:原索引29超出范围
cout << string3 << endl;            // 输出:1哈哈234567890123456789

// 8. 替换指定区间为另一个字符串
string3.replace(0, 10, "123456");
cout << string3 << endl;            // 输出:12345667890123456789

// 9. 交换两个字符串
cout << string1 << endl;  // 输出:123456
cout << string2 << endl;  // 输出:456
string1.swap(string2);
cout << string1 << endl;  // 输出:456
cout << string2 << endl;  // 输出:123456

// 10. 删除最后一个字节(注意多字节字符问题)
string3.push_back('1');
cout << string3 << endl;  // 输出:123456678901234567891
string3.pop_back();
cout << string3 << endl;  // 输出:12345667890123456789

// 11. 添加单个字符
string3.push_back('A');
cout << string3 << endl;  // 输出:12345667890123456789A

// 12. 查找字符串第一次出现的位置(找不到返回 -1)
string string4 = "12345627";
int i = string4.find("2");
cout << i << endl;        // 输出:1

// 13. 查找最后一次出现的位置
i = string4.rfind("2");
cout << i << endl;        // 输出:6

// 14. 查找首次/最后一次出现在传入字符串中的任一字符
string4 = "12345627";
i = string4.find_first_of("478");
cout << i << endl;        // 输出:3
i = string4.find_last_of("478");
cout << i << endl;        // 输出:7

// 15. 查找第一个/最后一个不在传入字符串中的字符
i = string4.find_first_not_of("12367");
cout << i << endl;        // 输出:3
i = string4.find_last_not_of("2367");
cout << i << endl;        // 输出:4

// 16. 截取字符串
string4 = "12345627";
string string5 = string4.substr(1, 4);
cout << string5 << endl;  // 输出:2345

// 17. 字符串转字符数组
const char* charPtr = string4.data();
for (int i = 0; i < string4.size(); i++)
{
    cout << charPtr[i] << endl;  // 逐行输出:1 2 3 4 5 6 2 7
}

// 18. 判断字符串内容是否相等
string1 = "123";
string2 = "123";
if (string1 == string2)
    cout << "相等" << endl;  // 输出:相等

string 的本质

  • string 的本质是 C++ 中封装的一个管理字符数组的类
  • 它封装了动态内存分配、长度管理、字符串操作函数等复杂逻辑
  • 因此在现代 C++ 开发中,建议使用 string 来替代字符数组,以提高效率与安全性

32.2 知识点代码

Lesson32_面向对象关联知识点_字符数组和string.cpp

#include <iostream>
using namespace std;
int main()
{
    #pragma region 知识回顾 字符数组
    //字符数组就是类型为字符char的一维数组

    //常用方式:
    //方式一:聚合初始化
    char charArr[5] = { 'H', 'E', 'L', 'L', 'O' };
    char charArr2[] = { 'H', 'E', 'L', 'L', 'O' };

    //方式二:单独初始化
    char charArr3[5];
    charArr3[0] = 'H';
    charArr3[1] = 'E';

    //这几种声明初始化的方式,如果直接将字符数组拿来打印 会出现问题

    //可以直接使用字符串对字符数组进行赋值
    char charArr4[] = "Hellow World"; //string
    cout << charArr4 << endl;
    //原本看起来只有12个字符的字符串 但是长度却有13个
    int length = sizeof(charArr4) / sizeof(char);
    cout << length << endl;
    //通过观察长度,我们发现了一个潜在规则
    //对字符数组用字符串去赋值时,会默认在尾部加上一个\0的转移字符
    //代表字符串的结束

    char charArr5[5];
    charArr5[0] = 'A';
    charArr5[1] = 'B';
    //charArr5[2] = 'X';
    //charArr5[3] = 'C';
    //charArr5[4] = '\0';
    //如果不对字符数组后面的内容进行初始化 并且其中没有\0
    //在打印时就不知道何时结束,就会不停的往后读取
    //因为没有明确的终止符,程序会根据内存中是否存在 '\0' 停止打印
    //可能打印出多余字符,甚至是乱码
    //也就是说可能出现访问到字符数组范围之外的内存区域,产生未定义的行为
    cout << charArr5[2] << endl;
    cout << charArr5 << endl;
    //关于“烫”字符
    //在内存中,汉字字符使用多字节编码(比如 UTF-8 或 GBK)
    //当程序读取到这些内存内容时,会尝试解释它们。
    //由于编码规则,有可能形成合法的汉字编码,导致控制台上显示出汉字或其他非预期字符
    cout << "****************************" << endl;



    #pragma endregion

    #pragma region 知识点一 字符数组公共方法


    //C++中内置一些关于字符数组的方法
    //方便我们对其进行操作
    //注意:
    // 这些方法其实都是C语言的方法 大家了解即可


    //1.strcat_s(字符数组, 字符数组长度, 字符串)
    //  作用:将一个字符串连接到另一个字符数组中
    //  如果字符数组长度不够会报错
    // 操作前:str1 = "123"(长度3),str2 = "456"(长度3),str1的可用空间为7(sizeof(str1)=10)
    char str1[10] = "123";
    char str2[10] = "456";
    strcat_s(str1, sizeof(str1), str2);
    cout << str1 << endl;  // 输出:123456

    //2.strcpy_s(字符数组, 字符数组长度, 字符串)
    //  作用:将一个字符串复制到另一个字符数组中
    //  如果字符数组长度不够会报错
    // 操作前:str1 = "123456"(长度6),目标字符串"7777777"长度7,str1的可用空间为3(sizeof(str1)=10,当前占用6)
    strcpy_s(str1, sizeof(str1), "7777777");
    cout << str1 << endl;  // 输出:7777777

    //3.strcmp(字符数组1, 字符数组2)
    //  作用:比较两个字符数组是否相同
    //  0 代表相同,非0代表不同
    // 操作前:str1 = "7777777",比较目标为"7777777"
    cout << strcmp(str1, "7777777") << endl;  // 输出:0

    //4.strlen(字符数组)
    //  作用:得到字符数组实际长度(不包含\0)
    // 操作前:str1 = "7777777"
    cout << strlen(str1) << endl;  // 输出:7

    #pragma endregion




    #pragma region 知识点二 string相关方法


    //1.拼接(C++入门中学习过)
    //  通过 + 或者 append方法进行拼接
    // 操作前:string1 = "123",string2 = "456"
    string string1 = "123";
    string string2 = "456";
    string1.append(string2);
    cout << string1 << endl;  // 输出:123456

    //2.返回字符串长度
    //  size()/length()
    // 操作前:string1 = "123456"
    cout << string1.size() << endl;  // 输出:6
    cout << string1.length() << endl;  // 输出:6

    //3.判断字符串是否为空
    //  empty()
    // 操作前:string3 = "1"
    string string3 = "1";
    cout << string3.empty() << endl;  // 输出:0(false)

    //4.清空字符串
    //  clear()
    // 操作前:string3 = "1"
    string3.clear();
    cout << string3 << endl;  // 输出:(空字符串)

    //5.返回容量
    //  capacity()
    // 操作前:string3 = "1234567890123456789"
    string3 = "1234567890123456789";
    cout << string3.capacity() << endl;  // 输出:31(不同环境可能不同)

    //6.在指定位置插入字符串
    //  insert(位置索引, 字符串)
    //  注意:不要超出最大位置(包含\0考虑)
    // 操作前:string3 = "1234567890123456789"
    string3.insert(1, "哈哈");
    cout << string3 << endl;  // 输出:1哈哈234567890123456789
    // 操作前:string3 = "1哈哈234567890123456789"
    string3.insert(23, "嘻嘻嘻");
    cout << string3 << endl;  // 输出:1哈哈234567890123456789嘻嘻嘻

    //7.从指定索引位置起删除固定个长度个字符
    //  erase(位置索引, 长度)
    //  注意:不要超出最大位置(包含\0考虑)
    // 操作前:string3 = "1哈哈234567890123456789嘻嘻嘻",长度为26
    cout << string3.size() << endl;  // 输出:26
    string3.erase(23, 6);  // 修正:原代码中的索引29超出范围
    cout << string3 << endl;  // 输出:1哈哈234567890123456789

    //8.替换指定区间为另一个字符串
    //  replace(开始位置索引,长度,字符串) 
    // 操作前:string3 = "1哈哈234567890123456789"
    string3.replace(0, 10, "123456");
    cout << string3 << endl;  // 输出:12345667890123456789

    //9.交换两个字符串
    //  swap(另一个字符串)
    // 操作前:string1 = "123456",string2 = "456"
    cout << string1 << endl;  // 输出:123456
    cout << string2 << endl;  // 输出:456
    string1.swap(string2);
    cout << string1 << endl;  // 输出:456
    cout << string2 << endl;  // 输出:123456

    //10.删除最后一个字节 需要注意 破坏多个字节字符的问题
    //  pop_back();
    // 操作前:string3 = "12345667890123456789"
    string3.push_back('1');
    cout << string3 << endl;  // 输出:123456678901234567891
    // 操作前:string3 = "123456678901234567891"
    string3.pop_back();
    cout << string3 << endl;  // 输出:12345667890123456789

    //11.添加单个字符
    //  push_back(字符)
    // 操作前:string3 = "12345667890123456789"
    string3.push_back('A');
    cout << string3 << endl;  // 输出:12345667890123456789A

    //12.查找字符串第一次出现的位置 如果找不到返回的是-1 但是一定基础最好用一个数值变量去存储它
    //   find(字符或字符串)
    // 操作前:string4 = "12345627"
    string string4 = "12345627";
    int i = string4.find("2");
    cout << i << endl;  // 输出:1

    //13.查找最后一次出现的位置(从尾部找) 如果找不到返回的是-1 但是一定基础最好用一个数值变量去存储它
    //   rfind(字符或字符串)
    // 操作前:string4 = "12345627"
    i = string4.rfind("2");
    cout << i << endl;  // 输出:6

    //14.查找首次/最后一次 出现传入字符串中 任一字符的位置
    //   首次:find_first_of(字符串)
    //   最后一次:find_last_of(字符串)
    // 操作前:string4 = "12345627"
    string4 = "12345627";
    i = string4.find_first_of("478");
    cout << i << endl;  // 输出:3
    i = string4.find_last_of("478");
    cout << i << endl;  // 输出:7

    //15.查找第一个/最后一个 不在传入字符串中的字符
    //   第一个:find_first_not_of(字符串)
    //   最后一个:find_last_not_of(字符串)
    // 操作前:string4 = "12345627"
    i = string4.find_first_not_of("12367");
    cout << i << endl;  // 输出:3
    i = string4.find_last_not_of("2367");
    cout << i << endl;  // 输出:4

    //16.截取字符串
    //   substr(起始位置,长度)
    // 操作前:string4 = "12345627"
    string4 = "12345627";
    string string5 = string4.substr(1, 4);
    cout << string5 << endl;  // 输出:2345

    //17.字符串转字符数组
    //  data()
    // 操作前:string4 = "12345627"
    const char* charPtr = string4.data();
    for (int i = 0; i < string4.size(); i++)
    {
        cout << charPtr[i] << endl;  // 逐行输出:1 2 3 4 5 6 2 7
    }

    //18.判断字符串内容是否相等
    //  ==/!=
    // 操作前:string1 = "456",string2 = "123456"(经过swap操作后)
    string1 = "123";
    string2 = "123";
    if (string1 == string2)
        cout << "相等" << endl;  // 输出:相等

    #pragma endregion

    #pragma region 知识点三 string的本质

    //stirng的本质就是一个C++中封装的一个管理字符数组的类
    //它封装了动态内存分配、长度管理、字符串操作函数等复杂逻辑
    //因此在现代C++开发当中,都建议大家使用string来替代字符数组
    //因为这样更高效,安全

    #pragma endregion
}

32.3 练习题

写出 string 中提供的截取和替换对应的函数名

C++ 中 std::string 提供了多个用于字符串截取与替换的函数:

  • substr(pos, len):用于从字符串中截取子串,pos 是起始位置,len 是长度;如果省略 len,则从 pos 开始截取到末尾。
  • replace(pos, len, str):用于从字符串中替换部分内容,从 pos 开始替换 len 个字符为新的字符串 str

示例代码如下:

// 操作前:str = "123456789"
string str = "123456789";

// substr(5):从索引5开始截取到末尾
str = str.substr(5);
cout << str << endl;  // 输出:6789

// 替换字符串
// 操作前:str = "6789"
// replace(0, 2, "哈哈"):从索引0开始的2个字符替换为"哈哈"
str.replace(0, 2, "哈哈");
cout << str << endl;  // 输出:哈哈89

将字符串 "1|2|3|4|5|6|7" 变为 "2|3|4|5|6|7|8" 并输出

目标是抛弃第一个数字(即 1),并在末尾添加 8,通过以下步骤实现:

// 操作前:str2 = "1|2|3|4|5|6|7"
string str2 = "1|2|3|4|5|6|7";

// 找到第一个 '|' 的索引,结果为 1
int index = str2.find('|');

// substr(index + 1):从索引2开始截取到末尾,即 "2|3|4|5|6|7"
str2 = str2.substr(index + 1);
cout << str2 << endl;  // 输出:2|3|4|5|6|7

// 找到最后一个 '|' 的索引,结果为 8
index = str2.rfind('|');

// substr(index + 1):截取最后一个数字,结果为 "7"
string lastStr = str2.substr(index + 1);

// 将 "7" 转换为整数
int lastNum = stoi(lastStr);

// 让最后一个数字加1,得到8
++lastNum;

// 拼接字符串,追加 |8
str2 += "|" + to_string(lastNum);
cout << str2 << endl;  // 输出:2|3|4|5|6|7|8

编写一个函数,将输入的字符串反转(不使用额外空间

要求是原地修改,即不能开辟新空间用于反转,且不允许使用中间变量交换字符。

例如:输入 hello,输出为 olleh

// 操作前:str3 = 用户输入的字符串
string str3;
cin >> str3;

// 设置两个指针,分别从字符串的两端向中间靠拢
int left = 0;
int right = str3.size() - 1;

while (left < right)
{
    // 不使用额外变量交换两个字符
    // 使用ASCII码值进行交换,过程如下:
    
    // 第一步:左字符 = 左字符 + 右字符
    str3[left] = str3[left] + str3[right];
    
    // 第二步:右字符 = 左字符 - 右字符(即原左字符)
    str3[right] = str3[left] - str3[right];
    
    // 第三步:左字符 = 左字符 - 右字符(即原右字符)
    str3[left] = str3[left] - str3[right];
    
    ++left;
    --right;
}

cout << str3 << endl;  // 输出:反转后的字符串

说明

  • 本方法使用加减法交换两个变量的值,省去了使用中间变量。
  • 这种方式仅适用于基本数据类型(如 char),且在字符值不溢出时安全。
  • 优点是空间复杂度为 O(1),无需额外内存。

32.4 练习题代码

Lesson32_练习题.cpp

#include <iostream>
#include <string>
using namespace std;
int main()
{
    #pragma region 练习题一
    // 请写出string中提供的截取和替换对应的函数名

    // 截取字符串
    // 操作前:str = "123456789"
    string str = "123456789";
    // substr(5) 从索引5开始截取到末尾
    str = str.substr(5);
    cout << str << endl;  // 输出:6789

    // 替换字符串
    // 操作前:str = "6789"
    // replace(0, 2, "哈哈") 从索引0开始的2个字符替换为"哈哈"
    str.replace(0, 2, "哈哈");
    cout << str << endl;  // 输出:哈哈89

    #pragma endregion

    #pragma region 练习题二
    /*请将字符串   1|2|3|4|5|6|7
    变为          2|3|4|5|6|7|8
    并输出*/

    // 1.截取后面的内容,相当于抛弃掉1|
    // 操作前:str2 = "1|2|3|4|5|6|7"
    string str2 = "1|2|3|4|5|6|7";
    int index = str2.find('|');  // 找到第一个'|'的索引,结果为1
    // substr(index + 1) 从索引2开始截取到末尾
    str2 = str2.substr(index + 1);
    cout << str2 << endl;  // 输出:2|3|4|5|6|7

    // 2.在最后一个数字的基础上去加上一个 n + 1 ,并且前面有一个 |
    // 操作前:str2 = "2|3|4|5|6|7"
    index = str2.rfind('|');  // 找到最后一个'|'的索引,结果为8
    // substr(index + 1) 截取最后一个数字
    string lastStr = str2.substr(index + 1);  // lastStr = "7"
    int lastNum = stoi(lastStr);  // 转换为整数7
    ++lastNum;  // 加1后变为8
    // 拼接字符串
    str2 += "|" + to_string(lastNum);
    cout << str2 << endl;  // 输出:2|3|4|5|6|7|8

    #pragma endregion

    #pragma region 练习题三
    /*编写一个函数,将输入的字符串反转。
    不要使用中间商,你必须原地修改输入数组。交换过程中不使用额外空间
    比如:输入{ ‘h’,‘e’,‘l’,‘l’,‘o’ }
    输出{ ‘o’,‘l’,‘l’,‘e’,‘h’ }*/

    // 操作前:str3 = 用户输入的字符串
    string str3;
    cin >> str3;
    int left = 0;
    int right = str3.size() - 1;
    while (left < right)
    {
        // 不使用额外变量交换两个字符
        // 得到left和right ASCII码的总和 left + right
        str3[left] = str3[left] + str3[right];
        // left + right - right = left
        str3[right] = str3[left] - str3[right];
        // left + right - left = right
        str3[left] = str3[left] - str3[right];
        ++left;
        --right;
    }
    cout << str3 << endl;  // 输出:反转后的字符串

    #pragma endregion
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏