文章目录
- 标准库中的string类
- string的构造
- string的赋值重载
- string的容量
- size(length)
- max_size
- resize
- reserve
- capacity
- clear
- empty
- shink_to_fit
- string的元素访问
- operator[] 和 at
- front 和 back
- string的迭代器 和 范围for
- string的修改
- operator+=
- append
- push_back
- assign
- insert
- erase
- replace
- swap
- pop_back
- string的输入输出
- operator<<
- operator>>
- getline
- string的查找
- rfind
- find_first_of
- find_last_of
- find_first_not_of
- find_last_not_of
- string的其他操作
- substr
- c_str
标准库中的string类
-
字符串是表示字符序列的类
-
标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
-
string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。
-
string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits 和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
-
注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。
总结:
- string是表示字符串的字符串类
- 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
本章节主要介绍string类的常用接口
string的构造
(1)空字符串构造函数(默认构造函数):构造一个空字符串,长度为零个字符。
(2)拷贝构造函数:构造str
的副本。
(3)子串构造函数:复制从字符位置pos
开始并跨越len
个字符的str
的部分(或者如果str
太短或者len为string::npos
,则复制到str
的末尾)。
(4)用C字符串构造:复制由s
指向的以空字符结尾的字符序列(C字符串)。
(5)用缓冲区数据构造:从由s
指向的字符数组中复制前n
个字符。
(6)填充构造函数:使用n
个连续的字符c
填充字符串。
(7)范围构造函数:以相同顺序复制范围[first,last)
中的字符序列。
下面这两个用到了C++11的内容,以后会介绍。
(8)初始化器列表:以相同顺序复制il
中的每个字符。
(9)移动构造函数:获取str
的内容。 str
将处于未指定但有效的状态。
Example:
// string constructor
#include <iostream>
#include <string>
int main ()
{
std::string s0 ("Initial string");
// constructors used in the same order as described above:
std::string s1;
std::string s2 (s0);
std::string s3 (s0, 8, 3);
std::string s4 ("A character sequence");
std::string s5 ("Another character sequence", 12);
std::string s6a (10, 'x');
std::string s6b (10, 42); // 42 is the ASCII code for '*'
std::string s7 (s0.begin(), s0.begin()+7);
std::cout << "s1: " << s1 << "\ns2: " << s2 << "\ns3: " << s3;
std::cout << "\ns4: " << s4 << "\ns5: " << s5 << "\ns6a: " << s6a;
std::cout << "\ns6b: " << s6b << "\ns7: " << s7 << '\n';
return 0;
}
Output:
s1:
s2: Initial string
s3: str
s4: A character sequence
s5: Another char
s6a: xxxxxxxxxx
s6b: **********
s7: Initial
string的赋值重载
字符串赋值:为字符串分配一个新值,替换其当前内容。
其中(4)和(5)是C++11新增的。
Example:
// string assigning
#include <iostream>
#include <string>
int main ()
{
std::string str1, str2, str3;
str1 = "Test string: "; // c-string
str2 = 'x'; // single character
str3 = str1 + str2; // string
std::cout << str3 << '\n';
return 0;
}
Output:
Test string: x
string的容量
size(length)
字符串的返回长度:返回字符串的长度(以字节为单位)。
这两个函数的功能都是返回字符串的长度,参数类型和返回值类型也都一样,那么库里面为什么要提供两个一模一样的函数呢?
因为string
类的出现早于STL
,对于string
类可以用strlen
来表示有效数据的个数,但是对于STL
中的其他容器,如list
、vector
、deque
、map
、set
等,就不宜用strlen
表示有效数据的个数,而用size
,string
为了和STL
中的其他容器保持接口的一致性,就增加了size
接口。其实作用是一样的,今后使用时两者都可。
max_size
返回字符串的最大大小:返回字符串可以达到的最大长度。
resize
调整字符串大小:将字符串大小调整为 n 个字符的长度。
如果 n 小于当前字符串长度,则当前值将缩短为其第一个 n 个字符,从而删除第 n个字符之外的字符。
如果 n 大于当前字符串长度,则通过在末尾插入所需数量的字符来扩展当前内容,以达到 n 的大小。如果指定了 c
,则新元素将初始化为 c
的副本,否则,它们是值初始化的字符(空字符)。
Example:
// resizing string
#include <iostream>
#include <string>
int main ()
{
std::string str ("I like to code in C");
std::cout << str << '\n';
unsigned sz = str.size();
str.resize (sz+2,'+');
std::cout << str << '\n';
str.resize (14);
std::cout << str << '\n';
return 0;
}
Output:
I like to code in C
I like to code in C++
I like to code
reserve
请求更改容量:请求将字符串容量调整为计划的大小更改,长度最多为 n 个字符。
如果 n 大于当前字符串容量,则该函数会导致容器将其容量增加到 n 个字符(或更大)。
在所有其他情况下,它被视为收缩字符串容量的非约束性请求:容器实现可以自由优化,并使字符串的容量大于 n。
此函数对字符串长度没有影响,并且无法更改其内容。
capacity
已分配存储的返回大小:返回当前为字符串分配的存储空间的大小,以字节表示。
此容量不一定等于字符串长度。它可以等于或更大,额外的空间允许对象在向字符串添加新字符时优化其操作。
请注意,此容量不假定字符串的长度有限制。当此容量耗尽并需要更多容量时,对象会自动扩展它(重新分配其存储空间)。字符串长度的理论限制由成员max_size给出。
字符串的容量可以在修改对象时随时更改,即使此修改意味着大小减小或容量尚未耗尽(这与向量容器中对容量的保证形成鲜明对比)。
可以通过调用成员 Reserve 来显式更改字符串的容量。
clear
清除字符串:清除字符串的内容,该字符串将变为空字符串(长度为 0
个字符)。但是不会改变底层空间的大小,即capacity
不变。
Example:
// string::clear
#include <iostream>
#include <string>
int main ()
{
char c;
std::string str;
std::cout << "Please type some lines of text. Enter a dot (.) to finish:\n";
do {
c = std::cin.get();
str += c;
if (c=='\n')
{
std::cout << str;
str.clear();
}
} while (c!='.');
return 0;
}
该程序重复用户引入的每一行,直到某一行包含一个点 ('.'
)。每个换行符 ('\n'
) 都会触发行的重复和当前字符串内容的清除。
empty
测试字符串是否为空:返回字符串是否为空(即其长度是否为 0
)。
shink_to_fit
收缩以适合:请求字符串减小其容量以适合其大小。
这是C++11中新增的接口。
该请求不具有约束力,容器实现可以自由优化,并使字符串的容量大于其大小。
此函数对字符串长度没有影响,并且无法更改其内容。
Example:
// string::shrink_to_fit
#include <iostream>
#include <string>
int main ()
{
std::string str (100,'x');
std::cout << "1. capacity of str: " << str.capacity() << '\n';
str.resize(10);
std::cout << "2. capacity of str: " << str.capacity() << '\n';
str.shrink_to_fit();
std::cout << "3. capacity of str: " << str.capacity() << '\n';
return 0;
}
Possible output:(因为这个函数并没有要求编译器的行为,所以在不同的编译器下运行的结果可能会有不同)
1. capacity of str: 100
2. capacity of str: 100
3. capacity of str: 10
而在VS2022下,输出的结果为:
string的元素访问
operator[] 和 at
获取字符串的字符:返回对字符串中位置 pos 处的字符的引用。
获取字符串中的字符:返回对字符串中位置 pos 处的字符的引用。
该函数会自动检查 pos 是否是字符串中字符的有效位置(即 pos 是否小于字符串长度),如果不是,则抛出out_of_range异常。
这两个函数的参数和返回值完全相同,用法也完全相同,区别仅在于对越界的处理,operator[]
调用时如果出现越界,则会通过assert
终止程序,而at
则会抛异常out_of_range
Example:
int main()
{
string s("12345");
try {
s.at(10) = '6';
}
catch (...)
{
cout << "越界访问" << endl;
}
return 0;
}
Output:
越界访问
front 和 back
访问第一个字符:返回对字符串的第一个字符的引用。
与成员 string::begin 不同,它返回对同一字符的迭代器,此函数返回直接引用。
不得在空字符串上调用此函数。
访问最后一个字符:返回对字符串最后一个字符的引用。
不得在空字符串上调用此函数。
string的迭代器 和 范围for
因为string
有迭代器,所以string
也可以使用范围for
遍历。
Example:
#include <iostream>
#include <string>
int main ()
{
std::string str ("Test string");
for ( std::string::iterator it=str.begin(); it!=str.end(); ++it)
std::cout << *it;
std::cout << '\n';
for(auto& e : str)
{
std::cout << e;
}
std::cout << '\n';
return 0;
}
Output:
Test string
Test string
string的修改
operator+=
追加到字符串:通过在当前值的末尾附加其他字符来扩展字符串:
Example:
#include <iostream>
#include <string>
int main ()
{
std::string name ("John");
std::string family ("Smith");
name += " K. "; // c-string
name += family; // string
name += '\n'; // character
std::cout << name;
return 0;
}
Output:
John K. Smith
append
追加到字符串:通过在当前值的末尾附加其他字符来扩展字符串。
Example:
#include <iostream>
#include <string>
int main ()
{
std::string str;
std::string str2="Writing ";
std::string str3="print 10 and then 5 more";
// used in the same order as described above:
str.append(str2); // "Writing "
str.append(str3,6,3); // "10 "
str.append("dots are cool",5); // "dots "
str.append("here: "); // "here: "
str.append(10u,'.'); // ".........."
str.append(str3.begin()+8,str3.end()); // " and then 5 more"
str.append<int>(5,0x2E); // "....."
std::cout << str << '\n';
return 0;
}
Output:
Writing 10 dots here: .......... and then 5 more.....
push_back
将字符附加到字符串:将字符 c 追加到字符串的末尾,将其长度增加 1。
assign
将内容分配给字符串:为字符串分配一个新值,替换其当前内容。
作用和operator=
相似
Example:
#include <iostream>
#include <string>
int main ()
{
std::string str;
std::string base="The quick brown fox jumps over a lazy dog.";
// used in the same order as described above:
str.assign(base);
std::cout << str << '\n';
str.assign(base,10,9);
std::cout << str << '\n'; // "brown fox"
str.assign("pangrams are cool",7);
std::cout << str << '\n'; // "pangram"
str.assign("c-string");
std::cout << str << '\n'; // "c-string"
str.assign(10,'*');
std::cout << str << '\n'; // "**********"
str.assign<int>(10,0x2D);
std::cout << str << '\n'; // "----------"
str.assign(base.begin()+16,base.end()-12);
std::cout << str << '\n'; // "fox jumps over"
return 0;
}
Output:
The quick brown fox jumps over a lazy dog.
brown fox
pangram
c-string
**********
----------
fox jumps over
insert
插入到字符串中:在字符串中插入其他字符,紧挨着 pos(或 p)指示的字符:
(1) 字符串:插入 str 的副本。
(2) 子字符串:插入 str 的子字符串的副本。子字符串是 str 中从字符位置 subpos 开始并跨越 sublen 字符的部分(或者直到 str 的末尾,如果任一 str 太短或 sublen 是 npos)。
(3) C-串:插入由 s 指向的以 null 结尾的字符序列(C 字符串)形成的字符串的副本。
(4) 缓冲器:插入 s 指向的字符数组中的前 n 个字符的副本。
(5)填写:插入字符 c 的 n 个连续副本。
(6) 单字:插入字符 c。
(7) 范围:以相同的顺序在 [first,last)
范围内插入字符序列的副本。
(8) 初始值设定项列表:以相同的顺序插入 il 中每个字符的副本。
Example:
#include <iostream>
#include <string>
int main ()
{
std::string str="to be question";
std::string str2="the ";
std::string str3="or not to be";
std::string::iterator it;
// used in the same order as described above:
str.insert(6,str2); // to be (the )question
str.insert(6,str3,3,4); // to be (not )the question
str.insert(10,"that is cool",8); // to be not (that is )the question
str.insert(10,"to be "); // to be not (to be )that is the question
str.insert(15,1,':'); // to be not to be(:) that is the question
it = str.insert(str.begin()+5,','); // to be(,) not to be: that is the question
str.insert (str.end(),3,'.'); // to be, not to be: that is the question(...)
str.insert (it+2,str3.begin(),str3.begin()+3); // (or )
std::cout << str << '\n';
return 0;
}
Output:
to be, or not to be: that is the question...
erase
清除字符串中的字符:清除部分字符串,减少其长度:
(1)顺序:擦除字符串值中从字符位置 pos 开始并跨越 len 字符的部分(如果内容太短或 len 为 string::npos,则擦除字符串值的末尾。 请注意,默认参数会擦除字符串中的所有字符(和成员函数 clear一样)。
(2)字符:擦除 p 指向的字符。
(3) 范围:擦除 [first,last)
范围内的字符序列。
Example:
#include <iostream>
#include <string>
int main ()
{
std::string str ("This is an example sentence.");
std::cout << str << '\n';
// "This is an example sentence."
str.erase (10,8); // ^^^^^^^^
std::cout << str << '\n';
// "This is an sentence."
str.erase (str.begin()+9); // ^
std::cout << str << '\n';
// "This is a sentence."
str.erase (str.begin()+5, str.end()-9); // ^^^^^
std::cout << str << '\n';
// "This sentence."
return 0;
}
Output:
This is an example sentence.
This is an sentence.
This is a sentence.
This sentence.
replace
替换字符串的一部分
用新内容替换字符串中从字符 pos 开始并跨越 len 字符的部分(或字符串在 [i1,i2)
之间的部分):
(1)字符串:副本 str.
(2)子字符串:复制从字符位置 subpos 开始并跨越 sublen 字符的 str 部分(如果任一 str 太短或 sublen 为 string::npos,则复制到 str 的末尾)。
(3)C-串:复制以 s 为结尾的以 null 结尾的字符序列(C 字符串)。
(4)缓冲器:从 s 指向的字符数组中复制前 n 个字符。
(5)填写:将字符串部分替换为字符 c 的 n 个连续副本。
(6)范围:以相同的顺序复制 [first,last)
范围内的字符序列。
(7)初始值设定项列表:以相同的顺序复制 il 中的每个字符。
swap
交换字符串值:通过 str 的内容交换容器的内容,str 是另一个字符串对象。长度可能不同。
请注意,存在一个同名的非成员函数 swap,它使用行为类似于此成员函数的优化重载该算法。
pop_back
删除最后一个字符:擦除字符串的最后一个字符,有效地将其长度减少 1。
string的输入输出
operator<<
将字符串插入到流中:将符合 str 值的字符序列插入到 os 中。
需要注意的是:string
的输出并不以遇到\0
而结束,而是以size()
为准,这是与打印字符串的不同之处。
Example:
#include <iostream> // std::cout
#include <string> // std::string
int main()
{
std::string str = "hello";
str += '\0';
str += " world";
std::cout << str << '\n';
return 0;
}
Output:
hello world
operator>>
从流中提取字符串:从输入流中提取字符串 is,将序列存储在 str 中,该序列将被覆盖(str 的先前值被替换)。
但是流提取操作符是遇到\0
或空格
就停止的,那么如果想要在string
中写入空格,可以使用getline
。
getline
将行从流获取到字符串:从 is 中提取字符并将它们存储到 str 中,直到找到分隔符 delim(或换行符 '\n'
,用于(2))
string的查找
在字符串中查找内容:在字符串中搜索其参数指定的序列的第一次出现。
指定 pos 时,搜索仅包括位置 pos 处或位置 pos 之后的字符,忽略任何可能出现的包含位置 pos 之前的字符。
请注意,与成员find_first_of不同,每当搜索多个字符时,仅其中一个字符匹配是不够的,整个序列必须匹配。
Example:
#include <iostream> // std::cout
#include <string> // std::string
int main ()
{
std::string str ("There are two needles in this haystack with needles.");
std::string str2 ("needle");
// different member versions of find in the same order as above:
std::size_t found = str.find(str2);
if (found!=std::string::npos)
std::cout << "first 'needle' found at: " << found << '\n';
found=str.find("needles are small",found+1,6);
if (found!=std::string::npos)
std::cout << "second 'needle' found at: " << found << '\n';
found=str.find("haystack");
if (found!=std::string::npos)
std::cout << "'haystack' also found at: " << found << '\n';
found=str.find('.');
if (found!=std::string::npos)
std::cout << "Period found at: " << found << '\n';
// let's replace the first needle:
str.replace(str.find(str2),str2.length(),"preposition");
std::cout << str << '\n';
return 0;
}
Output:
first 'needle' found at: 14
second 'needle' found at: 44
'haystack' also found at: 30
Period found at: 51
There are two prepositions in this haystack with needles.
rfind
查找字符串中内容的最后出现项:在字符串中搜索其参数指定的序列的最后一次出现项。
find_first_of
在字符串中查找字符:在字符串中搜索与其参数中指定的任何字符匹配的第一个字符。
该函数与find
的区别是:find
找到的序列必须和提供的序列完全相同,find_first_of
则是找到序列中的任意一个即可。
Example:
#include <iostream> // std::cout
#include <string> // std::string
#include <cstddef> // std::size_t
int main ()
{
std::string str ("Please, replace the vowels in this sentence by asterisks.");
std::size_t found = str.find_first_of("aeiou");
while (found!=std::string::npos)
{
str[found]='*';
found=str.find_first_of("aeiou",found+1);
}
std::cout << str << '\n';
return 0;
}
Output:
Pl**s*, r*pl*c* th* v*w*ls *n th*s s*nt*nc* by *st*r*sks.
find_last_of
从末尾查找字符串中的字符:在字符串中搜索与其参数中指定的任何字符匹配的最后一个字符。
find_first_not_of
查找字符串中缺少字符:在字符串中搜索与其参数中指定的任何字符不匹配的第一个字符。
find_last_not_of
从末尾查找字符串中不匹配的字符:在字符串中搜索与其参数中指定的任何字符不匹配的最后一个字符。
string的其他操作
substr
生成子字符串:返回一个新构造的对象,其值初始化为此对象的子字符串的副本。
Example:
#include <iostream>
#include <string>
int main ()
{
std::string str="We think in generalities, but we live in details.";
// (quoting Alfred N. Whitehead)
std::string str2 = str.substr (3,5); // "think"
std::size_t pos = str.find("live"); // position of "live" in str
std::string str3 = str.substr (pos); // get from "live" to the end
std::cout << str2 << ' ' << str3 << '\n';
return 0;
}
Output:
think live in details.
c_str
获取 C 字符串等效项:返回指向数组的指针,该数组包含以 null 结尾的字符序列(即 C 字符串),表示字符串对象的当前值。
Example:
#include <iostream>
#include <cstring>
#include <string>
int main ()
{
std::string str ("Please split this sentence into tokens");
char * cstr = new char [str.length()+1];
std::strcpy (cstr, str.c_str());
// cstr now contains a c-string copy of str
char * p = std::strtok (cstr," ");
while (p!=0)
{
std::cout << p << '\n';
p = std::strtok(NULL," ");
}
delete[] cstr;
return 0;
}
Output:
Please
split
this
sentence
into
tokens