C++编程基础(五):字符数组和字符串
文章目录
- 一、C 风格字符串(字符数组)
- 1. 定义与初始化
- 2.字符和字符串的区别
- 3.字符数组的地址
- 4.字符数组的输入输出
- 安全的行输入:cin.getline()
- 5. 常用 C 风格字符串函数
- 6. 字符检查
- 二、std::string 类
- 1.构造与初始化
- 2.常用操作
- 访问与长度
- 修改:拼接与附加
- 修改:插入、删除、替换
- 比较
- 查找与子串
- 输入输出
- 字符串反转
- 三、字符串与数值的转换
- 1. 字符串 -> 数值
- 传统 C 方式 (`<cstdlib>`)
- 现代 C++ 方式 (C++11+, `<string>`)
- 2. 数值 -> 字符串 (C++11+, `<string>`)
- 四、字符数组 vs. 字符串
在 C++ 中处理文本数据主要有两种方式:继承自 C 语言的字符数组(C 风格字符串),以及 C++ 标准库提供的 std::string 类。std::string 提供了更强大、更安全、更易用的接口,是现代 C++ 编程的首选。
本文将详细介绍这两种方式的定义、使用和关键区别。
一、C 风格字符串(字符数组)
C 风格字符串本质上是一个以特殊字符 \0(称为 NULL 终止符)结尾的 char 数组。
1. 定义与初始化
字符数组的声明与其他数组类似,但可以使用字符串字面量(用双引号括起)来快速初始化。
// 数据类型 数组名[长度];
char arr1[10];// 方式一:使用字符串字面量初始化
// 编译器会自动在末尾添加 \0
char arr2[10] = "hello"; // arr2 内容为 {'h', 'e', 'l', 'l', 'o', '\0', ?, ?, ?, ?}
char arr3[] = "world"; // 编译器自动推断长度为 6 (world 5个 + \0 1个)// 方式二:使用字符列表初始化
// 必须手动添加 \0 才能作为字符串使用
char arr4[10] = {'a', 'b', 'c', '\0'};
2.字符和字符串的区别
char是字符,也是0~127的无符号整数。通常能用一个char表示的被称为ASCII编码。
字符串是以NULL结尾的连续地址。常用转义字符\0表示。
为字符串字面量分配空间时,必须确保数组长度至少为字符数 + 1(为 \0 留出空间)。
char arrName[5] = "abcde"; // 错误!没有空间存放 \0!
char arrName[6] = "abcde"; // 正确(5 个字符 + 1 个 \0)
char arrName[5] = {'a', 'b', 'c', 'd', 'e'}; // 正确,是字符数组,不是字符串
3.字符数组的地址
对于字符数组,不能直接取地址,否则输出的是整个字符串,直到遇到\0。所以就算是普通的char单个字符地址,我们也需要(void*)来强转它的类型。(void*)为无类型指针,一般被称为通用指针或泛指针。
char c1[10] = "abcde";
char c2[10] = {'a', 'b', 'c', 'd', 'e'};
char c3[10] = {"abcde"};
cout << c1 << endl; // abcde
cout << &c1[0] << endl; // abcde
cout << &c1[1] << endl; // bcdecout << (void*)c1 << endl; //
cout << (void*)&c1[0] << endl;
cout << (void*)&c1[1] << endl;
4.字符数组的输入输出
cin 与 cout
cout在遇到char*类型时,会假定它是一个 C 风格字符串,并打印直到\0为止。cin在读取char[]时,会以空格、制表符或换行符作为分隔符,导致无法读取包含空格的完整句子。
char c[100];cin >> c; // 如果输入 ab scdd
cout << c; // 输出 ab
安全的行输入:cin.getline()
要读取包含空格的一整行,可以使用 cin.getline()。
char line[100];
cin.getline(line, 100); // 最多读取 99 个字符(为 \0 留空cout << line << std::endl; // 输出与输入一样
return 0;
5. 常用 C 风格字符串函数
需要包含头文件 <cstring>。
| 函数 | 描述 |
|---|---|
strlen(p) | 返回字符串 p 的长度(不包括 \0)。 |
strcpy(p1, p2) | 将 p2 复制到 p1。 |
strncpy(p1, p2, n) | 最多将 p2 的 n 个字符复制到 p1。 |
strcat(p1, p2) | 将 p2 拼接到 p1 的末尾。 |
strncat(p1, p2, n) | 最多将 p2 的 n 个字符拼接到 `p1。 |
strcmp(p1, p2) | 按字典序比较 p1 和 p2(p1<p2 返回-1, ==返回0, p1>p2 返回1)。 |
strncmp(p1, p2, n) | 比较前 n 个字符。 |
stricmp(p1, p2)/strcasecmp(p1, p2) | 不区分大小写比较两个字符串 |
strnicmp(p1, p2, n) | 不区分大小写比较字符串p1和p2前n个字符。 |
strchr(p, c) | 查找 c 在 p 中首次出现的位置,返回指针,否则返回 NULL。 |
strrchr(p, c) | 查找 c 在 p 中最后出现的位置。 |
strlwr | 将字符串中大写字母转成小写字母 |
strupr | 将字符串中小写字母转成大写字母 |
memset(void *s, int c, size_t n) | 将 s 指向的 n 字节内存设为字节值 c;常用于清零,仅对 0 或 -1 安全用于多字节类型。 |
strcpy和strcat假定目标数组p1有足够的空间,如果空间不足,它们会写入数组边界之外,破坏内存。在 C++ 中应避免使用它们,优先使用std::string。memset按字节赋值,假设 int 是 4 字节,那么每个 int 会被设为 0x01010101(十进制 16843009),而不是 1。
6. 字符检查
头文件 <cctype> 提供了一系列用于检查单个字符类型的函数。
| 函数 | 作用 |
|---|---|
isalpha(int c) | 检查字符是否为字母(大小写) |
isupper(int c) | 检查是否为大写字母(‘A’–‘Z’) |
islower(int c) | 检查是否为小写字母(‘a’–‘z’) |
isdigit(int c) | 检查是否为十进制数字(‘0’–‘9’) |
isxdigit(int c) | 检查是否为十六进制数字(‘0’–‘9’, ‘a’–‘f’, ‘A’–‘F’) |
isspace(int c) | 检查是否为空白字符(空格、换行、回车、制表等) |
iscntrl(int c) | 检查是否为控制字符(如 \n, \r, \t, \0 等) |
ispunct(int c) | 检查是否为标点符号(可打印但非字母、数字、空格) |
isalnum(int c) | 检查字符是否为字母或数字(alphanumeric) |
isprint(int c) | 检查是否为可打印字符(包括空格) |
isgraph(int c) | 检查是否为图形字符,等效于isalnum(int c) |
二、std::string 类
std::string 是 C++ 标准库中的类(在 <string> 头文件中),定义隐藏了字符串的数组性质,可以像处理普通变量一样处理字符串。string对象和字符数组的主要区别是:可以将string对象声明为简单变量,而不是数组。
1.构造与初始化
// 1. 默认构造
std::string s1; // s1 = ""// 2. C 风格字符串构造
std::string s2 = "Hello";
std::string s3("World");// 3. 复制构造
std::string s4(s2); // s4 = "Hello"
std::string s5 = s3; // s5 = "World"// 4. 填充构造 (n 个 c)
std::string s6(10, 'A'); // s6 = "AAAAAAAAAA"
2.常用操作
std::string 提供了丰富的成员函数来处理字符串。
访问与长度
std::string s = "Hello";// 获取长度
std::cout << s.length() << std::endl; // 5
std::cout << s.size() << std::endl; // 5 (与 length 相同)// 判断是否为空
std::cout << s.empty() << std::endl; // 0 (false)// 访问单个字符 (同数组)
std::cout << s[1] << std::endl; // 'e'// 安全访问 (带边界检查,会抛出异常)
std::cout << s.at(1) << std::endl; // 'e'
修改:拼接与附加
std::string s1 = "Hello";
std::string s2 = "World";// 1. 使用 + 运算符
std::string s3 = s1 + " " + s2; // s3 = "Hello World"// 2. 使用 += 运算符
s1 += "!"; // s1 = "Hello!"// 3. 使用 append()
s2.append(" C++"); // s2 = "World C++"// 4. 使用 push_back() (仅限单个字符)
s2.push_back('!'); // s2 = "World C++!"
修改:插入、删除、替换
std::string s = "Hello C++";// 1. 插入 (insert)
s.insert(6, "Beautiful "); // 在索引 6 处插入
// s = "Hello Beautiful C++"// 2. 删除 (erase)
s.erase(6, 10); // 从索引 6 开始,删除 10 个字符
// s = "Hello C++"// 3. 替换 (replace)
s.replace(6, 3, "World"); // 从索引 6 开始,替换 3 个字符
// s = "Hello World"
比较
可以直接使用关系运算符进行字典序比较。
std::string s1 = "abc";
std::string s2 = "abd";
if (s1 < s2) {std::cout << "s1 小于 s2" << std::endl;
}
if (s1 == "abc") {std::cout << "s1 等于 \"abc\"" << std::endl;
}
查找与子串
find() 是最常用的查找函数,如果找不到,它返回一个特殊值 std::string::npos。
std::string s = "Hello World, World";// 1. 查找 (find)
size_t pos1 = s.find("World"); // 从头开始查找
if (pos1 != std::string::npos) {std::cout << "第一次出现 'World' 的索引: " << pos1 << std::endl; // 6
}// 2. 反向查找 (rfind)
size_t pos2 = s.rfind("World"); // 从末尾开始查找
std::cout << "最后一次出现 'World' 的索引: " << pos2 << std::endl; // 13// 3. 截取子串 (substr)
std::string sub = s.substr(6, 5); // 从索引 6 开始,截取 5 个字符
std::cout << "子串: " << sub << std::endl; // "World"
输入输出
cin 同样会遇到空格停止。读取整行应使用 std::getline。
string str1,str2;
//读入方式1遇到换行停止
getline(cin, str1);
//读入方式2遇到空格停止
cin>>str2;//输出方式1
printf("%s\n",str1.c_str());
//输出方式2
cout<<str2<<end1;
字符串反转
使用<algorithm> 头文件里面的reverse函数
#include <iostream>
#include <algorithm>
using namespace std;int main() {string s1;cin >> s1;reverse(s1.begin(),s1.end());cout << s1 << endl;return 0;
}
三、字符串与数值的转换
1. 字符串 -> 数值
传统 C 方式 (<cstdlib>)
这些函数不进行错误检查。如果转换失败,通常返回 0。
atoi(p): 字符串 (const char*) 转intatof(p): 字符串转doubleatol(p): 字符串转long
现代 C++ 方式 (C++11+, <string>)
这些函数更安全,如果无法转换会抛出异常。
std::stoi(s):std::string转intstd::stod(s):std::string转doublestd::stol(s):std::string转long
#include <string>
#include <iostream>int main() {std::string s_num = "123.45";int i = std::stoi(s_num); // 转换 "123" (遇到 . 停止)double d = std::stod(s_num); // 转换 "123.45"std::cout << "Int: " << i << ", Double: " << d << std::endl;return 0;
}
2. 数值 -> 字符串 (C++11+, <string>)
使用 std::to_string 是最简单的方式。
#include <string>
#include <iostream>int main() {int i = 42;double pi = 3.14;std::string s_i = std::to_string(i); // "42"std::string s_pi = std::to_string(pi); // "3.140000"std::cout << s_i << " 和 " << s_pi << std::endl;return 0;
}
四、字符数组 vs. 字符串
| 特性 | C 风格字符串 (char[]) | std::string |
|---|---|---|
| 内存管理 | 手动(大小固定,易溢出) | 自动(动态调整大小) |
| 安全性 | 低(strcpy, gets 等) | 高(边界检查,at()) |
| 功能 | 基础(需 ) | 丰富(查找、替换、插入等) |
| 易用性 | 复杂,易错 | 简单直观 |
