深入理解C++中string类
背景介绍:在C++中使用string.h和cstring并不是所要介绍的string类指的是C语言库中字符串,而本文所要介绍的是string类。要使用string类主要是了解它的公共接口,也就是string类的众多方法。
下面介绍的是string类的构造函数,主要用于将字符串赋值给变量、合并字符串、比较字符串、若干重载操作符、在字符串中寻找子字符串。
一、构造字符串
string类的7个构造函数,如下表所示
构造函数 | 描述 |
---|---|
string(const char* s) | 将string对象初始化为s指向的NBTS |
string(size_type n,char c) | 创建一个包含n个元素的string对象,其中每个对象都被初始化为字符c |
string(const string & str) | 将一个string初始化为string对象str(赋值构造函数) |
string() | 创建一个默认的string对象,长度为0(默认构造函数) |
string(const char* s,size_type n) | 将string对象初始化为s指向的NBTS的前n个字符,即是超过了NBTS结尾 |
template<class Iter> string(Iter begin,Iter end) | 将string对象初始化为区间[begin,end)内的字符,其中begin和end为迭代器用于指定位置,范围包括begin,但是不包括end |
string(const string & str,string size_type pos=0,size_type n=npos) | 将一个string对象初始化为对象str中从位置pos开始到结尾的字符,或从位置pos开始的n个字符 |
以下为C++11后添加的构造函数 | |
string(string && str) noexcept | 将string对象初始化为string对象str,并可能修改str(移动构造函数) |
string(initializer_list <char > il) | 他将string对象初始化为初始化列表il中的字符 |
注:NBTS是以空字符串结束的字符串,也就是传统的C字符串。
示例代码如下:
//str1.cpp -- introducing the string class
#include <iostream>
#include <string>
//using string constructors
int main()
{
using namespace std;
string one("Lottery Winner!");
cout << one << endl;
string two(20,'&');
cout << two << endl;
string three(one);
cout << three << endl;
one += "Oops!";
cout << one << endl;
two = "Sorry! That was";
three[0] = 'P';
string four;
four = two + three;
cout << four << endl;
char alls[] = "All's well that ends well";
string five(alls,20);
cout << five << "!\n";
string six(alls+6,alls+10);
cout << six << ",";
string seven(&five[6], &five[20]);
cout << seven << "...\n";
string eight(four,7,16);
cout << eight << "in motion!" << endl;
return 0;
}
结果如图:
说明:four = two + three;
string类中重载了=和+操作符。
二、string类输入
对于C语言风格的字符串,有三种方式
char info[100];
cin >> info;
cin.getline(info,100);
cin.get(info,100);
string类有两种输入方式:
string stuff;
cin >> stuff;
getline(cin,stuff);
string类的getline()可以自动调节大小。但是也会存在限制,比如:第一个限制因素是有string类中定义的string::npos决定的string对象最大长度,如果你用此方法来输入整个文件的内容可能会出错。第二个错误是你的计算机可以分配的最大内存大小。
往string对象中输入字符,可能会出现三种情况:
1、输入到文件尾了,输入流的寄存器eofbit将会被设置为true,这意味着string类中的fail()方法和eof()方法都将返回true;
2、如果遇到分界符\n,则会从输入流中删除分界符,不存储它;
3、读取的字符数达到最大允许值string::npos,或者是你计算机可分配内存中比较小的哪一个值,则会设置底层寄存器failbit,这意味中fail()方法将返回true。
实例代码如下:
//strfile.cpp -- read strings from a file
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
int main()
{
using namespace std;
ifstream fin;
fin.open("tobuy.txt");
if (fin.is_open() == false)
{
cerr << "can't open file.Bye.\n";
exit(EXIT_FAILURE);//程序因为异常而结束
}
string item;
int count = 0;
getline(fin,item,':');
while (fin)
{
++count;
cout << count << ":" << item << endl;
getline(fin,item,':');
}
cout << "Done\n";
fin.close();
return 0;
}
结果如图所示:
注:辅助记忆ASCII码,数字0(48)< 大写字符A(65) < 小写字符a(97)。
三、使用字符串
好了到这里,本文可以总结一下,string类可以使用第一个表格中的九种方式构造string对象、重载<<和[]符号来显示string对象、使用getline(cin,a);将数据读取和附加到string对象上、重载了=给string对象赋值、重载+将两个string对象连接起来。
对于比较两个string对象的大小,则是重载了6个关系运算符,比较的对视这些字符串在不同系统中的机器码的大小,不限定于ASCII码。在此不细说了。
上面提到的string::npos,指的是string对象可存储的最大字符数,通常指的是无符号的int或者无符号的long的最大值,32位机和64位机的npos值是不一样的。如需研究,可参考计算机组成原理。
下面重点介绍find()方法,用于查找字符串中给定的子字符串。
方法原型 | 描述 |
---|---|
size_type(const string & str,size_type pos = 0)const | 从字符串的pos位置开始,查找子字符串str。如果找到则返回该子字符串首次出现的位置的索引;否则返回string::npos |
size_type find(const char * s,size_type pos=0)const | 从字符串的pos位置开始,查找子字符串s。如果找到则返回该子字符串首次出现的位置的索引;否则返回string::npos |
size_type find(const char * s,size_type pos=0,size_type n) | 从字符串的pos位置开始,查找子字符串s的前n个字符。如果找到则返回该子字符串首次出现的位置的索引;否则返回string::npos |
size_type find(const char ch,size_type pos=0) | 从字符串的pos位置开始,查找子字符ch。如果找到则返回该子字符串首次出现的位置的索引;否则返回string::npos |
其中string类还提供了好多的其他变形方法,rfind()方法查找字符串最后一次出现的位置;find_first_of()方法在字符串中任一字符首先出现的位置。find_last_of()方法是在字符串中查找子字符串中每一个字符最后出现的位置。
应用代码如下:
//hangman.cpp -- some string methods
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
#include <cctype>
using std::string;
const int NUM = 26;
const string wordlist[NUM] = { "apiary","beetle","cereal","danger",
"ensign","florid","garage","health","insult","jackal",
"keeper","loaner","manage","nonce","onset","plaid","quilt","remote",
"stolid","train","useful","valid","whence","xenon","yearn","zippy" };
int main()
{
using std::cout;
using std::cin;
using std::tolower;
using std::endl;
std::srand(std::time(NULL));
char play;
cout << "Will you play a word game? <y/n>";
cin >> play;
play = tolower(play);//将大写的字母变成小写字母
while (play == 'y')
{
string target = wordlist[std::rand()%NUM];
int length = target.length();
string attempt(length,'-');//复制length个-给attempt对象
string badchars;
int guesses = 6;
cout << "Guess my secret word.It has " << length
<< " letters,and you guess\n"
<< "one letter at a time, you get " << guesses
<< " wrong guesses.\n";
cout << "Your word:" << attempt << endl;
while (guesses>0&&attempt!=target)
{
char letter;
cout << "Guess a letter:";
cin >> letter;
if (badchars.find(letter) != string::npos || attempt.find(letter) != string::npos)
{
cout << "You already guessed that. Try again.\n";
continue;
}
int loc = target.find(letter);
if (loc == string::npos)
{
cout << "Oh,bad guess!\n";
--guesses;
badchars += letter;
}
else
{
cout << "Good guess!\n";
attempt[loc] = letter;
//check if letter appears again
loc = target.find(letter,loc+1);
while (loc!=string::npos)
{
attempt[loc] = letter;
loc = target.find(letter,loc+1);
}
}
cout << "Your word:" << attempt << endl;
if (attempt != target)
{
if (badchars.length() > 0)
cout << "Bad choices:" << badchars << endl;
cout << guesses << "bad guesses left\n";
}
}
if (guesses > 0)
cout << "That's right!\n";
else
cout << "Sorry, the word is" << target << ".\n";
cout << "Will you play another? <y/n>";
cin >> play;
play = tolower(play);
}
cout << "Bye\n";
return 0;
}
四、string还提供了哪些功能
1、删除字符串的部分或者全部内容;
2、用一个字符串的部分或者全部内容替换另一个字符串中的部分或者全部内容;
3、将数据插入到字符串中或删除字符串中数据;
4、将一个字符串中的部分或者全部内容与另一个字符串的全部或者部分的被内容进行比较;
5、从字符串中提取子字符串;
6、将一个字符串内容复制到另一个字符串中;
7、交换两个字符串的内容。
//str2.cpp -- capacity() and reserve()
#include <iostream>
#include <string>
int main()
{
using namespace std;
string empty;
string small = "bit";
string larger = "Elephants are a girl's best friend";
cout << "Sizes:\n";
cout << "\tempty:" << empty.size() << endl;
cout << "\tsmall:" << small.size() << endl;
cout << "\tlarger:" << larger.size() << endl;
cout << "Capacities:\n";
cout << "\tempty:" << empty.capacity() << endl;
cout << "\tsmall:" << small.capacity() << endl;
cout << "\tlarger:" << larger.capacity() << endl;
empty.reserve();
small.reserve();
cout << "Capacity after empty.reserve(50):"
<< empty.capacity()<<endl;
cout << "Capacity after small.reserve(50):"
<< small.capacity() << endl;
return 0;
}
五、字符串的种类
string库实际上是基于一个模板类的:
template <class charT,class traits = char _traits<charT>,class Allocator = allocator<cahrT>>
basic_string {...};
模板basic_string有4个具体化,每一个躯体化都有一个typedef名称:
typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring;
typedef basic_string<char16_t> u16string;
typedef basic_string<char32_t> u32string;
这样就可以使用基于类型wchar_t、char16_t、char32_t和char的字符串了。