C++青少年简明教程:字符类型、字符数组和字符串
在 C++ 语言中,处理文本数据的基础是字符类型 char,字符数组,以及标凌库中的字符串类 std::string。
C++中的char类型占用 1 字节的内存空间,用于存储单个ASCII字符。例如,字符'A'在ASCII编码中对应的值是65。
字符数组是一个可以存储多个字符的连续内存空间。你可以使用字符数组来存储一个字符串(即一系列字符,以空字符'\0'结尾)。如:
char str[10] = "Hello"; // 注意,这里实际上存储了"Hello\0",即字符串后有一个空字符作为结束符
字符数组可以通过循环逐个访问其元素,但在处理时必须小心维护 '\0' 结束符,以防止出现越界错误。字符数组通常被称为 C-风格字符串(C-style strings)。这种字符串表示方式来源于 C 语言,在 C++ 中仍然被广泛支持,主要因为历史兼容性和操作系统接口的要求。
字符串类,在C++中,std::string是一个类,用于处理字符串。它是C++标准库中的一部分,提供了许多方便的方法来操作字符串,如连接、比较、查找等。使用 std::string 类可以大大简化字符串的操作,并提高代码的安全性和可读性。
字符类型
在C++中,字符类型使用关键字char来定义。字符类型表示单个字符,包括数字、字母、符号等。字符类型的大小是一个字节(8位)。
字符类型变量只能存储一个字符,例如:
char c = 'A'; // 定义字符类型变量c,存储值为'A'
注意使用字符类型数据确实使用单引号(' ')。
空字符的 ASCII 码值为 0,因此,可以使用以下语句来表示空字符:
char c = '\0';
字符数组
字符数组是由字符组成的一维数组,包含多个字符元素。字符数组也有一维、二维、三维之分。
一维字符数组
c++中,可以使用字符类型数组来存储多个字符。字符类型数组是由多个字符类型变量组成的有序集合,即一串字符。可以使用花括号和逗号分隔符来初始化字符类型数组,也可以使用双引号来初始化字符类型数组,这样可以更容易地输入一串字符,而无需逐个输入每个字符。例如:
char str1[] = {'H', 'e', 'l', 'l', 'o'}; // 定义字符类型数组str1,但不是一个字符串
char str2[] = {'H', 'e', 'l', 'l', 'o', '\0'}; // 定义字符类型数组str2,并且是一个字符串
char str3[] = "Hello"; // 定义并双引号进行初始化字符类型数组str3,并且是一个字符串
str2和str3是等价的。
字符类型数组可以用来存储字符串,字符数组以空字符('\0')作为结尾标识,表示字符串的结束。在使用双引号进行初始化时,编译器会自动在字符数组末尾添加一个空字符'\0',也就是说使用字符串字面值来初始化字符类型数组时,不需要明确地添加空字符。
一维字符数组和字符串的不同点:
1.字符串以空字符('\0')作为字符串的结束符,而字符数组没有特定的结束符。
2.字符串可以使用双引号("")来初始化,例如char str[] = "hello",而字符数组则需依次赋值,例如char arr[] = {'h', 'e', 'l', 'l', 'o'}或者char arr[] = "hello";(这种方式会在数组的末尾自动添加一个空字符)。
3.字符串可以使用一些针对字符串类型的函数,例如strlen()、strcpy()、strcmp()等等。字符数组也可以使用这些函数,但是需要对其具体的长度、空字符进行处理。
尽管字符数组可以用来处理字符串,但在C++中,通常推荐使用string类。
一维字符数组初始化
1.使用字符串字面值进行初始化:
char str[] = "Hello";
这会创建一个字符数组str,并将字符串字面值"Hello"复制到该数组中。注意,字符数组的大小会根据字符串的长度自动确定。
2.使用花括号初始化列表进行初始化:
char str[] = {'H', 'e', 'l', 'l', 'o', '\0'};
这里使用花括号{}来指定字符数组的初始元素,包括最后一个null字符('\0')。
3.逐个赋值:
char str[6];
str[0] = 'H';
str[1] = 'e';
str[2] = 'l';
str[3] = 'l';
str[4] = 'o';
str[5] = '\0';
这种方式需要逐个给字符数组的元素赋值,并确保最后一个元素是null字符('\0')。
4.使用strcpy函数进行赋值(需要包含头文件<cstring>):
char str[6];
strcpy(str, "Hello");
注意,无论使用哪种方式,都需要保证字符数组的容量足够大,能够容纳赋值的字符串。另外,字符串赋值时要确保最后一个字符是字符串的结尾符 '\0'即null字符。
一维字符数组和字符串(string)的关系
在C/C++中,字符串实际上是以字符数组的形式存储的,以null字符('\0')作为字符串的结束标志。因此,可以说字符串是字符数组的一种特殊情况。一维字符数组可以用于存储任意的字符序列,而字符串则是一个以null字符结尾的字符数组。
字符数组可以通过初始化或赋值来定义,而字符串只能通过初始化来定义。字符数组是定长的,而字符串长度可变。
注意,下面两句是有区别的:
char str[6] = {'H', 'e', 'l', 'l', 'o'};
char str[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
都是合法的。
第一种写法定义了一个包含5个字符的字符数组,初始化为指定的字符序列,但没有明确指定空字符('\0')。如果将其作为字符串使用,可能会导致访问越界或其他错误。
第二种写法定义了一个包含5个字符和一个空字符的字符数组,并且明确地将最后一个元素设置为空字符('\0')。这样就确保了数组以空字符结尾,可以被正确地当作字符串来处理。
【顺便提示:C语言中只有一种方式来表示字符串,那就是使用C-style string的字符数组(char array)。
C++中处理字符串的两种都可以:
1.C-style string的字符数组(char array):使用字符数组来表示和处理字符串。字符数组以null字符 '\0' 结尾,可以使用C风格的字符串函数来进行操作。这种方式需要手动管理内存,并且对于较长的字符串长度可能会有限制。
2.使用 string 类型:C++标准库提供了 string 类型(std::string),它是一个类模板。可以通过包含 <string> 头文件来使用 string 类型。string 类型提供了许多方便的字符串操作方法,它为字符串的处理提供了更高级的抽象和功能。】
一维字符数组简单示例:
#include <iostream>
using namespace std;int main() {char str[] = "Hello, World!"; //长度自动确定cout << str << endl; // 输出:Hello, World!return 0;
}
使用标准库函数printf、scanf、puts、gets等完成字符数组的输入输出需要引入<cstdio>头文件。该头文件是对<stdio.h>头文件的C++版本支持,其中包含了许多对输入输出操作有用的函数及变量。详情可参考 “C++的输入输出”一节。
★使用printf和scanf完成字符数组的输入输出:
#include <cstdio>int main() {char str[50];// 从标准输入读取一个字符串到字符数组中printf("请输入字符串:");scanf("%s", str);// 将字符数组的内容打印到标准输出中printf("您输入的字符串是:%s\n", str);return 0;
}
★使用puts和gets完成字符数组的输入输出
#include <cstdio>int main() {char str[50];// 从标准输入读取一个字符串到字符数组中printf("请输入字符串:");gets(str);// 将字符数组的内容打印到标准输出中puts("您输入的字符串是:");puts(str);return 0;
}
C++ 标准库提供了一些函数用来处理以 null 结尾的字符数组(C 风格字符串)。这些函数包括在 <cstring>(或 <string.h>)头文件中。常用的如:
strlen :用于返回指定字符串的长度(不包括空字符)。
strcpy :用于将一个字符串复制到另一个字符数组中。
strcat :用于将两个字符串进行拼接。
strcmp :用于比较两个字符串,并返回它们之间的区别。
strchr :用于在字符串中查找给定字符的第一个出现位置。
strstr :用于在字符串中查找给定字符串的第一个出现位置。
提示:这些函数需要引入 <cstring> (或 <string.h>)头文件。该头文件包含了 C++标准库中与C语言字符串操作相关的函数的声明。
例
#include <iostream>
#include <cstring>
using namespace std;int main() {// 定义两个C风格字符串char str1[] = "Hello, World!";char str2[] = "C++";// 使用strlen计算字符串长度int length1 = strlen(str1);int length2 = strlen(str2);cout << "字符串1长度: " << length1 << endl;cout << "字符串2长度: " << length2 << endl;// 使用strcpy复制字符串char str3[100];strcpy(str3, str1);cout << "复制后的字符串: " << str3 << endl;// 使用strcat拼接字符串char str4[100];strcpy(str4, str1);strcat(str4, ", ");strcat(str4, str2);cout << "拼接后的字符串: " << str4 << endl;// 使用strcmp比较字符串if (strcmp(str1, str2) == 0) {cout << "两个字符串相等" << endl;} else {cout << "两个字符串不相等" << endl;}// 使用strchr查找字符char* found = strchr(str1, 'W');if (found != nullptr) {cout << "找到字符: " << *found << endl;} else {cout << "未找到字符" << endl;}// 使用strstr查找字符串char* found2 = strstr(str1, str2);if (found2 != nullptr) {cout << "找到字符串: " << found2 << endl;} else {cout << "未找到字符串" << endl;}return 0;
}
运行输出:
字符串1长度: 13
字符串2长度: 3
复制后的字符串: Hello, World!
拼接后的字符串: Hello, World!, C++
两个字符串不相等
找到字符: W
未找到字符串
对于字符数组重点掌握一维字符数组,下面简要介绍二维字符数组。
二维字符数组最常见的初始化方式
1:逐个初始化
char charArray1[2][3] = {{'a', 'b', 'c'}, {'d', 'e', 'f'}};
2:使用字符串字面值初始化
char charArray2[2][3] = {"ab", "cd"};
二维字符数组简单示例
#include <iostream>
using namespace std;int main() {// 二维字符数组char str[][10] = {"Hello", "World"}; // 大小为2x10cout << "str[0]: " << str[0] << endl;cout << "str[1]: " << str[1] << endl;return 0;
}
输出:
str[0]: Hello
str[1]: World
字符串类(std::string)
一般推荐在 C++ 程序中使用 std::string 类来处理字符串,除非有特定的性能要求或者需要与旧的 C 代码库互操作。利用 std::string 提高开发效率和程序的安全性。
要使用 std::string 类,你需要引入的是 <string> 头文件。
在C++中,string类是标准库提供的字符串类,它在<string>头文件中定义。string类提供了一系列成员函数和操作符,用于处理和操作字符串。string不属于STL(Standard Template Library)中的容器类,而是C++标准库(Standard Library)中的一个类,用于表示和操作字符串。
你可以很方便地使用string类进行字符串的赋值、拼接、查找、替换等操作,而不需要手动管理内存或考虑字符串长度的限制。
赋值和拼接:可以使用=操作符进行字符串的赋值,也可以使用+操作符进行字符串的拼接。
访问字符:可以使用下标操作符[]来访问字符串中的单个字符,并可以修改它们。
字符串长度:使用length()或size()成员函数可以获取字符串的长度。
比较字符串:可以使用==、!=、<、>、<=、>=等操作符进行字符串的比较。
查找和替换:string类提供了find()、rfind()、replace()等成员函数,用于在字符串中查找指定的子串并进行替换。
【https://cplusplus.com/reference/string/string/ 】
可以使用不同的构造函数(constructor)【注:在C++中,构造函数是一种特殊的成员函数,它在创建类的对象时被自动调用。构造函数的主要目的是初始化对象的数据成员】来创建不同的字符串。这些是一些常见的方法示例:
☆默认构造函数(无参构造函数):例如:
string str; //创建一个空的string对象。
string str = "Hello World!"; //创建一个的string对象并赋值初始化
☆带字符串参数的构造函数:创建一个string对象,并将其初始化为一个给定的字符串。例如:
string str("Hello");
☆带字符参数和个数的构造函数:创建一个string对象,并将其初始化为指定个数的相同字符。例如:
string str(5, 'a'); // str = "aaaaa"
☆拷贝构造函数:使用一个已有的string对象创建一个新的string对象。例如:
string str1("Hello");
string str2(str1); // str2 = "Hello"
下面介绍string 类中提供的 getline 函数。
getline()函数语法如下:
istream& getline (istream& is, string& str, char delim);
其中,is是输入流对象;str是目标字符串对象;delim是可选参数,表示定界符(分隔符,默认为换行符)。
使用getline()函数时,它会从输入流中读取一行文本,并将读取到的内容存储到目标字符串str中,直到遇到定界符(或文件结尾)为止。如果指定了定界符delim,则getline()函数会在遇到该字符时停止读取,否则默认为换行符。
例
#include <iostream>
#include <string>
using namespace std;int main() {string str;cout << "请输入名字:";getline(cin, str); //cin >> str;// 检查字符串是否为空if (str.empty()) {cout << "The string is empty." << endl;} else {cout << "The string is not empty." << endl;}return 0;
}
运行效果:
特别说明:c++中,cin >> str 和 string类的getline(cin, str) 用来读取用户输入的两种不同方式的不同点:
数据类型不同:cin >> str是用来读取字符串类型的单个单词或数字型数据(整数、实数等),而getline(cin, str)则是用来读取整行的字符串。
分隔符:使用cin >> str时,输入被空白字符(如空格、制表符、换行符)分隔为不同的字符串,并将第一个非空白字符开始的连续字符序列存储到str中,剩余部分将会留在输入队列中;而getline(cin, str)则会读取整行输入,包括空白字符,在遇到换行符之前将其存储到str中。
输入限制:cin >> str只能读取到空白字符之前的内容,因此无法读取含有空格的字符串,一次只能读取一个单词或数字。而getline(cin, str)可以读取包含空格的完整行,因此可以读取多个单词或整行文本。
换行符处理:cin >> str读取后会将换行符留在输入流中,可以影响下一次读取操作。相比之下,getline(cin, str)会将换行符从输入流中移除,以防止影响后续读取操作。
【空白字符是指在文本中没有可见形式的字符,主要包括空格、制表符和换行符。它们在文本处理中具有不同的作用。
空格(Space):表示一个可见的空间,在文本中起到分隔单词或字符的作用。多个连续的空格会被视为一个空格。
制表符(Tab):通常用\t来表示,在文本中占据一个固定的宽度,经常用于对齐文本中的内容。
换行符(Newline):表示换行的特殊字符,在不同的操作系统中可能有不同的表示方式。在Unix和Linux系统中,换行符用\n表示;在Windows系统中,换行符由两个字符组成,即回车符(Carriage Return)和换行符,用\r\n表示。
这些空白字符在输入和输出操作中都需要注意处理。在C++中,空白字符通常用于分隔不同的数据项,而换行符用于标识一行的结束。了解和正确处理这些字符对于正确解析和处理用户输入以及生成正确格式的输出非常重要。】
例2、演示了如何连接字符串、获取字符串长度、检查字符串是否为空、访问和修改字符串中的字符、查找子字符串的位置以及提取子字符串。您可以根据需要进一步使用std::string执行其他操作,例如插入、删除、比较等等。
#include <iostream>
#include <string>
using namespace std;int main() {string str1 = "Hello";string str2 = " world!";// 连接两个字符串string result = str1 + str2;cout << "Concatenated string: " << result << endl;// 获取字符串长度cout << "Length of the string: " << result.length() << endl;// 检查字符串是否为空if (result.empty()) {cout << "The string is empty." << endl;} else {cout << "The string is not empty." << endl;}// 访问字符串中的字符char c = result[0];cout << "First character of the string: " << c << endl;// 修改字符串中的字符result[0] = 'h'; // 将第一个字符修改为小写 'h'cout << "Modified string: " << result << endl;// 查找子字符串的位置size_t pos = result.find("world");if (pos != string::npos) {cout << "Substring 'world' found at position: " << pos << endl;} else {cout << "Substring not found." << endl;}// 提取子字符串string sub = result.substr(6, 5); // 从位置 6 开始,提取长度为 5 的子字符串cout << "Substring: " << sub << endl;return 0;
}
你可以编译运行试试看。