这里是关于c++标准库string的初学笔记
- 一种访问库中名字的简单方法——using声明
在之前,当从std库中调用库函数时,必须使用作用域操作符(::),其作用是:编译器应从操作符左侧的名字所示的作用域中寻找右侧这个名字。如,std::cin的意思是使用std空间中的名字cin。这显然显得比较繁琐。
有了using声明,便无需专门的前缀(如命名空间::)也可以使用所需的名字了,using声明具有一下形式:
using namesapace::name;
一旦声明了上述语句,就可以直接访问命名空间的名字了:
#include<iostream>
//using声明,当我们使用cin名字时,从命名空间std中获取它
using std::cin;
int main()
{
int i;
cin >> i; //正确:cin和std::cin含义相同;
cout << i; //错误:未using声明,必须使用完整的名字std::cout;
return 0;
}
从上面的例子可以看出,每个名字都需要独立的using声明
- 标准库类型string
标准库string表示可变长的字符序列,使用string类型必须首先包含string头文件。string定义在std空间中。
#include<iostream>
#include<string>
using std::string;
- 初始化string对象的方式
string s1; //默认初始化,s1是一个空串;
string s2(s1); //s2是s1的副本
string s2 = s1; //同上
string s3 ("value") //s3是字面值"value"的副本,除了字面值最后的空字符外
string s3 = value; //同上
string s4 = (n,'c') //把s4初始化为连续n个字符c组成的串
- 拷贝初始化和直接初始化
表面来说,使用等号就是拷贝初始化,将等号右侧初始值拷贝到新创建的对象上。不使用等号则是直接初始化,如s4。
string对象上的操作
os<<s;//将s写到输出流os当中,返回os
is>>s;//从is中读取字符串赋给s,字符串以空白分隔,返回is
getline(is,s);//从is中读取出一行赋给s,返回is
s.empty();//判断s是否为空
s.size();//返回s中字符的个数
s[n];//返回s中第n个字符的引用,位置n从0计起
s1+s2;//返回s1+s2连接的结果
s1=s2;//把s2的值赋给s1
s1==s2;//判断s1和s2中所含的字符是否完全一样,string对象对字母的大小写敏感
s1!=s2;//判断s1和s2中所含字符是否不同
<,<=,>,>= //利用字符在字典中的顺序进行比较,且对字母的大小写敏感
可以使用IO操作读写string对象:
//适当的#include和using省略
int main
{
string s; //空字符串
cin >> s;
cout << s << endl;
return 0;
}
string对象会自动忽略开头的空白(空格符,换行符,制表符)并从真正的第一个字符开始读起,直到遇见下一个空白为止。
如上所述,若输入" Hello World! “,则将输出"Hello”,输出结果没有任何空格。
可以多个输入或者多个输出连写在一起:
string s1,s2;
cin >> s1 >> s2;
cout << s1 << s2 << endl;
输出结果"HelloWorld!"
- 读取未知数量的string对象
可以编写一个程序来读取未知数量的string对象:
int main()
{
string word;
while(cin >> word) //反复读取,直至到达文件末尾
cout << word <<endl;
return 0;
}
- 使用getline读取一整行
有时候我们希望保留输入时的空白符,这时应该用getline函数代替原来的>>运算符。getline函数的参数是一个输入流和一个string对象,函数从给定的输入流中读入内容,直到遇到换行符为止(换行符也被读取进来了),然后把所读的内容存入到那个string中去(不存换行符)。getline只要一遇到换行符就结束读取的操作并且返回结果,若一开始就是换行符则所得结果是一个空串。
int main()
{
string line;
while(getling(cin,line))
cout << line <<endl; //因为line中不包含换行符,我们手动加上换行符
return 0;
}
- string的empty和size操作
empty根据string对象是否为空范围一个对应的布尔值,使用点操作符指明是哪个对象执行了empty函数即可调用函数。
int main()
{
//每次读入一行,遇到空行则直接跳过
string line;
while(getling(cin,line))
if(!line.empty())
cout << line <<endl; //因为line中不包含换行符,我们手动加上换行符
return 0;
}
size与empty相似
int main()
{
//每次读入一行,输出其中超过80字符的行
string line;
while(getling(cin,line))
if(line.size()>80)
cout << line <<endl; //因为line中不包含换行符,我们手动加上换行符
return 0;
}
string::size_type类型
从逻辑上来讲,size()成员函数似乎应该返回整型数值,或如2.2节“建议”中所述的无符号整数。但事实上,size操作返回的是string::size_type类型的值。我们需要对这种类型做一些解释。
string类类型和许多其他库类型都定义了一些伙伴类型(companion types)。这些伙伴类型使得库类型的使用是机器无关的(machine-independent)。size_type就是这些伙伴类型中的一种。它定义为与unsigned型(unsigned int或unsigned long)具有相同的含义,而且可以保证足够大可存储任意string对象的长度。为了使用由string类型定义的size_type类型,程序员必须加上作用域操作符来说明所使用的size_type类型是由string类定义的。
任何存储string的size操作结果的变量必须为string::size_type类型。特别重要的是,不要把size的返回值赋给一个int变量。
虽然我们不知道string::size_type的确切类型,但可以知道它是unsigned型(2.1.1节)。对于任意一种给定的数据类型,它的unsigned型所能表示的最大正数值比对应的signed要大一倍。这个事实表明size_type存储的string长度是int所能存储的两倍。
使用int变量的另一个问题是,有些机器上int变量的表示范围太小,甚至无法存储实际并不长的string对象。如在有16位int型的机器上,int类型变量最大只能表示32767个字符的string对象。而能容纳一个文件内容的string对象轻易就会超过这个数字。因此,为了避免溢出,保存一个string对象size的最安全的方法就是使用标准库类型string:: size_type。
比较string对象
1.如果两个string对象长度不同,而且较短的string对象的每个字符都与较长的string对象对应位置上的字符相同,则较短的对象小于较长的对象。
2.如果两个string对象在某些对应位置上不一致,则string对象比较的结果是第一对相异字符比较的结果。
如:
①string str = “Hello”;
②string phrase = “Hello World”;
③string slang = “Hiya”;
则③>②>①
为string对象赋值
对于string类而言,允许把一个对象值赋给另一个对象。
st1 = st2 //将s2的值赋给s1
两个string对象相加
两个string对象相加,结果是把左侧运算对象与右侧的运算对象串接而成。
string s1 = "Hello,",s2 = "world\n";
string s3 = s1 + s2; //s3的内容是"Hello,world\n"
s1 += s2; //等价于s1 = s1 + s2
字面值和string对象相加
注意一点,C++语言中字符串字面值并不是标准库类型string对象,字符串字面值与string是不同的类型。
当把string对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符(+)的两侧的运算对象至少有一个是string:
string s1 = "Hello",s2 = "world";
string s3 = s1 + "," + s2 + "\n"; //正确
string s4 = s1 + ","; //正确
string s5 = "hello" + ","; //错误,两个运算对象都不是string
string s6 = s1 + "," + "world" //正确,从左往右运算,等价于(s1 + ",") + "world"
string s7 = "hello" + "," + s2; //错误,前两个运算对象都不是string
处理string对象中的字符
cctype头文件中的函数可以用于处理某个特定的字符:
isalnum() 如果参数是字母数字,即字母或者数字,函数返回true
isalpha() 如果参数是字母,函数返回true
iscntrl() 如果参数是控制字符,函数返回true
isdigit() 如果参数是数字(0-9),函数返回true
isgraph() 如果参数是除空格之外的打印字符,函数返回true
islower() 如果参数是小写字母,函数返回true
isprint() 如果参数是打印字符(包括空格),函数返回true
ispunct() 如果参数是标点符号,函数返回true
isspace() 如果参数是标准空白字符,如空格、换行符、水平或垂直制表符,函数返回true
isupper() 如果参数是大写字母,函数返回true
isxdigit() 如果参数是十六进制数字,即0-9、a-f、A-F,函数返回true
tolower() 如果参数是大写字符,返回其小写,否则返回该参数
toupper() 如果参数是小写字符,返回其大写,否则返回该参数
但如果要处理每个字符呢?可以使用基于范围的for语句。
这种语句可以遍历给定序列中的每个元素并对序列中的每个值执行某种操作,其语法是:
for(declaration:expression)
statement
其中,expression部分是一个对象,表示一个序列。declaration部分负责定义一个变量用于访问序列中的基础元素。每次迭代,declaration部分的变量会被初始化为expression部分的下一个元素值。
如统计string对象中标点符号的个数:
string s("Hello,world!!")
decltype(s.size()) punct_cnt = 0; //使punct_cnt类型和s.size()相同
for(auto c : s) //让编译器决定c的类型
if(ispunct(c)) //如果该字符是标点符号
++punct_cnt; //标点符号总数加1
cout << punct_cnt << "punctuation characters in" << s <<endl;
改变字符串中的字符(巧妙使用引用),将字符串改为大写:
string s("Hello World!!");
for(auto &c : s) //注意这里c是s的引用
c = toupper(c); //c的改变会引起s的改变
cout << s <<endl;
我们也可以使用下标运算符([ ])对某个字符进行处理,类似于C语言中的处理。这里就暂时不多写了。