标准库类型string表示可变长的字符序列,使用string类型必须包含string头文件以及命名空间std,即具体如下:

#include <string>
using namespace std;

1. 定义并初始化string对象

如果定义变量时没有指定初始值,则变量在编译时被默认赋予初值(函数内局部变量除外),具体如下:

全局变量、static静态局部变量、无须显示初始化的类 =====> 支持默认初始化

函数内部的局部变量 =====> 必须手动初始化,否则有出现 不确定值 的情况

所以最稳妥的办法就是在 不管在定义什么变量时我们手动初始化

//默认初始化,s1是一个空串(绝大多数类都支持无须显式初始化而定义对象)
string s1;
string s2(s1);
string s3 = s1;
//使用等号表示执行的是 拷贝初始化copy initialization(不包含最后的'\0'空字符)
string s4 = "hello string";
//不使用等号表示 直接初始化direct initialization(同上,不包含'\0')
string s5("hello string");
//把s6初始化为n个字符c组成的字符串
string s6(n, 'c');

构造string的其他方法

构造函数接受一个string或一个const char *参数,还接受(可选的)指定拷贝多少个字符的参数。

string s(cp, n) s是cp指向的数组中前n个字符的拷贝,此数组至少应该包含n个字符

string s(s2, pos2) s是string s2从下标pos2开始的字符的拷贝。要求:pos2 <= s2.size(),否则会报out_of_range异常

string s(s2, pos2, len2) s是string s2从下标pos2开始len2个字符的拷贝。pos2大小要求同上,len2的值不管,标准库最多拷贝到string结尾

const char *cp = "Hello world";
char noNull[] = {'H', 'i'};
string s1(cp); //拷贝cp中的字符直到遇到空字符;s1 == "Hello world"
string s2(noNull, 2); //拷贝noNull中的两个字符;s2 == "Hi"
string s3(noNull); //未定义:noNull不是以空字符结束

string s4(cp+6, 5); //从cp[6]开始拷贝5个字符;s4 == "world"
string s5(s1, 6, 5); //从s1[6]开始拷贝5个字符;s5 == "world"
string s6(s1, 6); //从s1[6]开始拷贝,直至s1末尾;s6 == "world"
string s7(s1, 6, 20);//正确,只拷贝到s1末尾; s7 == "world"
try {
    cout << "size: " << s1.size() << endl;
    string s8(s1,12); //s1.size() == 11,若pos2>11,则报out_of_range异常
} catch (out_of_range err) {
    cout << "xx" << endl;
    cout << err.what() << endl;
}

2. string对象上的操作

将标准输入读取到string类型的s对象中

string s;
cin >> s;//将string对象读入s,遇到空白停止

string word;
while(cin >> word) { //反复读取,直至到达文件末尾
    cout << word << endl; //可以实现逐个输出单词,每个单词后面紧跟一个换行符
}

使用getline读取一整行

getline仅遇到换行符就结束读取操作并返回(空格也会被当做一个字符),适合读取文本相关信息

//从标准输入中读取
string line;
while( getline(cin, line) ) {
    cout << "getline: " << line << endl;
}
    
//从文本中读取
string line2;
ifstream file("./../test.txt");
if (file.is_open()) {
    while( getline(file, line2)) {
        cout << "getline2: " << line2 << endl;
    }
} else {
    cout << "Failed to open file" << endl;
}

s.empty() s为空返回true,否则返回 false

s.size() 返回string对象中字符长度个数

本质上size()函数返回的是一个string::size_type类型的值。它是一个无符号类型的值而且能足够存放下任何string对象的大小。在C++11标准中,允许编译器通过 auto / decltype 来推断变量的类型:auto len = s.size()

substr操作用于实现截取字符串的操作

s.substr(pos, n) 返回一个string,包含s中从pos开始的n个字符的拷贝。pos的默认值为0。n的默认值为s.size() - pos,即拷贝从pos开始的所有字符。总之,无论n多大,都只会拷贝到s的末尾,n的大小并不会影响具体操作,只有pos>s.size()才会报out_of_range异常。

string s("hello world");
string s2 = s.substr(0, 5);
string s3 = s.substr(6); //n的值没指定,那么就从s[6]开始拷贝直至s末尾
string s4 = s.substr(6, 110);

try {
    cout << "size: " << s.size() << endl; //size: 11
    string s5 = s.substr(12);//pos>s.size()会报out_of_range异常
} catch (out_of_range err) {
    cout << err.what() << endl;
}

s.insert(pos, args) 在pos之前插入args指定的字符。pos可以是一个下标或一个迭代器。接受下标的版本返回一个指向s的引用;接受迭代器的版本返回指向第一个插入字符的迭代器

s.erase(pos, len) 删除从位置pos开始的len个字符。如果len被省略,则删除从pos开始直至s末尾的所有字符。返回一个指向s的引用

s.assign(args) 将s中的字符替换为args指定的字符,仅一个参数时assign总是替换s中所有内容。返回一个指向s的引用

s.append(args) 将args追加到s,append总是将新字符追加到s末尾。返回一个指向s的引用

s.replace(range,args) 删除s中范围range内的字符,替换为args指定的字符。range或者是一个下标和一个长度,或者是一对指向s的迭代器。返回一个指向s的引用

string s("hello world");
s.insert(s.size(),5,'!');// s == "hello world!!!!!"
cout << "s.size()=" << s.size() << endl; // s.size()=16
s.erase(s.size() - 5, 5); // pos=11,删除从s[11]开始的5个字符 s == "hello world"

const char *cp = "Stately, plump Buck";
s.assign(cp, 7); // s == "Stately", s.size()=7
s.insert(s.size(), cp + 7); // 在s[7]之前插入cp[7]及其以后的字符 s == "Stately, plump Buck"

string s("C++ Primer 4th Ed.");
s.erase(11, 3);//删除从s[11]开始的三个字符 s=="C++ Primer  Ed."
s.insert(11, "5th");//在s[11]之前插入"5th"这三个字符 s=="C++ Primer 5th Ed."
s.replace(11, 3, "6th"); //从s[11]开始删除三个字符并插入"6th" s=="C++ Primer 6th Ed."
s.replace(11, 3, "Fifth"); //从s[11]开始删除三个字符并插入5个字符"Fifth" s=="C++ Primer Fifth Ed."

string搜索操作 find(大小写敏感)

find系列函数返回值类型为 string::size_type值,表示匹配发生位置的下标,搜索失败返回string::npos,标准库将npos定义为一个const string::size_type类型,并初始化为-1。

s.find(args) 查找s中args第一次出现的位置

s.rfind(args) 查找s中args最后一次出现的位置

s.find_first_of(args) 在s中查找args中任何一个字符第一次出现的位置

s.find_last_of(args) 在s中查找args中任何一个字符最后一次出现的位置

s.find_find_not_of(args) 在s中查找第一个不在args中的字符

s.find_last_not_of(args) 在s中查找最后一个不在args中的字符

auto rt = s.find("zhu");
if (rt == string::npos) { //未找到对应的字符串"zhu",返回npos
    cout << "Failed to find 'zhu'" << endl;
}

string numbers("0123456789"), name("r2d2");
auto pos = name.find_first_of(numbers); // pos=1,name中第一个数字的下标为1
cout << "pos:" << pos << endl;

string dept("03714p3");
auto pos2 = dept.find_first_not_of(numbers); // pos=5,字符'p'的下标
cout << "pos:" << pos2 << endl;

// 正向搜索
string::size_type pos3 = 0;
//find_first_of入参pos3表示从name中位置pos3开始查找字符串numbers;当入参为0时,在下标位置0没找到会自动找下一个字符
while((pos3 = name.find_first_of(numbers, pos3)) != string::npos) {
    cout << "found number at index: " << pos3 << ", element is " << name[pos3] << endl;
    ++pos3;
}

// 逆向搜索
string river("Mississippi");
auto first_pos = river.find("is"); //在river中找到"is"的位置在 1
cout << "first_pos:" << first_pos << endl;
auto last_pos  = river.rfind("is");//在river中找到最后一个匹配"is"的位置在 4
cout << "last_pos:" << last_pos << endl;

s.compare函数,用于字符串之间的比较

类似于C标准库的strcmp函数,根据s是等于、大于还是小于参数指定的字符串,s.compare返回0、正数或负数。

s.compare的参数有以下几种:

s2 比较s和s2

pos1, n1, s2 将s中从pos1开始的n1个字符与s2进行比较
pos1, n1, s2, pos2, n2 将s中从pos1开始的n1个字符与s2中从pos2开始的n2个字符进行比较
cp 比较s与cp指向的以空字符结尾的字符数组
pos1, n1, cp 将s中从pos1开始的n1个字符与cp指向的以空字符结尾的字符数组进行比较
pos1, n1, cp, n2 将s中从pos1开始的n1个字符与指针cp指向的地址开始的n2个字符进行比较

字符串之间运算

比较string对象:逐一比较每一个字符,并对大小写敏感

相等性运算符(==和!=)分别检验两个string对象相等或不相等

关系运算符(<、<=、>、>=)分别检验一个string对象是否小于、小于等于、大于、大于等于 另一个string对象

s1 = s2 表示将对象s2赋值给s1

s1 + s2 表示将s1与s2串联连接形成一个新的string

s1 + ",123" 同上,string对象加上一个字符串,也是为了连接形成新的string

处理string对象中的字符

#include<cctype>
isalnum(c) //当c是字母或数字时为真
isalpha(c) // 当c是字母时为真
isdigit(c) //当c是数字时为真
islower(c) // 当c是小写字母时为真
ispunct(c) // 当c是标点符号时为真(即c不是控制字符、数字、字母、可打印空白中的一种)
isspace(c) // 当c是空白时为真(即c是空格、横向制表符、纵向制表符、回车符、换行符、进纸符中的一种)
isupper(c) // 当c是大写字母时为真
tolower(c) // 如果c是大写字母,输出对应的小写字母;否则原样输出c
toupper(c) // 如果c是小写字母,输出对应的大写字母;否则原样输出c

我们通过使用C++11标准中的范围for语句结合上面的函数来对string对象的每个字符进行处理

以下是一个大小写字母互换的例子,我们在范围for语句中结合tolower、toupper来对大小写进行切换:

string str("Hello STRing");
string get_str;
int temp = 0;
for (auto c : str) {
    if (c >= 97 && c <= 122) { // 如果字符大于等于97,小于等于122(即字符在a~z之间)
        temp = toupper(c);
        //temp = c - 32;
    } else if ( c >= 65 && c <= 90) { //字符在 A~Z之间
        temp = tolower(c);
        //temp = c + 32;
    } else {
        temp = c;
    }
    get_str += temp;
}
cout << "get_str: " << get_str << endl;

如果要处理string对象中的每一个字符,使用范围for语句是个好主意。但是我们也可以使用下标或使用迭代器进行操作。

下标运算符( [ ]) 接收的输入参数是string::size_type类型的值,这个参数表示要访问的字符的位置;返回值是该位置上字符的引用。

string对象的下标必须大于等于0且小于 s.size(),下标最大是 s.size() - 1

以下使用下标执行迭代使得遇到首个空格前的所有字符转变为大写字符:

string str("Hello world");
for (decltype (str.size()) index = 0; ((index != str.size()) && (!isspace(str[index]))); index++)
    str[index] = toupper(str[index]);
cout << "get_str: " << str << endl;

使用下标时必须保证其在合理范围之内,我们设置下标的类型为string::size_type,因为此类型是无符号数,可以确保下标不会小于0,此时代码只要保证下标小于s.size()的值即可。

3. 数值转换

string与unsigned char *类型的转换

// string 转 unsigned char *
const unsigned char *const_uc = (const unsigned char *)(str.c_str());
unsigned char * c = const_cast<unsigned char *>(const_uc); //去除const限定

// unsigned char * 转 string
string result= reinterpret_cast<char *>(c);// reinterpret_cast<>()用于指针类型之间的相互转换

to_string 用于将正数转换为字符表示形式

stoi、stol、stoul、stoll、stoull 用于将字符串转换为int、long、unsigned long、long long、unsigned long long

stof、stod、stold 用于将字符串转换为float、double、long double类型

int i = 125;
string str = to_string(i);
int iI = stoi(str);
double dI = stod(str);