Skip to content

C++ string类

主要内容

string类:构造对象、获取长度、访问元素、比较大小、修改内容、输入输出、查找、迭代器

构造函数:创建string对象

要初始化一个string对象,可以使用 C 风格字符串或string对象,也可以使用 C 风格字符串的部分或string类型对象的部分或序列,或者使用string对象提供的迭代器(const) char* 指针。

常见的string类构造函数

下面的两段代码中,约定使用以下标识符带代表相应变量或对象:

  • str表示string对象;cstr表示C风格的字符串((const) char *);
  • iter表示string对象的迭代器;ptr表示char*指针。
1
2
3
4
5
6
7
8
9
10
11
12
string s();				//生成空字符串
string s(str); //生成字符串str的复制品
string s(cstr); //利用C风格的字符串cstr初始化新的字符串
string s(str, num); //用字符串str中始于num的部分构造新的字符串
string s(cstr, num); //以C风格的字符串cstr的前num个字符构造新的字符串
//用字符串(string对象、(const) char *)中始于num长度为len的部分构造新的字符串
string s(str, num, len);
string s(cstr, num, len);
//使用字符串的迭代器iter(或ptr)来初始化新的字符串
string s(iter1, iter2);
string s(ptr1, ptr2);
string s(num, ch); //生成一个字符串,包含num个ch字符

有关使用迭代器或 (const) char* 指针构造string对象的详细说明:

1
2
3
4
5
6
7
string s(str.begin(), str.end());	//等价于string s(str);
string s(cstr, cstr+字符串长度); //等价于string s(cstr);
//截取str的正向索引m(包括)到负向索引n(不包括)的部分初始化字符串
string s(str.begin()+m, str.end()-n);
//截取字符串下标[m, n)区间的部分初始化字符串
string s(str.begin()+m, str.begin()+n);
string s(cstr+m, cstr+n);

注:第二个参数不用考虑越界问题,但是两个迭代器(指针)位置不能反,否则程序会崩溃。

注意:不能使用字符或者整数去初始化字符串

如果字符串只包含一个字符,使用构造函数对其初始化时,使用以下形式比较合理:

1
2
3
string s(1, 'x');	//正确
string s("x"); //正确
//错误:string s('x');

(const) char* 与 string 对象

目前,在C++中存在从 const char* 到string的隐含类型转换,但不存在从string对象到C风格字符串的自动类型转换。

一般来说,任何出现字符串字面值的地方都可以用以空字符结束的字符数组来替代:

  • 允许使用以空字符结束的字符数组来初始化string对象或为string对象赋值。
  • 在string对象的加法运算中允许使用以空字符结束的字符数组作为其中的一个运算对象(但不能两个运算对象都是字符数组,这样就相当于将两个相加,是不合法的)
  • 在string对象的复合赋值(+=)中允许使用以空字符结束的字符数组作为右侧的运算数。

但应注意的是:如果程序的某处需要一个C风格字符串,无法直接用string对象来代替它。

例如 不能用string对象直接初始化指向字符的指针。

为了完成该功能,string专门提供了一个名为c_str的成员函数,返回结果是一个指针,该指针指向一个以空字符(‘\0’)结束的字符数组,所存的数据与string对象的相同。结果指针的类型是const char*,从而不可以改变返回的字符数组的内容(即返回的是右值)。

1
2
char *s1 = s;				//错误:不能用string对象初始化char*
const char *s2 = s.c_str(); //正确

注意:在整个程序中应坚持使用string对象,直到必须将内容转化为 char* 时才将其转换为C 风格字符串。

string类构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
char c_str[] = "01234567";
string cpp_str("abcdefgh"); //使用const char*来构造string对象
string s0; //空字符串
string s1(cpp_str); //复制字符串
string s2(c_str); //利用C风格的字符串初始化新的字符串
string s3(cpp_str, 2); //用字符串中始于2的部分构造新的字符串
string s4(c_str, 3); //以C风格的字符串的前3个字符构造新的字符串
//s5、s6: 用字符串(string、(const) char *)中始于2长度为3的部分构造新的字符串
string s5(cpp_str, 2, 3);
string s6(c_str, 2, 3);
//s7、s8: 使用字符串的迭代器(或char *)来初始化新的字符串
string s7(cpp_str.begin()+2, cpp_str.end()-2);
string s8(c_str+2, c_str+6);
string s9(5, 'a'); //生成一个字符串,包含5个'a'字符
/* 错误示例:
* string serr1(1);
* string serr2('a');
*/

使用cout输出结果

1
2
3
4
5
6
7
8
9
10
s0:
s1: abcdefgh
s2: 01234567
s3: cdefgh
s4: 012
s5: cde
s6: 234
s7: cdef
s8: 2345
s9: aaaaa

获取字符串长度

string对象包括三种求解字符串长度的函数:size()length()maxsize()capacity()

  • size()length():都会返回string对象中的字符个数,且执行效果相同。
  • maxsize()maxsize() 函数返回string对象最多包含的字符数(一般为int所能容纳的最大值)。一旦程序使用长度超过 maxsize() 的string操作,编译器会拋出length_error异常。
  • capacity():该函数返回在重新分配内存之前,string对象所能包含的最大字符数。

string类还具有一个 reserve() 函数。调用该函数可以为string对象重新分配内存,大小由其参数决定,默认参数为0。

访问字符串元素

字符串中元素是可以访问的,一般有两种方法访问字符串中的单一字符:下标操作符[] 和 成员函数 at()。两者均返回指定的下标位置的字符。

需要注意的是,这两种访问方法是有区别的(在VS2019下):

  • 下标操作符 [] 在使用时不检查索引的有效性,如果下标超出字符串的长度范围,会示导致未定义行为。
    • 对于C风格字符串,使用下标操作符时,字符串的最后字符(即 '\0',下标等于字符串长度)是有效的;下标越界(大于字符串长度)会导致Warning:读取数据无效,索引超出了的有效范围。
    • 对于string对象,str.length() 的下标访问是有效的,调用返回字符 '\0';下标越界(大于 length())会导致传入无效参数而程序崩溃。
  • 成员函数 at() 在使用时会检查下标是否有效。如果给定的下标超出范围[0, length()),系统会抛出out_of_range异常。

下标访问的有效问题

1
2
3
4
5
6
7
8
9
10
11
char cstr[] = "0123456";
string str = "ABCDEFG";

char c1 = cstr[7]; //'\0'
char c2 = cstr[10]; //Warning:读取数据无效,索引超出了的有效范围
cout << c2 << endl; //无效的输出

char h1 = str[str.length()]; //'\0'
char h2 = str.at(str.length()); //Error:out_of_range
char e1 = str[10]; //程序崩溃:无效数据
char e2 = str.at(10); //Error:out_of_range

string对象的比较方法

字符串可以和类型相同的字符串相比较,也可以和具有同样字符类型的字符数组比较。

比较运算符

String 类的常见运算符包括 >、<、==、>=、<=、!=。其意义分别为"大于"、“小于”、“等于”、“大于等于”、“小于等于”、“不等于”。

注意:对于参加比较的两个字符串,任一个字符串均不能为 NULL,否则程序会异常退出。

compare() 函数

string类的成员函数 compare() 返回一个整数来表示比较结果。

如果比较的串与对象相同返回 0;否则按字典顺序比较:对象小于(先于)比较的串返回负值,反之返回正值。该函数支持多参数处理,支持用索引值和长度来定位子串进行比较。

compare() 函数原型:

1
2
3
4
5
6
7
8
9
10
11
12
// compare [0, size()) with _Right
int compare(const basic_string& _Right) const;
// compare [0, size()) with [_Ptr, <null>)
int compare(_In_z_ const _Elem* const _Ptr) const noexcept;
// compare [_Off, _Off + _N0) with _Right
int compare(size_type _Off, size_type _N0, const basic_string& _Right) const;
// compare [_Off, _Off + _N0) with [_Ptr, <null>)
int compare(const size_type _Off, const size_type _N0, _In_z_ const _Elem* const _Ptr) const;
// compare [_Off, _Off + _N0) with _Right [_Roff, _Roff + _Count)
int compare(const size_type _Off, const size_type _N0, const basic_string& _Right, const size_type _Roff, const size_type _Count = npos) const;
// compare [_Off, _Off + _N0) with [_Ptr, _Ptr + _Count)
int compare(const size_type _Off, const size_type _N0, _In_reads_(_Count) const _Elem* const _Ptr, const size_type _Count) const;

compare() 的使用方法:

下面的代码中,约定使用以下标识符带代表相应变量或对象:

  • str表示原始string对象,sstr表示另一个string对象;
  • cstr表示C风格字符串(char*);pos为下标表示的位置
1
2
3
4
5
6
7
8
9
10
11
//str([0, size()))与sstr([0, size()))或cstr([cstr, <null>))比较
str.compare(sstr);
str.compare(cstr);
//str从pos开始的n个字符的子串([pos, pos+n))与sstr([0, size()))、cstr([cstr, <null>))比较
str.compare(pos, n, sstr);
str.compare(pos, n, cstr);
//str从pos开始的n个字符的子串([pos, pos+n))与sstr从pos2开始的n2个字符的子串([pos2, pos2+n2))比较。
//【注】从const char*到string存在隐含类型转换,所以sstr可以换成cstr
str.compare(pos, n, sstr, pos2, n2);
//str从pos开始的n个字符的子串([pos, pos+n))与cstr区间[cstr, cstr+n2)的子串比较
str.compare(pos, n, cstr, n2);

字符串内容的变化

字符串内容的变化包括修改替换两种。

这一小节的代码中,约定使用以下标识符带代表相应变量或对象:

  • str表示原始string对象,sstr表示另一个string对象;cstr表示C风格字符串

  • ch表示char字符;iter表示string对象的迭代器;pos为下标表示的位置

“cstr的前n个字符” 基本都可以替换为 char* 指针,类似 [_Ptr, _Ptr + _Count)

字符串内容的修改

可以使用多种方法修改字符串的值。例如赋值运算符= ,assign()erase(),交换(swap()),插入(insert())等。另外,还可通过 append() 函数添加字符。

assign() 函数

使用 assign() 函数可以直接给字符串赋值,并返回复制后字符串的引用。既可以将整个字符串赋值给新串,也可以将字符串的子串赋值给新串。其使用方法如下:

1
2
3
4
5
6
str.assign(sstr);			//直接使用字符串sstr/cstr赋值
str.assign(cstr, n); //使用cstr前n个字符的子串赋值
str.assign(sstr, pos); //将str的子串[pos, size())赋值给调用串
str.assign(sstr, pos, n); //将str的子串[pos, pos+n)赋值给调用串
str.assign(n, ch) ; //使用n个重复字符ch赋值
str.assign(iter1, iter2); //使用迭代器赋值

erase() 函数

erase() 函数可以删除整个字符串或者子串的内容。可以不提供参数,默认删除整个字符串;可以提供下标表示的位置;也可以提供迭代器。若不提供或提供下标表示的位置,则返回删除后字符串的引用;若提供迭代器,则返回指向删除后的下一个元素的迭代器。

1
2
3
4
//全部清除(m、n缺省时)\清除子串[pos, size())(n缺省时)\清除子串[pos, pos+n)
str.erase([pos[, n=npos]]);
//清除子串[iter1, end())(iter2缺省时)\清除子串[iter1, iter2)
str.ersae(iter1[, iter2]);

swap() 函数

swap()函数可以交换两个字符串内容,无返回值。

1
str.swap(sstr);

insert() 函数

insert() 函数可以将字符或字符串插入到指定位置。位置可由下标指定(返回插入后的字符串的引用),也可以由迭代器指定(返回插入后下一个元素的迭代器)。

1
2
3
4
5
6
7
8
str.insert(pos, cstr); 			//在pos前面插人字符串cstr
str.insert(pos, cstr, n); //将cstr的前n个字符插入pos前面
str.insert(pos, sstr); //在pos前面插人字符串sstr
str.insert(pos, sstr, pos2, n); //在pos前面插人sstr的子串[pos2, pos2+n)
str.insert(pos, n, ch); //在pos前面插人n个字符ch
str.insert(iter, ch); //在iter位置插入字符ch
str.insert(iter, iter1, iter2); //在iter位置插入串[iter1, iter2)
str.insert(iter, n, ch); //在iter位置重复插入n个字符ch

不可在下标指定位置时只插入一个字符,正确的做法如下。

在字符串str下标3的位置插入字符 ‘Z’

1
2
3
4
5
//错误:str.insert(3, 'Z');
//正确:
str.insert(3, 1, 'Z');
str.insert(3, "Z");
str.insert(str.begin()+3, 'Z');

append() 函数

append() 函数用于向字符串追加内容,其用法和insert() 函数相差不大,相当于固定pos=npos,iter=end(),但返回值均为返回追加后的字符串的引用。

1
2
3
4
5
6
str.append(cstr); 			//追加字符串cstr
str.append(cstr, n); //将cstr的前n个字符追加到末尾
str.append(sstr); //追加字符串sstr
str.append(sstr, pos2, n); //追加sstr的子串[pos2, pos2+n)
str.append(n, ch); //追加n个字符ch
str.append(iter1, iter2); //使用迭代器追加串[iter1, iter2)

字符串内容的替换

  • 修改指定位置字符的值:下标访问修改。

  • 替换某个子串:成员函数 replace() 。

replace() 函数

replace() 函数可以用于替换字符串,返回替换后的字符串的引用。

使用下标指定的位置:

1
2
3
4
5
6
7
8
9
10
11
12
//1. 将源串的[pos, pos+n)部分使用cstr替换
str.replace(pos, n, cstr);
//2. 将源串的[pos, pos+n)部分使用cstr的前num个字符替换
str.replace(pos, n, cstr, num);
//3. 将源串的[pos, pos+n)部分使用sstr替换
str.replace(pos, n, sstr);
//4. 将源串的[pos, pos+n)部分使用sstr的[pos1, pos1+n1)子串替换
str.replace(pos, n, sstr, pos1, n1);
//5. 将源串的[pos, pos+n)部分使用sstr的[pos1, sstr.size())子串替换
str.replace(pos, n, sstr, pos1);
//6. 将源串的[pos, pos+n)部分使用num个字符ch替换
str.replace(pos, n, num, ch);

使用迭代器:

和下标的形式差别不大,函数的前两个参数换为 iter1iter2 ,表示源串的 [iter1, iter2) 部分。

1
2
3
4
5
6
7
8
9
10
//1. [iter1, iter2) -> cstr
str.replace(iter1, iter2, cstr);
//2. [iter1, iter2) -> [cstr, cstr+num)
str.replace(iter1, iter2, cstr, num);
//3. [iter1, iter2) -> sstr
str.replace(iter1, iter2, sstr);
//4.5. [iter1, iter2) -> [iter_n1, iter_n2)
str.replace(iter1, iter2, iter_n1, iter_n2);
//6. [iter1, iter2) -> num个字符ch
str.replace(iter1, iter2, num, ch);

字符串输入输出操作

流运算符

“<<” 和 “>>” 提供了C++语言的字符串输入和字符串输出功能。

  • “<<” 可以将字符插入到一个流对象中(例如 ostream);
  • “>>” 可以实现将以空格或回车为 “结束符” 的字符序列读入到对应的字符串中,并且开头和结尾的空白字符不包括进字符串中。

getline() 函数

getline() 函数可将整行的所有字符读到字符串中,并且读取的方式更加多样。该函数的原型包括两种形式:

1
2
3
4
5
6
//包含2个参数:第1个参数是输入流对象_Istr;第2个参数是保存输入内容的字符串_Str
template<class CharType, class Traits, class Allocator>
basic_istream<CharType, Traits>& getline(basic_istream<CharType, Traits>& _Istr, basic_string<CharType,Traits, Allocator>& _Str);
//包含3个参数:第1个参数是输入流对象_ Istr,第 2 个参数保存输入的字符串_Str,第3个参数指定的分界符_Delim。
template<class CharType, class Traits, class Allocator>
basic_istream<CharType, Traits>& getline(basic_istream<CharType, Traits>& _Istr, basic_string<CharType, Traits, Allocator>& _Str, CharType _Delim);

在读取字符时,遇到文件结束符、分界符、回车符时,将终止读入操作,且文件结束符、分界符、回车符在字符串中不会保存;当已读入的字符数目超过字符串所能容纳的最大字符数时,将会终止读入操作。

字符串查找函数

在STL中,字符串的查找可以实现多种功能:

  • 搜索单个字符、子串;
  • 实现前向搜索、后向搜索;
  • 分别实现搜索第一个和最后一个满足条件的字符(或子串);

string::npos 静态成员常量

1
static const size_t npos = -1;

npos是静态成员常量值,值为size_t类型可能的最大值。

当这个值在字符串成员函数中的长度或者子长度被使用时,该值表示**“直到字符串结尾”。作为返回值他通常被用作表明没有匹配**。

此常量的值定义为-1,由于size_type是无符号类型,故需转换为无符号整数类型,因此它是此类型可表示值的最大值。不过实际值还是取决于size_type的实际定义类型,即无符号整型 unsigned int 的-1或无符号长整型 unsigned long 的-1。

find() 和 rfind()

find() 或 rfind() 函数用于在源串中查找完全相同的字符(串)或子串。

find() 或 rfind() 函数没有搜索到期望的字符(或子串),则返回 npos;若搜索成功,find() 返回搜索到的第 1 个字符或子串的位置,rfind() 返回逆向搜索到的第 1 个字符或子串的位置。

find() 的使用方法:

在下面的代码中,约定使用以下标识符带代表相应变量或对象:

str表示原始string对象,sstr表示另一个string对象;cstr表示C风格字符串

ch表示char字符;pos为下标表示的位置

注:“cstr的前n个字符” 基本都可以替换为 char* 指针,类似 [_Ptr, _Ptr + _Count)

1
2
3
4
5
6
7
8
//在源串str中下标pos位置(默认为0)开始查找1个字符ch
str.find(ch, pos=0);
//在源串str中下标pos位置(默认为0)开始查找C风格字符串cstr
str.find(cstr, pos=0);
//在源串str中下标pos位置开始查找C风格字符串cstr的前num个字符的子串
str.find(cstr, pos, num);
//在源串str中下标pos位置开始查找字符串对象sstr
str.find(sstr, pos=0);

rfind() 函数的功能和 find() 函数的功能类似,参数情况也类似,不同之处pos默认为npos,逻辑为在源串中下标pos位置之前逆向查找。

find_first_of() 和 find_last_of()

find_first_of() 函数和 find_last_of() 函数的使用方式、参数表与上述基本相同。

find_first_of() 函数最容易出错的地方是和 find() 函数搞混。find_first_of() 最大的区别就是如果在一个源串中查找另一个字符串,如果源串中含有另一个字符串中的任何字符(或当实参为字符类型时,源串中含有指定字符),就会查找成功,并返回任何一个字符首次在源串中出现的位置(find_last_of() 与 rfind() 的逆向查找相似)。

find_first_not_of() 和 find_last_not_of()

find_first_not_of() 函数和 find_last_not_of() 函数的使用方式、参数表与上述基本相同。

find_first_not_of() 函数可实现在源串中搜索与指定字符(串)不相等的第 1 个字符;find_last_not_of() 函数可实现在源串中搜索与指定字符(串)不相等的最后 1 个字符(与 rfind() 的逆向查找相似)。

string类的迭代器

迭代器的定义

1
string::iterator 迭代器名称;

几个常用的迭代器

begin() 和 end() 为正向迭代提供支持;rbegin() 和 rend() 为反向迭代提供支持。

  • begin() 返回指向字符串第一个字符的迭代器;

  • end() 返回指向字符串最后一个字符串的后一个位置的迭代器;

  • rbegin() 返回指向字符串最后一个字符的迭代器;

  • rend() 返回指向字符串第一个字符的前一个位置的迭代器。

迭代器的使用:

1
2
for (string::iterator iter = str.begin(); iter != str.end(); iter++) {    //……
}

About this Post

This post is written by Holger, licensed under CC BY-NC 4.0.

#C++ #字符串 #string类 #STL