string类
1 基本介绍与头文件
标准库类型string表示可变长的字符序列,使用string类型必须首先包含string头文件。string定义在命名空间std中。
// 头文件以及命名空间
#include <string>
using std::string;
2 string对象的定义和初始化
string s1; // 默认初始化,s1是一个空串
string s2(s1); // s2是s1的副本
string s2 = s1; // 等价于s2(s1),s2是s1的副本
string s3("value"); // s3是字面值“value"的副本,除了字面值最后的空字符外
string s3 = "value"; // 等价于s3("value"),s3是字面值”value“的副本
string s4(n,'c'); // 把s4初始化为连续n个字符c组成的串
2.1 拷贝初始化
string s5 = "hello"; // 使用=号初始化一个变量,实际为拷贝初始化
2.2 直接初始化
string s6("hello"); // 不使用=号初始化一个变量,为直接初始化
string s7(5, 'c'); // 直接初始化,s7内容为ccccc
对于多个值进行初始化的情况,一般使用直接初始化,当然也可以使用拷贝初始化的方式,需要显示的创建一个(临时)对象用户拷贝
string s8 = string(5,'c'); // 拷贝初始化,s8的内容是ccccc
// 本质上等价于
string temp(5,'c'); // temp的内容是ccccc
string s8 = temp; // 将temp拷贝给s8
可以看到,尽管初始化s8的语句合法,但是和初始化s7的方式比价可读性更差,也没有任何补偿优势,不建议使用。
3 string对象上的操作
string的操作 | 意义 |
---|---|
os << s | 将s写到输出流os当中,返回os |
is >> s | 从is中读取字符串赋给s,字符串以空白分隔,返回is |
getline(is,s) | 从is中读取一行赋给s,返回is |
s.empty() | s为空返回true,否则返回false |
s.size() | 返回s中字符的个数 |
s[n] | 返回s中第n个字符的引用,位置n从0记起 |
s1 + s2 | 返回s1和s2连接后的结果 |
s1 = s2 | 用s2的副本代替s1中原来的字符 |
s1 == s2 | 如果s1和s2所含的字符完全 一样,则他们相等;对大小写敏感 |
s1 != s2 | 如果s1和s2所含的字符不完全 一样,则他们不相等;对大小写敏感 |
<, <=, >, >= | 利用字符在字典中的顺序进行比较,且对字母大小写敏感 |
4 string操作实例
4.1 is >> s 和 os << s
** 4.1.1 读写string对象**
/*
* function: 演示读取string对象
*
* 2020-11-26
*/
#include <iostream>
#include <string>
using std::string;
int main(int argc, char *argv[])
{
string s1, s2; // 默认初始化,s1,s2是空串
std::cin >> s1 >> s2; // 从标准输入给s1, s2赋值,以空格分隔
std::cout << s1 << s2 << std::endl; // 将s1, s2输出到标准输出
return 0;
}
注意:1.在执行读取操作时,string对象会自动忽略开头的空白(即空格符、换行符、制表符等)并从第一个真正的字符开始读起,直到遇见下一处空白。2.和内置类型的输入输出操作一样,string对象的此类操作也是返回运算符左侧的运算对象作为结果。因此,多个输入或者多个输出可以连写在一起。
4.1.2 读取未知数量的string对象
/*
* function: 演示读取未知数量的string对象
*
* 2020-11-26
*/
#include <iostream>
#include <string>
using std::string;
int main(int argc, char *argv[])
{
string word; // 默认初始化,s为空串
while (std::cin >> word) // 反复读取,直到文件末尾
{
std::cout << word << std::endl; // 逐个输出单词,每个单词后面紧跟一个换行符
}
return 0;
}
注意:最后使用ctrl+d表文件结尾,在linux中ctrl+d 不是发送信号,而是表示一个特殊的二进制值,表示 EOF。
4.3 getline(is, s)
/*
* function: 演示使用getline读取string对象的一整行
*
* 2020-11-26
*/
#include <iostream>
#include <string>
using std::string;
int main(int argc, char *argv[])
{
string line;
while (getline(std::cin, line)) // 每次读取一行,直到文件结尾
{
std::cout << line << std::endl;
}
return 0;
}
4.4 s.empty()和s.size()
4.4.1 empty()的基本用法
/*
* function: 演示s.empty()的一般用法
*
* 2020-11-27
*/
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(int argc, char *argv[])
{
ifstream in("test.txt");
string line;
while (getline(in, line)) // 从test.txt文件中,依次读取一行,直到文件末尾
{
if (!line.empty()) // 如果为空行,则不打印
{
std::cout << line << std::endl;
}
}
return 0;
}
4.4.2 s.size()的基本用法
/*
* function: 演示s.size()的一般用法
*
* 2020-11-27
*/
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(int argc, char *argv[])
{
ifstream in("test.txt");
string line;
while (getline(in, line)) // 从test.txt文件中,依次读取一行,直到文件末尾
{
if (line.size() > 6) // 一行大于6个字符才打印
{
std::cout << line << std::endl;
}
}
return 0;
}
4.5 比较string对象
string类定义了几种用于比较字符串的运算符。这些比较运算符逐一比较string对象中字符,并且对大小写敏感。
1.如果两个string对象的长度不同,而且较短的string对象的每个字符都与较长的string对象对应位置上的字符形同,就说较短的string对象小于较长string对象。
2.如果两个string对象在某些对应的位置上不一致,则string对象比较的结构其实是string对象中第一对相异字符的比较结果。
4.6 string对象相加
/*
* function: 演示两个string相加以及字面值与string对象相加操作
*
* 2020-11-29
*/
#include <iostream>
#include <string>
using std::string;
int main(int argc, char *argv[])
{
string s1("hello");
string s2("world");
std::cout << "s1+s2=" << s1 + s2 << std::endl;
string s3 = s1 + "," + "world";
std::cout << "s3=" << s3 << std::endl;
//string s4 = "," + "world" + s1; // 错误两个运算对象都不是string
return 0;
}
5 处理string对象中的字符
在cctype头文件中定义了一组标准库函数处理这部分工作
函数名 | 意义 |
---|---|
isalnum( c ) | 当c是字母或数字时为真 |
isalpha( c ) | 当c是字母时为真 |
isdigit( c ) | 当c是数字时为真 |
islower( c ) | 当c是小写字母时为真 |
isupper( c ) | 当c是大写字母时为真 |
isxdigit( c ) | 当c是十六进制数子时为真 |
tolower( c ) | 如果c是大写字母,输出对应的小写字母;否则原样输出c |
toupper( c ) | 如果c是小写字母,输出对应的大写字母;否则原样输出c |
iscntrl( c ) | 当c是控制字符时为真 |
isprint( c ) | 当c是可打印字符时为真(即c是空格或c具有有可视形式) |
ispunct( c ) | 当c是标点符号时为真(即c不是控制字符、数字、字母、可打印空白中的一种) |
isspace( c ) | 当c是空白时时为真(即c是空格、横向制表符、纵向制表符、回车符、换行符、进纸符中的一种) |
isgraph( c ) | 当c不是空格但可以打印时为真 |
5.1 处理每个字符
处理每个字符?使用基于范围的for 语句
如果想对string对象中的每个字符做点儿什么操作,目前最好的办法是使用C++11新标准提供的一种语句:范围for(range for)语句。这种语句遍历给定序列中的每个元素并对序列中的每个值执行某种操作,其语法形式是:
for(declaration:expression)
statement
/*
expression部分是一个对象,用于表示一个序列。
declaration部分负责定义一个变量,该变量将被用于访问序列中的基础元素。
每次迭代,declaration部分的变量会被初始化为expression部分的下一个元素值。
*/
/*
* function: 演示使用范围for语句遍历string对象的每个字符
*
* 2020-11-30
*/
#include <iostream>
#include <string>
using std::string;
int main(int argc, char *argv[])
{
string str("hello world");
for (auto c : str) // 对于str中的每个字符
{
std::cout << c << std::endl; // 输出当前字符,后面紧跟一个换行符
}
return 0;
}
/*
* function: 演示使用范围for语句遍历string对象的每个字符并且修改
*
* 2020-11-30
*/
#include <iostream>
#include <string>
#include <cctype>
using std::string;
int main(int argc, char *argv[])
{
string str("hello world");
std::cout << str <<std:: endl;
for (auto &c : str) // 对于str中的每个字符(需要修改字符c,注意:c是引用)
{
c = toupper(c); // 判断是不是小写字母,如果是小写字母改为大写
std::cout << c;
}
std::cout << std::endl;
return 0;
}
5.2 处理部分字符
只处理一部分字符?
如果要处理string对象中的每一个字符,使用范围for语句是个好主意。然而,有时我们需要访问的只是其中一个字符,或者访问多个字符但遇到某个条件就要停下来。例如,同样是将字符改为大写形式,不过新的要求不再是对整个字符串都这样做,而仅仅把string对象中的第一个字母或第一个单词大写化。
要想访问string对象中的单个字符有两种方式:一种是使用下标,另外一种是使用迭代器,这个方法以后在介绍。
下标运算符([])接收的输入参数是string::size_type类型的值,这个参数表示要访问的字符的位置:返回值是该位置上字符的引用。
注意:string的下标必须大于等于0而小于s.size().
使用超出此范围的下标将引发不可预知的结果,依次类推,使用下标访问空string也会引发不可预知的结果。
/*
* function: 演示使用下标运算符修改第一个字符
*
* 2020-11-30
*/
#include <iostream>
#include <string>
#include <cctype>
using std::string;
int main(int argc, char *argv[])
{
string str("hello world");
std::cout << str <<std:: endl;
if (!str.empty()) // 注意判断string是否为空
{
str[0] = toupper(str[0]); // 修改第一个字符
}
std::cout << str << std::endl;
return 0;
}
/*
* function: 演示使用下标运算符修改第一个单词
*
* 2020-11-30
*/
#include <iostream>
#include <string>
#include <cctype>
using std::string;
int main(int argc, char *argv[])
{
string str("hello world");
std::cout << str <<std:: endl;
// 判断sting对象不为空
if (!str.empty())
{
// 依次处理str中的字符直到处理完所用字符或者遇到第一个空白
for (decltype(str.size()) ii = 0; ii != str.size() && !isspace(str[ii]); ++ii)
{
str[ii] = toupper(str[ii]);
}
}
std::cout << str << std::endl;
return 0;
}
扩展阅读
1 string::size_type类型介绍
string类及其他大多数标准库类型都定义了几种配套的类型。这些配套类型体现了标准库类型与机器无关的特性,类型size_type 即是其中的一种。在具体使用的时候,通过作用域操作符来表明名字size_type是在类string中定义的。
尽管我们不太清楚string::size_type类型的细节,但有一点是可以肯定的:它是一个无符号类型的值,而且能足够存放下任何string对象的大小。所有用于存放string类的size函数返回值的变量,都应该是string::size_type类型的。
在c++11新标准中,允许编译器通过auto类型说明符或者decltype类型指示符来推断变量的类型:
// len的类型是string::size_typelen
auto len = s.size();
decltype(s.size()) len = s.size();
注意:如果一条表达式中已经有了size()函数就不要再使用int了,这样可以避免混用int和unsigned可能带来的问题。
例如:假设n是一个具有负值的int,则表达式s.size()<n的判断结果几乎肯定是true。因为负值n会自动的转换成一个比较大的无符号值。