C++ 中标准库类型 string
标准库类型 string 表示可变的字符序列,使用 string 类型必须首先包含 string 头文件。
string 本质上是一个类,是 STL 提供的 char* 的容器。
定义初始化 string 对象
初始化 string 对象的方式 | |
---|---|
string s1 | 默认初始化 s1 是一个空串 |
string s2(s1) | s2 是 s1 的副本,拷贝构造 |
string s2 = s1 | 等价于 string s2(s1) ,拷贝构造 |
string s3("value") | s3 是字面值 "value" 的副本,除了最后的 '\0' 字符 |
string s3 = "value" | 等价于 string s3("value"),s3 是 "value" 的副本 |
string s4(n, 'c') | v4 是 n 个 'c' 拼接而成的 string 对象 |
直接初始化和拷贝构造
如果使用等号 ( = ) 初始化一个变量,实际上执行的是拷贝初始化 (copy initialization) 编译器把等号右侧的初始化值拷贝到新创建的对象中去。与之相反,如果不使用等号,则执行的是直接初始化 (direct initialization)。
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 个字符,从 0 开始计数 |
s1 + s2 | 返回 s1 和 s2 连接后的结果 |
s1 = s2 | 用 s2 的副本代替 s1 |
s1 == s2 | 如果 s1 与 s2 中所含的字符完全一样,则它们相等;string 对象的相等判断对大小写敏感 |
s1 != s2 | 等性判断对大小写敏感 |
<,<=,>,>= | 利用字符在字典中的顺序进行比较,且对字母的大小写敏感 |
读写 string 对象
示例
string s; // 空字符串
cin >> s; // 将 string 读入 s, 遇到空白停止
cout << s <<endl; // 将 s 输出
这段程序首先定义一个名为 s 的空 string 对象,然后将标准输入的内容读取到 s 中。在执行读取操作时,string 对象会自动忽略开头的空白(即空格符、换行符、制表符等)并从第一个真正的字符开始读取,直到遇到下一个空白为止。
综上所述,如果输入的是 " hello world " 那么输出是 "hello" 没有任何空格。
如果修改成
string s1, s2;
cin >> s1 >> s2;
cout << s1 << s2 << endl;
如果是这种情况,输入 " hello world " 那么输出是 "helloworld" 还是没有任何空格。
读取任意数量的 string 对象
string s;
while(cin >> s){ // 反复读取 直到达到文件尾
cout << s << endl; // 逐个输出 s 每个单词接一个换行
}
使用 getline 读取一整行
string s;
while(getline(cin, s)){ // 每次读入一整行 直到文件尾
cout << s << endl;
}
触发 getline 函数返回的那个换行符实际上是被丢弃掉了。得到的 string 不包括换行符。
string 的 empty 和 size 操作
empty 函数根据 string 对象是否为空,返回一个对于的布尔值
size 函数返回 string 对象的长度,即 string 对象中字符的个数
string::size_type 类型
size 函数的返回的是一个 string::size_type 类型的值。string 类以及其他大多数标准库类型都定义了几种配套的类型,这些配套类型体现了标准库类型与机器无关的特性,类型 size_type 即是其中的一种。
尽管我们不清楚 string::size_type 类型的细节,但有一点是肯定的:它是一个无符号类型的值,而且能够存放得下任何 string 对象的大小。
由于 size 函数返回的是一个无符号整数,因此切记,如果在表达式中混用了带符号数和无符号数可能产生意想不到的结果。
int 类型参与无符号数的运算时,会转换成无符号数据,正数无所谓,但是负数会变成一个很大的无符号数。
C++ primer 建议:如果一个表达式中已经有 size() 函数,就不要再使用 int 了,这样可以避免混用 int 和 unsigned 可能带来的问题。
比较 string 对象
string 比较依照(大小写敏感的)字典顺序:
- 如果两个 string 对象的长度不同,而且较短的 string 对象的每个字符都与较长 string 对象对应位置上的字符相同,就说较短 string 对象小于较长的 string 对象。
- 如果两个 string 对象在某些对应的位置上字符不一致,则 string 对象比较的结果其实是 string 对象中第一对相异字符比较的结果。
示例
string str = "Hello";
string phare ="Hello world";
string slang = "hiya";
bool b1 = str < phare; // true
bool b2 = str < slang; // true
比较字符串函数签名int compare(const string& s) const;
int compate(const char* s) const;
逐个比较 ASCII 码
大于返回 1
小于返回 -1
相同返回 0
注意:compare 最常见的用处是比较字符串相等
两个 string 相加
两个 string 对象相加得到一个新的 string 对象,其内容是把左侧的运算对象与右侧的运算对象串联而成。
string 对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符 (+) 的两侧的运算对象至少有一个是 string:
string s1 = "hello", s2 = "world";
string s3 = s1 + ", " + s2 + '\n';
string s4 = s1 + ","; // ok 字符串拼接
string s5 = "hello" + ", "; // compile error 字符串字面值相加是不被支持的
string s6 = s1 + ", " + "world"; // ok
string s7 = "hello" + ", " + s2; // compile error 字面值相加是不被支持的
直接使用字面值相加会引起编译错误
因为某些历史原因,也为了与 C 兼容,所以 C++ 语言中的字符串字面值并不是标准库类型 string 的对象。
切记,字符串字面值与 string 是不同的类型。
处理 string 对象中的字符
表 cctype 头文件中的函数
cctype 头文件中的函数 | |
---|---|
isalnum(c) | 当 c 是字母或者数字时为真 |
isalpha(c) | 当 c 是字母时为真 |
iscntrl(c) | 当 c 是控制字符时为真 |
isdigit(c) | 当 c 是数字时为真 |
isgraph(c) | 当 c 不是空格但可打印时为真 |
islower(c) | 当 c 是小写字母时为真 |
isprint(c) | 当 c 是可打印字符时为真(即 c 是空格或 c 具有可视形式) |
ispunct(c) | 当 c 是标点符号时为真(即 c 不是控制字符、数字、字母、可打印空白中的一种) |
isspace(c) | 当 c 是空白时为真(即 c 是空格、横向制表符、纵向制表符、回车符、换行符、进纸符中的一种) |
isupper(c) | 当 c 是大写字母时为真 |
isxdigit(c) | 当 c 是十六进制数字时为真 |
tolower(c) | 如果 c 是大写字母,输出对应的小写字母;否则原样输出 c |
tpupper(c) | 如果 c 是小写字母,输出对应的大写字母,否则原样输出 c |
建议:使用 C++ 版本的 C 标准头文件
C++ 标准库中除了定义 C++ 语言特有的功能外,也兼容了 C 语言的标准库。 C 语言的头文件形如 name.h,C++ 则将这些文件命名为 cname。也就是去掉了 .h 后缀,而在文件名 name 之前添加了字母 c,这里的 c 表示这是一个属于 C 语言标准库的头文件。
因此 cctype 头文件与 ctype.h 头文件的内容是一样的,只不过从命名规范上来讲更符合 C++ 语言的要求。特别的,在名为 cname 的头文件中定义的名字从属于命名空间 std,而定义在名为 .h 的头文件中的则不然。
一般来说,C++ 程序应该使用名为 cname 的头文件而不使用 name.h 的形式,标准库中的名字总能在命名空间 std 中找到。如果使用 .h 形式的头文件,程序员就不得不时刻牢记哪些是从 C 语言那儿继承过来的,哪些又是 C++ 语言所独有的。
使用 for 范围循环处理字符
for 范围循环的语法句式for (declaration : expression) { statement }
一个 string 对象表示一个字符的序列,因此 string 对象可以作为范围 for 循环中的 expreesion 的部分。
示例
string str("hello world");
for(auto c : str){
cout << c << endl;
}
在这个 for 返回循环中, c 是 char 类型。如果使用 auto& c 就会是 char& 类型,引用可以避免一次拷贝。
string 下标运算
下标运算符 [ ] 接收的输入参数是 string::size_type 类型的值,这个参数表示要访问的字符的位置;返回值是该位置上字符的引用,可以作为左值使用。
string 对象的下标必须大于等于 0 而小于 s.size()
使用超出此范围的下标将引发不可预知的结果,以此推断,使用下标访问空 string 也会引发不可预知的结果。
下标的值称为“下标”或者“索引”,任何表达式只要它的值是一个整形就能作为索引。不过如果某个索引是带符号类型的值将自动转换成由 string::size_type 表达的无符号类型。
提示:注意检查下标的合法性
使用下标时必须确保其在合理范围之内,也就是说,下标必须大于等于 0 而小于字符串的 size() 的值。一种简便易行的方法是,总是设下标的类型为 string::size_type,因为此类型是无符号数,可以确保下标不会小于 0。此时,代码只需要保证下标小于 size() 就可以了。
C++ 标准并不要求标准库检测下标是否合法,一旦使用了一个超出范围的下标,就会产生不可预知的结果。
读取字符的函数签名
char& operator[](int n);
数组方式读取字符char& at(int n);
函数成员读取字符
常用函数原型
assign 分配运算
string& assign(const char* s);
将常量字符串分配给 stringstring& assign(const char* s, int count);
将常量字符串中的指定长度的一段,分配给 stringstring& assign(const string& s);
将 string 的内容分配给 stringstring& assign(int count, char c);
将重复 count 个的字符 c 组成的字符串分配给 string
string 字符串拼接 + 与 append
函数原型string& operator+=(const char* str);
直接通过 + 号连接字符串常量string& operator+=(const char c);
通过 + 连接字符string& operator+=(const string& str);
通过 + 连接 string
append 追加字符串
string& append(const char *s);
追加一个字符串常量string& append(const char* s, int count);
将字符串 s 前 count 个字符追加到末尾string& append(const string &s);
将 string s 追加到末尾string& append(const string &s, int pos, int n);
将 string s 从位置 pos 开始的前 n 个字符追加到末尾,包含 pos
请注意追加运算!append 追加 string 的第一个数字是 pos 如果忽略 n 作为参数的话,将会读取 pos 开始到结尾的全部字符
string 查找替换 find rfind
查找 find rfind(右查找)
find 左查找 查找第一个匹配项目
返回值为 int 类型,找不到返回 -1,find 的函数原型后边有 const 代表方法不会修改任何类中的数据。int find(const string& s, int pos = 0) const;
从 pos 下标开始查找 string 返回匹配到的首字母下标int find(const char* s, int pos = 0) const;
从 pos 开始查找字符串常量,返回找到的首字母下标int find(const char* s, int pos, int int n) const;
从 pos 开始,查找参数 s 的前 n 个字符组成的字符串int find(const char c, int pos = 0) const;
从 pos 位置查找字符 c
rfind 右查找 查找最后一个匹配项目
npos 最后一个的下标int rfind(const string& s, int pos = npos) cosnt;
从 pos 下标开始往左边查找 stringint rfind(const char* s, int pos = npos)const;
从 pos 下标开始往前查询字符串常量 sint rfind(const char* s, int pos, int n) const;
从 pos 下标往前查找 s 字符串常量的前 n 个字符组成的字符串int rfind(const charc, int pos = npos) const;
查找 字符 c 最后一次出现的位置
替换 replacestring& replace(int pos, int n, const string& str);
从 pos 开始的第 n 个字符内容替换为 strstring& replace(int pos, int n, const char* s);
从 pos 开始的第 n 个字符的内容替换为 s
string 插入与删除
string& insert(int pos, const char* s);
将字符串常量插入到 string 中的第 pos 个下标位置string& insert(int pos, const string& str);
将字符串插入到 string 中的第 pos 个下标位置string& insert(int pos, int n, char c);
将 n 个字符 c 插入到 pos 下标位置string& erase(int pos, int n = npos);
删除从 pos 下标开始一共 n 个字符
插入/删除的操作的第一个参数都是 pos 下标位置
读取字串 substr
string substr(int pos = 0, int n = npos) const;
读取从 pos 开始的 n 个字符作为新的字符串
字符串判断包含关系
str1.find(str2) != string::npos
等同于 str1.Contain(str2)
npos 是否定的含义
string 与 基本类型的转换
std 命名空间中有 stoi
这种函数
可以将 string 转成 int
类似的函数还有 stof 和 stol
分别转换成 double 和 long
示例
string s("123.1");
double num = stod(s);
cout << "num is " << s << endl; // 123.1
string s2 = to_string(num);
cout << "s2 is " << s2 << endl; // 123.10000