C++参悟:标准字符串string
标准字符串string
- 一、概述
- 二、函数
- 1. 构造函数
- 2. 数值转换
- 1. 转换字符串为有符号整数
- 2. 转换字符串为无符号整数
- 3. 转换字符串为浮点值
- 4. 数值转字符串
- 5. 异常处理
- 3. 范围访问
- 4. 迭代器
- 5. 容量
- 6. 操作
- 7. 查找
- 8. 字面量
一、概述
C++为我们提供了非常多的字符串,标准字符串 string 就是用的 char 作为 basic_string的 模板参数形成的一个字符串。
模板类 std::basic_string 通用化了,字符序列如何操作、存储、创建及析构全部的类方法与相关函数。
标准提供 std::basic_string 对常用类型的数种特化:
在 <string> 头文件中 有如下的定义 ,string 其实就是用的basic_string的模板。下面的基本上所有讨论都是基于basic_string.这里讨论的 C11标准
typedef std::basic_string<char> std::string;
typedef std::basic_string<wchar_t> std::wstring
typedef std::basic_string<char16_t> std::u16string
typedef std::basic_string<char32_t> std::u32string
想如果要在字符串使用中文的话或者其他语言字符串,就得使用 wstring才行,因为ASCII只有英文的字符。
二、函数
1. 构造函数
下面是几种最常用的构造函数例子
#include <iostream>
#include <cassert>
#include <iterator>
#include <string>
#include <cctype>int main()
{// 1. string::string()std::string s;assert(s.empty() && (s.length() == 0) && (s.size() == 0));// 2. string::string(size_type count, charT ch)std::string s(4, '=');std::cout << s << '\n'; // "===="std::string const other("Exemplary");// 3. string::string(string const& other, size_type pos, size_type count)std::string s(other, 0, other.length()-1);std::cout << s << '\n'; // "Exemplar"// 4. string::string(charT const* s, size_type count)std::string s("C-style string", 7);std::cout << s << '\n'; // "C-style"// 5. string::string(charT const* s)std::string s("C-style\0string");std::cout << s << '\n'; // "C-style"char mutable_c_str[] = "another C-style string";// 6. string::string(InputIt first, InputIt last)std::string s(std::begin(mutable_c_str)+8, std::end(mutable_c_str)-1);std::cout << s << '\n'; // "C-style string"std::string const other("Exemplar");std::string s(other);std::cout << s << '\n'; // "Exemplar"// 7. string::string(string&& str)std::string s(std::string("C++ by ") + std::string("example"));std::cout << s << '\n'; // "C++ by example"// 8. string(std::initializer_list<charT> ilist)std::string s({ 'C', '-', 's', 't', 'y', 'l', 'e' });std::cout << s << '\n'; // "C-style"// 9. 重载决议选择 string(InputIt first, InputIt last) [with InputIt = int]// 这表现为如同调用 string(size_type count, charT ch)std::string s(3, std::toupper('a'));std::cout << s << '\n'; // "AAA"
}// 输出:
====
Exemplar
C-style
C-style
C-style string
Exemplar
C++ by example
C-style
AAA
2. 数值转换
数值转换主要就是字符串转换为整数、浮点数之类的。以及从浮点数转为字符串。
1. 转换字符串为有符号整数
会使用如下3个函数:
int stoi( const std::string& str, std::size_t* pos = 0, int base = 10 );
int stoi( const std::wstring& str, std::size_t* pos = 0, int base = 10 );long stol( const std::string& str, std::size_t* pos = 0, int base = 10 );
long stol( const std::wstring& str, std::size_t* pos = 0, int base = 10 );long long stoll( const std::string& str, std::size_t* pos = 0, int base = 10 );
long long stoll( const std::wstring& str, std::size_t* pos = 0, int base = 10 );
参数的解释如下:
-
str - 要转换的字符串 ;舍弃所有空白符(以调用 isspace() 鉴别),直到找到首个非空白符,然后取尽可能多的字符组成底 n (其中 n=base )的整数表示,并将它们转换成一个整数值。不支持科学计数法哦,合法的整数值由下列部分组成:
- (可选)正或负号
- (可选)指示八进制底的前缀( 0 )(仅当底为 8 或 0 时应用)
- (可选)指示十六进制底的前缀( 0x 或 0X )(仅当底为 16 或 0 时应用)
- 一个数字序列
-
pos - 存储已处理字符数的整数的地址,也就是说这个数字由几位字符构成
-
base - 数的底 ;
- 底的合法集是 {0,2,3,…,36} 。合法数字集对于底 2 整数是 {0,1},对于底3整数是 {0,1,2} ,以此类推。对于大于 10 的底,合法数字包含字母字符,从对于底 11 整数的 Aa 到对于底36整数的 Zz 。忽略字符大小写。
- 若 base 为 0 ,则自动检测数值进制:若前缀为 0 ,则底为八进制,若前缀为 0x 或 0X ,则底为十六进制,否则底为十进制。
示例:
std::string str1 = "45E+3";std::string str2 = "3.141 59";std::string str3 = "31337 with words";std::string str4 = "words and 2";size_t num1 = 0;size_t num2 = 0;size_t num3 = 0;int myint1 = std::stoi(str1, &num1);int myint2 = std::stoi(str2, &num2);int myint3 = std::stoi(str3, &num3);// 错误: 'std::invalid_argument'// int myint4 = std::stoi(str4);std::cout << "std::stoi(\"" << str1 << "\") is " << myint1 << " char num: " << num1<< '\n';std::cout << "std::stoi(\"" << str2 << "\") is " << myint2 << " char num: " << num2<< '\n';std::cout << "std::stoi(\"" << str3 << "\") is " << myint3 << " char num: " << num3<<'\n';//std::cout << "std::stoi(\"" << str4 << "\") is " << myint4 << '\n';// 输出
std::stoi("45E+3") is 45 char num: 2
std::stoi("3.141 59") is 3 char num: 1
std::stoi("31337 with words") is 31337 char num: 5
2. 转换字符串为无符号整数
用法和 1. 完全一致
unsigned long stoul( const std::string& str, std::size_t* pos = 0, int base = 10 );
unsigned long stoul( const std::wstring& str, std::size_t* pos = 0, int base = 10 );unsigned long long stoull( const std::string& str, std::size_t* pos = 0, int base = 10 );
unsigned long long stoull( const std::wstring& str, std::size_t* pos = 0, int base = 10 );
3. 转换字符串为浮点值
float stof( const std::string& str, std::size_t* pos = 0 );
float stof( const std::wstring& str, std::size_t* pos = 0 );double stod( const std::string& str, std::size_t* pos = 0 );
double stod( const std::wstring& str, std::size_t* pos = 0 );long double stold( const std::string& str, std::size_t* pos = 0 );
long double stold( const std::wstring& str, std::size_t* pos = 0 );
-
str - 要转换的 string;函数会舍弃任何空白符(由 std::isspace() 确定),直至找到首个非空白符。然后它会取用尽可能多的字符,以构成合法的浮点数表示,并将它们转换成浮点值。 合法的浮点值可以为下列之一:
- 十进制浮点数表达式。它由下列部分组成:
- (可选) 正或负号
- 非空的十进制数字序列,可选地包含一个小数点字符(由当前的 C 本地环境确定)(定义有效数字)
- (可选) e 或 E ,并跟随可选的正或负号,以及非空十进制数字序列(以 10 为底定义指数)
- 十六进制浮点数表达式。它由下列部分组成:
- (可选) 正或负号
- 0x 或 0X
- 非空的十六进制数字序列,选地包含一个小数点字符(由当前的 C 本地环境确定)(定义有效数字)
- (可选) p 或 P ,并跟随可选的正或负号,以及非空十进制数字序列(以 2 为底定义指数)
- 无穷大表达式。它由下列部分组成:
- (可选) 正或负号
- INF 或 INFINITY ,忽略大小写
- 非数(NaN)表达式。它由下列部分组成:
- (可选) 正或负号
- NAN 或 NAN(char_sequence) ,忽略 NAN 部分的大小写。 char_sequence 只能由数字、拉丁字母和下划线构成。结果是一个静态的 NaN 浮点值。
- 任何其他可由当前 C 本地环境接受的表达式
- 十进制浮点数表达式。它由下列部分组成:
-
pos - 存储已处理字符数的整数的地址 ,也就是说这个数字由几位字符构成
示例:
std::string str1 = "45E+3";std::string str2 = "3.141 59";std::string str3 = "31337.43 with words";std::string str4 = "words and 2";size_t num1 = 0;size_t num2 = 0;size_t num3 = 0;double myint1 = std::stod(str1, &num1);double myint2 = std::stod(str2, &num2);double myint3 = std::stod(str3, &num3);// 错误: 'std::invalid_argument'// int myint4 = std::stoi(str4);std::cout << "std::stod(\"" << str1 << "\") is " << myint1 << " char num: " << num1<< '\n';std::cout << "std::stod(\"" << str2 << "\") is " << myint2 << " char num: " << num2<< '\n';std::cout << "std::stod(\"" << str3 << "\") is " << myint3 << " char num: " << num3<<'\n';//std::cout << "std::stoi(\"" << str4 << "\") is " << myint4 << '\n';//输出
std::stod("45E+3") is 45000 char num: 5
std::stod("3.141 59") is 3.141 char num: 5
std::stod("31337.43 with words") is 31337.4 char num: 8
4. 数值转字符串
to_string :转换整数或浮点值为 string
to_wstring:转换整数或浮点值为 wstring,这个和string基本类似
std::string to_string( int value );
std::string to_string( long value );
std::string to_string( long long value );
std::string to_string( unsigned value );
std::string to_string( unsigned long value );
std::string to_string( unsigned long long value );
std::string to_string( float value );
std::string to_string( double value );
std::string to_string( long double value );
示例代码
#include <iostream>
#include <string>int main()
{double f = 23.43;double f2 = 1e-9;double f3 = 1e40;double f4 = 1e-40;double f5 = 123456789;std::string f_str = std::to_string(f);std::string f_str2 = std::to_string(f2); // 注意:返回 "0.000000"std::string f_str3 = std::to_string(f3); // 注意:不返回 "1e+40".std::string f_str4 = std::to_string(f4); // 注意:返回 "0.000000"std::string f_str5 = std::to_string(f5);std::cout << "std::cout: " << f << '\n'<< "to_string: " << f_str << "\n\n"<< "std::cout: " << f2 << '\n'<< "to_string: " << f_str2 << "\n\n"<< "std::cout: " << f3 << '\n'<< "to_string: " << f_str3 << "\n\n"<< "std::cout: " << f4 << '\n'<< "to_string: " << f_str4 << "\n\n"<< "std::cout: " << f5 << '\n'<< "to_string: " << f_str5 << '\n';
}// 输出:
std::cout: 23.43
to_string: 23.430000std::cout: 1e-09
to_string: 0.000000std::cout: 1e+40
to_string: 10000000000000000303786028427003666890752.000000std::cout: 1e-40
to_string: 0.000000std::cout: 1.23457e+08
to_string: 123456789.000000
这个可能会抛出一个 std::bad_alloc 异常,我们用try catch捕获即可
5. 异常处理
数值转换的时候,出现问题的话就会有 std::invalid_argument 异常出现,都是可以用try catch 捕获异常
try {} catch (const std::invalid_argument &e) {std::cout<<e.what();
}
3. 范围访问
at(size_type pos ): 访问指定pos位置的字符,有边界检查
#include <stdexcept>
#include <iostream>
#include <string>
int main()
{std::string s("message"); // 为容量s = "abc";s.at(2) = 'x'; // okstd::cout << s << '\n';std::cout << "string size = " << s.size() << '\n';std::cout << "string capacity = " << s.capacity() << '\n';try {// 抛出,即使容量允许访问元素s.at(3) = 'x';}catch (std::out_of_range const& exc) {std::cout << exc.what() << '\n';}
}// 可能的输出:
abx
string size = 3
string capacity = 7
basic_string::at
operator[size_type pos ]:访问指定字符,没得校验,推荐使用at 函数
#include <iostream>
#include <string>
int main()
{std::string const e("Exemplar");for (unsigned i = e.length() - 1; i != 0; i /= 2)std::cout << e[i];std::cout << '\n';const char* c = &e[0];std::cout << c << '\n'; // 作为 C 字符串打印// 更改 s 的最后字符为 'y'std::string s("Exemplar ");s[s.size()-1] = 'y';std::cout << s << '\n';
}//输出:
rmx
Exemplar
Exemplary
front: 访问首字符
back: 访问最后的字符
data:返回指向字符串首字符的指针
c_str:返回字符串的不可修改的 C 字符数组版本,这个区别于data,可以说data可以修改、而c_str不能修改
4. 迭代器
返回指向字符串首字符的迭代器,迭代器就是一种特异化的指针,带’c’就是说是只读的迭代器。
| 含义 | |
|---|---|
| begin cbegin | 返回指向起始的迭代器 |
| end cend | 返回指向末尾的迭代器 |
| rbegin crbegin | 返回指向起始的逆向迭代器 |
| rend crend | 返回指向末尾的逆向迭代器 |


#include <string>
#include <iostream>int main()
{std::string s("Exemplar");*s.begin() = 'e';std::cout << s <<'\n';auto i = s.cbegin();std::cout << *i << '\n';
// *i = 'E'; // 错误: i 是常迭代器
}
逆序字符串示例:
#include <iostream>
#include <algorithm>
#include <iterator>
#include <string>int main()
{std::string s("Exemplar!");*s.rbegin() = 'y';std::cout << s << '\n'; // "Exemplary"std::string c;std::copy(s.crbegin(), s.crend(), std::back_inserter(c));std::cout << c << '\n'; // "yralpmexE"
}// 输出:
Exemplary
yralpmexE
5. 容量
empty: 检查字符串是否为空;若 string 为空则为 true ,否则为 false 。
size length : 返回字符数
max_size : 返回字符数的最大值
reserve : 保留存储;告诉 std::basic_string 对象大小的有计划更改,使得它能准确地管理存储分配。
-
若 new_cap 大于当前 capacity() ,则分配新存储,并令 capacity() 大于或等于 new_cap 。
-
若 new_cap 小于当前 capacity() ,则这是非强制的收缩请求。
-
若 new_cap 小于当前 size() ,则这是非强制的收缩到适合 (shrink-to-fit) 请求,等价于 shrink_to_fit() (C++11 起)。
capacity : 返回当前对象分配的存储空间能保存的字符数量
shrink_to_fit: 通过释放不使用内存减少内存使用 ;也就是移除未使用的容量。
6. 操作
在每一个版本的函数有些差异。C20版本最全。
clear: 清除内容
insert: 插入字符
erase: 移除字符
push_back: 后附字符到结尾
pop_back: 移除末尾字符
append: 后附字符到结尾
operator+= : 后附字符到结尾
compare : 比较二个字符串
replace: 替换字符串的指定部分;往往搭配 find 函数一起使用
substr: 返回子串
copy: 复制字符
resize: 更改存储的字符数
swap: 交换内容
7. 查找
find: 于字符串中寻找字符
rfind: 寻找子串的最后一次出现
find_first_of:寻找字符的首次出现
find_first_not_of: 寻找字符的首次缺失
find_last_of: 寻找字符的最后一次出现
find_last_not_of: 寻找字符的最后一次缺失
#include <string>
#include <iostream>void print(std::string::size_type n, std::string const &s)
{if (n == std::string::npos) {std::cout << "not found\n";} else {std::cout << "found: " << s.substr(n) << '\n';}
}int main()
{std::string::size_type n;std::string const s = "This is a string";// 从 string 开始搜索n = s.find("is");print(n, s);// 从位置 5 开始搜索n = s.find("is", 5);print(n, s);// 寻找单个字符n = s.find('a');print(n, s);// 寻找单个字符n = s.find('q');print(n, s);
}
// 输出:
found: is is a string
found: is a string
found: a string
not found
8. 字面量
字面量就是不可以被修改的常量字符串,在程序中多个副本会指向同一个字面量。
其中的一个基础定义就是
std::string operator""s(const char *str, std::size_t len);
#include <string>
#include <iostream>int main()
{using namespace std::string_literals;std::string s1 = "abc\0\0def";std::string s2 = "abc\0\0def"s;std::cout << "s1: " << s1.size() << " \"" << s1 << "\"\n";std::cout << "s2: " << s2.size() << " \"" << s2 << "\"\n";
}// 可能的输出:
s1: 3 "abc"
s2: 8 "abcdef"