• Index

字符串的数据类型和使用

Reads: 64

为了讲解方便,std::stringstd::wstringstd::u16stringstd::u32string在下面讲解中将统一叫做string系列std::string_viewstd::wstring_viewstd::u16string_viewstd::u32string_view在下面讲解中将统一叫做string_view系列

讲解前先简单介绍一下几个名词,这些名词将会在后续详细讲解:

  1. 函数:调用不同的函数可以执行不同的功能。
  2. 返回值:如果函数有计算结果,那么,函数的计算结果就叫做返回值。
  3. 成员函数:专属于某个类的函数就是成员函数,不同的类有不同的成员函数。
  4. std::string是一个类,它有一个成员函数叫做size()。假设用std::string定义一个对象str,那么就可以这样调用成员函数str.size(),以获取str的大小。
  5. string系列string_view系列都是类。

string系列简单常用的使用方法

以下将讲解字符串数据类型string系列的使用。

string系列字符串数据类型的详细用法请参考以下文档

cplusplus string 系列字符串说明文档

cppreference string 系列字符串说明文档

使用第三方输入输出库(可选)

我们已经知道,标准C++不支持输出UTF-16和UTF-32,这个时候就需要第三方输入输出库。如果搭建开发环境的时候,按照步骤完成了第三方输入输出库的下载和配置,那么就可以使用如下方式输出:

#include <extios/extiostream.h>

int main(void)
{
    extios::wcout << L"这是宽字符串" << std::endl;
    extios::u8cout << u8"这是UTF-8字符串" << std::endl;
    extios::u16cout << u"这是UTF-16字符串" << std::endl;
    extios::u32cout << U"这是UTF-32字符串" << std::endl;
    return 0;
}

输出结果:

这是宽字符串
这是UTF-8字符串
这是UTF-16字符串
这是UTF-32字符串

extios是这个库的名字。可以使用extios::wcout输出宽字符字符串;使用extios::u8cout输出UTF-8字符串;使用extios::u16cout输出UTF-16字符串;使用extios::u32cout输出UTF-32字符串。

字符串变量常用的创建方式(以UTF-32举例,其他同理)

std::u32string text; // 没有字符的字符串, 即空字符串
std::u32string text1 = U"小古银的C++教程"; // 最常用的声明
std::u32string text2(U"小古银的C++教程"); // 与上面一样
std::u32string text3(3, U'啪'); // 3个啪(U"啪啪啪")
std::u32string text4{ U'小', U'古', U'银', U'的', U'C', U'+', U'+', U'教', U'程' }; // 多个字符合并
std::u32string text5 = text1; // 复制9个字符(U"小古银的C++教程")

字符串字面量

#include <string>
#include <iostream>

int main()
{
    using namespace std::string_literals;

    std::string s1 = "abc\0\0def";
    std::string s2 = "abc\0\0def"s; // 使用s后缀
    std::cout << "s1: " << s1.size() << " \"" << s1 << "\"\n";
    std::cout << "s2: " << s2.size() << " \"" << s2 << "\"\n";
}

跟基本数据类型的后缀不一样,使用字符串数据类型的后缀前,需要先使用using namespace std::string_literals;

默认情况,字符串是以'\0'作为结尾,第一个'\0'后面的字符都会被忽略;而使用字符串后缀,字符'\0'会在字符串中被当作可以使用的字符。

获取字符串字符数

在之前的教程中已经使用过成员函数size(),它的返回值是字符变量的数量:

#include <string> // std::u32string
#include <iostream> // std::cout std::endl

int main(void)
{
    std::u32string text = U"小古银的C++教程"; // 9个字符
    std::cout << text.size() << std::endl; // 输出9
    return 0;
}

另外,length()size()是一样的用法和一样的作用。

跟之前说的一样,std::string只能获取字节数,std::u16string有可能不能正确获取字符数。

判断字符串是不是空字符串(以UTF-32举例,其他同理)

成员函数empty()用来判断字符串是不是空,它的返回值true或者false

#include <string> // std::u32string
#include <iostream> // std::cout std::endl std::boolalpha

int main(void)
{
    std::u32string text1; // 空字符串
    std::u32string text2 = U"小古银的C++教程"; // 不是空字符串
    std::cout << std::boolalpha << text1.empty() << std::endl; // true
    std::cout << std::boolalpha << text2.empty() << std::endl; // false
    return 0;
}

对字符串的字符进行操作

#include <string> // std::u32string
#include <iostream> // std::cout std::endl
//#include <extios/extiostream.h> // 如果已安装extios可去掉开头注释

int main(void)
{
    std::u32string text = U"小古银的C++教程";
    text[2] = U'淫'; // 银改成淫, 此时text是U"小古淫的C++教程"
    text[5] = U'艹'; // 第一个+改成艹, 此时text是U"小古淫的C艹+教程"
    text[6] = U' '; // 第二个+改成空格, 此时text是U"小古淫的C艹 教程"
    //extios::u32cout << text << std::endl; // 如果已安装extios可去掉开头注释
    return 0;
}

字符串从0开始数字符,也就是说,text[0]就是第1个字符。所以上面代码将第3个字符改成,第6个字符改成,第7个字符改成空格。

std::string如果不是纯ASCII,这样操作会有问题;std::u16string这样操作有可能会有问题。

清空字符串变量中的字符

成员函数clear()用来清空字符串变量,它没有返回值:

#include <string> // std::u32string
#include <iostream> // std::cout std::endl std::boolalpha

int main(void)
{
    std::u32string text = U"小古银的C++教程";
    std::cout << std::boolalpha << text.empty() << std::endl; // false
    text.clear(); // 清空变量, 与 text = U""; 等价
    std::cout << std::boolalpha << text.empty() << std::endl; // true
    return 0;
}

末尾添加字符和字符串

#include <string> // std::u32string
#include <iostream> // std::cout std::endl
//#include <extios/extiostream.h> // 如果已安装extios可去掉开头注释

int main(void)
{
    std::u32string text = U"小古银"; // (小古银)
    text += U'的'; // 追加一个字符 (小古银的)
    text += U"C++"; // 追加一个字符串 (小古银的C++)
    text += { U'教', U'程' }; // 追加一堆字符 (小古银的C++教程)
    //extios::u32cout << text << std::endl; // 如果已安装extios可去掉开头注释
    return 0;
}

插入字符和字符串

成员函数insert()用来在字符串的开头、中间或者结尾处插入字符或者字符串:

#include <string> // std::u32string
#include <iostream> // std::cout std::endl
//#include <extios/extiostream.h> // 如果已安装extios可去掉开头注释

int main(void)
{
    std::u32string text = U"银+教程"; // 银+教程
    text.insert(0, U"小古"); // 在第1个字符前插入"小古"(小古银+教程)
    text.insert(3, 1, U'的'); // 在第4个字符前插入1个'的'字符(小古银的+教程)
    text.insert(5, { U'C', U'+', U'+' }); // 在第6个字符前按顺序插入'C', '+'2个字符(小古银的C++教程)
    //extios::u32cout << text << std::endl; // 如果已安装extios可去掉开头注释
    return 0;
}

删除字符和字符串

成员函数erase()用来删除字符串中的字符或者字符串:

#include <string> // std::u32string
#include <iostream> // std::cout std::endl
//#include <extios/extiostream.h> // 如果已安装extios可去掉开头注释

int main(void)
{
    std::u32string text = U"小古银的C++教程"; // 小古银的C++教程
    text.erase(1, 6); // 删除从第2个字符开始的6个字符(小教程)
    //extios::u32cout << text << std::endl; // 如果已安装extios可去掉开头注释
    return 0;
}

替换字符和字符串

成员函数replace()用来替换字符串中的字符或者字符串:

#include <string> // std::u32string
#include <iostream> // std::cout std::endl
//#include <extios/extiostream.h> // 如果已安装extios可去掉开头注释

int main(void)
{
    std::u32string text = U"小古银的C++教程"; // 小古银的C++教程
    text.replace(4, 3, U"女秘书调"); // 从第5个字符开始的3个字符替换成"女秘书调"(小古银的女秘书调教程)
    text.replace(4, 1, 2, U'男'); // 从第5个字符开始的1个字符替换成2个字符U'男'(小古银的男男秘书调教程)
    //extios::u32cout << text << std::endl; // 如果已安装extios可去掉开头注释
    return 0;
}

字符串比较

使用等于号==和不等于号!=来判断字符串是否相等:

#include <string> // std::u32string
#include <iostream> // std::cout std::endl std::boolalpha

int main(void)
{
    std::u32string text1 = U"小古银的C++教程";
    std::u32string text2 = U"小古银的C++教程";
    std::u32string text3 = U"小古银的C艹教程";

    std::cout << std::boolalpha << (text1 == text2) << std::endl; // true
    std::cout << std::boolalpha << (text1 == text3) << std::endl; // false

     return 0;
}

获取子字符串

成员函数substr()用来获取字符串中某个部分的字符串,它的返回值是子字符串:

#include <string> // std::u32string
#include <iostream> // std::cout std::endl
//#include <extios/extiostream.h> // 如果已安装extios可去掉开头注释

int main(void)
{
    std::u32string text = U"小古银的C++教程";
    auto cpp = text.substr(4, 3); // 第5个字符开始的3个字符(auto cpp = U"C++";)
    //extios::u32cout << text << std::endl; // 如果已安装extios可去掉开头注释
    //extios::u32cout << cpp << std::endl; // 如果已安装extios可去掉开头注释
    return 0;
}

这里使用成员函数substr()会将子字符串复制出来,变成新的字符串。由于是将字符串复制出来,所以,复制多少个字符,就会占用多少个字符的内存。例如上面代码中的变量cpp,它保存的字符串是C++,三个char32_t,也就是说,新的字符串变量cpp将另外占用3 * 4 = 12个字节内存。

查找

成员函数find()rfind()find_first_of()find_last_of()find_first_not_of()find_last_not_of()用来查找字符或者字符串,它的返回值是查找字符/字符串在字符串中的位置;如果没有找到,则返回std::string::npos

#include <string> // std::u32string
#include <iostream> // std::cout std::endl std::boolalpha

int main(void)
{
    std::u32string text = U"小古银的C++教程";
    std::cout << text.find(U'+') << std::endl; // 从前面第1个字符开始, 找出最先出现'+'的位置
    std::cout << std::boolalpha << (text.find(U'淫') == std::u32string::npos) << std::endl; // 如果找不到则返回npos
    std::cout << text.find(U'+', 6) << std::endl; // 从前面第6个字符开始, 找出最先出现'+'的位置
    std::cout << text.rfind(U'+') << std::endl; // 从后面第1个字符开始, 找出最先出现'+'的位置
    std::cout << text.find_first_of(U"银+教") << std::endl; // 从前面第1个字符开始, 找出最先出现'银'或'+'或'教'的位置
    std::cout << text.find_last_of(U"银+教") << std::endl; // 从后面第1个字符开始, 找出最先出现'银'或'+'或'教'的位置
    std::cout << text.find_first_not_of(U"银+教") << std::endl; // 从前面第1个字符开始, 找出最先出现的不是'银'或'+'或'教'的字符位置
    std::cout << text.find_last_not_of(U"银+教") << std::endl; // 从后面第1个字符开始, 找出最先出现的不是'银'或'+'或'教'的字符位置

     return 0;
}

输出结果:

5
true
6
6
2
7
0
8

数值转换

使用函数stoi()stol()stoll()stoul()stoull()stof()stod()stold()可以将字符串形式的数字转换成数值形式的数字,它们都在string标准库中。注意:这些函数仅std::stringstd::wstring有效。

使用函数to_string()to_wstring()可以将数值形式的数字转换成字符串形式的数字,它们都在string标准库中。

#include <string>
#include <iostream>

int main(void)
{
    std::string num = "123.45";
    std::cout << num << std::endl;
    std::cout << num + "1" << std::endl;
    std::cout << std::stoi(num) + 1 << std::endl; // 字符串转int(舍弃小数部分)
    std::cout << std::stol(num) + 2 << std::endl; // 字符串转long(舍弃小数部分)
    std::cout << std::stoll(num) + 3 << std::endl; // 字符串转long long(舍弃小数部分)
    std::cout << std::stoul(num) + 4 << std::endl; // 字符串转unsigned long(舍弃小数部分)
    std::cout << std::stoull(num) + 5 << std::endl; // 字符串转unsigned long long(舍弃小数部分)
    std::cout << std::stof(num) + 6 << std::endl; // 字符串转float
    std::cout << std::stod(num) + 7 << std::endl; // 字符串转double
    std::cout << std::stold(num) + 8 << std::endl; // 字符串转long double
    std::cout << std::to_string(std::stoi(num) + 1) << std::endl; // 字符串转int, 然后数值转std::string
    std::cout << std::to_wstring(12).size() << std::endl; // 数值转std::wstring, 然后输出转换后字符串的字符数
    return 0;
}

输出结果:

123.45
123.451
124
125
126
127
128
129.45
130.45
131.45
124
2

string_view

string_view系列字符串数据类型的详细用法请参考cppreference string_view 系列字符串说明文档

string系列变量用来修改和查看字符串,而string_view系列变量只是字符串的查看工具,它只能用来查看字符串。string_view系列的使用方法和string系列是一样的,只是修改字符串的操作都不能使用

#include <string> // std::string
#include <string_view> // std::string_view
#include <iostream> // std::cout std::endl

int main(void)
{
    std::string_view textsv1("123456"); // textsv1保存字符串"123456"的内存地址

    // 使用后缀
    using namespace std::string_view_literals;
    auto textsv2 = "654321"sv; // "654321"sv是std::string_view类型

    std::cout << textsv1.substr(2, 2) << std::endl;
    std::cout << textsv2.substr(2, 2) << std::endl;

     return 0;
}

以上代码是string_view的两种创建方式:第一种是用std::string_view变量保存字符串的内存地址然后进行操作。第二种是创建std::string_view字符串。

string系列变量在使用成员函数substr()时,它会复制出一个新的字符串;而string_view系列的substr()不复制出新的字符串,所以不会因为子字符串而占用更多内存。这就是string_view系列出现的主要原因,也是它的主要作用

注意string_view系列的变量只是实际字符串的查看工具。当实际的字符串的内存被销毁(离开作用域)时,此时如果用string_view变量继续对实际的字符串进行处理,程序将会出现严重问题。

#include <string> // std::string
#include <string_view> // std::string_view

int main(void)
{
    std::string_view textsv;

    // 大括号限制变量text的范围
    {
        std::string text = "123456";
        textsv = text;
    }
    // 这里如果对textsv进行操作将会出现严重问题, 因为字符串已经不存在

     return 0;
}

巩固练习

  1. 输入一个字符串,将字符串里的字符前后颠倒,如输入Hello,输出olleH
  2. 输入一个字符串,判断字符串是不是回文字符串(回文字符串:正读和反读都一样的字符串,如level或者noon),如果是回文字符串则输出这是回文字符串,如果不是则输出这应该大概不会是回文字符串
  3. 输入两个字符串,统计第二个字符串在第一个字符串中出现的次数,如输出aaabbbccaabab,输出2

Comments

Make a comment

  • Index

WARNING: You are using an old browser that does not support HTML5. Please choose a modern browser (Chrome / Microsoft Edge / Firefox / Sarafi) to get a good experience.