C++ 标准库类型string

标准库类型 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组成的串

字符串的字面值实际上是由常量字符构成的数组(array)。编译器在每个字符串的结尾处添加一个空字符(’/0’),因此,字符串字面值的实际长度要比内容多1。

初始化分为直接初始化和拷贝初始化。使用等号(=)初始化一个变量实际上执行的是拷贝初始化(copy initialization),不使用等号执行的是直接初始化。

当初始值只有一个时,两种初始化都可以。
当初始化要用到的值有多个时,一般来说只能使用直接初始化的方式:

string s3("value")    直接初始化
string s3 = "value"   拷贝初始化
string s7(10,'c')     多个初始化要用的值,内容是cccccccccc

对于用多个值初始化,如果使用拷贝初始化,需要显式地创建一个(临时)对象用于拷贝:

string s8 = string s7(10,'c')

相比于s7的初始化没有任何补偿优势。

string 对象上的操作

一个类除了要规定初始化其对象外,还要定义对象上所能执行的操作。其中,类既能定义通过函数名调用的操作,也能定义<<,+等各种运算符在该类对象上的新含义。

//一些基操
os<<s              将s写到输出流os中,返回os
is>>s              从is中读取字符串给s,字符串以空白分隔,返回is
getline(is,s)      从is中读取一行,返回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中所含字符完全一样,则它们相等。注意到string对象的相等性判断对字母的大小写敏感
s1!=s2             
<,<=,>,>=          利用字母在字典里的顺序判断,对大小写敏感
读写string对象

可以使用IO操作符读写string对象。在执行读取操作的时候,string对象会自动忽略开头的空白(即空格符,换行符,制表符等等)并从第一个真正的字符开始读起,直到遇到下一处空白为止。

{
string s;
cin>>s;
cout<<s<<endl;
}

如果输入的是“hello world!”,输出的是"hello"没有空格。

和内置型输入输出一样,string对象也是返回运算符左侧的运算结果作为其结果,多个输入输出可以连在一起。

{
string s1,s2;
cin>>s1>>s2;
cout<<s1<<s2<<endl;
}

如果输入的是“hello world!”,输出的是"helloworld!"没有空格。

读写未知数量的string对象

Primer又给出了while()的方法

 int main()
 {
      string word;
      while(cin>>word)
         cout<<word<<endl;  //逐个输出单词。每个后面紧跟一个换行
      return 0;   
 }

实际上和前面一样,实操的时候无法跳出循环,解决方案引用如下

具体原因解释如下:
输入(cin)缓冲是行缓冲。当从键盘上输入一串字符并按回车后,这些字符会首先被送到输入缓冲区中存储。每当按下回车键后,cin 就会检测输入缓冲区中是否有了可读的数据。
cin 还会对键盘上是否有作为流结束标志的 Ctrl+Z 或者 Ctrl+D 键按下作出检查,其检查的方式有两种:阻塞式以及非阻塞式。 阻塞式检查方式指的是只有在回车键按下之后才对此前是否有 Ctrl+Z 组合键按下进行检查,非阻塞式样指的是按下 Ctrl+D 之后立即响应的方式。如果在按 Ctrl+D 之前已经从键盘输入了字符,则 Ctrl+D的作用就相当于回车,即把这些字符送到输入缓冲区供读取使用,此时Ctrl+D不再起流结束符的作用。如果按 Ctrl+D 之前没有任何键盘输入,则 Ctrl+D 就是流结束的信号。
还有一点需要知道:Ctrl+Z产生的不是一个普通的ASCII码值,也就是说它产生的不是一个字符,所以不会跟其它从键盘上输入的字符一样能够存放在输入缓冲区。。
从键盘上输入abcd^ z 加 回车之后在Windows系统上是这样处理的:由于回车的作用,前面的 abcd 等字符被送到输入缓冲区(注意:上面说过了,^z不会产生字符,所以更不会存储到输入缓冲区,缓冲区中没有 ^z 的存在)。这时,cin.get() 检测到输入缓冲区中已经有数据存在(因此不再检查是否有 ^z 的输入),于是从缓冲中读取相应的数据。如果都读取完了,则输入缓冲区重新变为空,cin.get() 等待新的输入。可见,尽管有 ^z 按下,但是由于在此之前还有其它输入字符(abcd),所以流也不会结束。
因此,输入流结束的条件就是:^z 之前不能有任何字符输入(回车除外),否则 ^z 起不到流结束的作用。

就是说,Windows系统中一般采用阻塞式检查 Ctrl+Z、Unix/Linux系统下一般采用非阻塞式的检查 Ctrl+D。如果利用非法字符结束,那么非法字符前不能有任何字符输入(回车除外)。

getline

利用getline函数读取一整行,可以在最终得到字符串中输入时的空白符。getline函数参数是一个输入流和一个string对象,函数从给定的输入流中读入内容,直到遇到换行符为止(注意到,换行符也被读入进来了),把所读内容存到那个string对象中去(注意到,不存在换行符)。如果输入一开始就是换行符,那么所得结果就是空string。

int main{
        string line;
        while(getline(cin,line))
            cout<<line<<endl;
        return 0;
    }

因为line中不含有换行符,我们手动加上换行符。注意到,出发getline返回的换行符最终并未被包含在string对象中。

empty 和 size

empty函数根据string对象是否为空返回一个对应的bool值。

while(getline(cin,line))
    if(!line.empty())
        cout<<line<<endl;

在该程序中,if语句条件部分使用了逻辑非运算符(!),它返回与运算对象相反的结果。此例中,str不为空即为真。
size函数返回str对象长度(即字符个数)

string::size_type类型

无符号整型,仅能表示大于0的值。在int等类型前加上unsigned就可以得到无符号类型,例如unsigned long。类型unsigned int可以缩写为unsigned。

size函数返回值类型为string::size_type类型,在C++11新标准中允许用auto,decltype来推断变量类型

auto len=line.size();//len的类型是string::size_type

如果一条表达式中有size()就不要再用int,以避免混用int,unsigned带来的问题。

比较string对象

依照(大小写敏感的)字典顺序:

  1. 如果两个string对象长度不同,且较短对象的每个字符都与较长字符对应位置上的字符相同,就说较短对象小于较长对象。
  2. 如果两个string对象在某些对应位置上不一致,其实两者的比较就是第一对相异字符的比较结果
string str =Hello
string phrase=Hello World
string slang=Hiya

str小于phrase,slang既大于str也大于phrase

字面值与string对象相加

因为标准库允许把字符字面值和字符串字面值转换成string对象,所以需要string的地方可以以这两种方式替代。
注意到,当把string对象和字符字面值以及字符串字面值混在一条语句中使用时,必须确保每个运算符(+)的两侧至少有一个是string:

string s1 = "hello" + "!";      //wrong
string s2 = s + "hello" + "!";  //right
string s3 = "hello" + s + "!";  //right
string s4 = "hello" + "!" + s;  //wrong

对于s2,实际上的工作机理适合连续输入连续输出是一样的,相当于string s2 =( s + "hello") + "!";其中左侧子表达式的结果是一个string对象。同样也解释了s4的错误。

处理string对象中的字符

cctype头文件中定义了一些标准库函数来处理这部分工作

isalnum(c)   当c是字母或数字时为真
islapha(c)   当c是字母时为真
iscntrl(c)   当c是控制字符时为真
isdigit(c)   当c是数字时为真
isgraph(c)   当c不是空格但可打印时为真(即c具有可视形式)
islower(c)   当c是小写字母时为真
isprint(c)   当c是可打印字符时为真(即c是空格或具有可视形式)
ispunct(c)   当c是标点符号时为真(即非控制字符、数字、字母、可打印空白)
isspace(c)   当c是空白时为真(即空格、横向制表符(\t)、纵向制表符(\v)、回车符(\r)、换行符(\n)、进纸符(\f)中的一种)
isupper(c)   当c时大写字母时为真
isxdigit(c)  当c时16进制数时为真
tolower(c)   当c为大写,输出其小写;否则原样输出c
toupper(c)   当c为小写,输出其大写;否则原样输出c
使用基于范围的for语句

这种语句遍历给定序列中的每个元素并对序列中给的每个值执行某种操作,语法:

for (declaration:expression)
    statement

其中expression部分是一个对象,用于表示一个序列。declaration部分负责定义一个变量,被用于访问序列中的基础元素。每次迭代,declaration中的变量会初始化为expression部分的下一元素值。
我们可以用for把string中的字符每行一个输出出来:

string str("some string");
for(auto c:str)
    auto <<c <<endl;

输出标点的个数:

string s("Hello World!!!");
decltype(s.size())punct_cnt = 0;
for(auto c:s)
    if(ispunct(c))
        ++punct_cnt;
cout<<punct_cunt<<endl;
//输出“3”
使用范围for改变字符串中的字符

如果想改变string对象中字符的值,必须把循环定义变量定义成引用类型。注意到,所谓引用只是给定对象的一个别名,因此当使用引用作为循环控制变量时,这个变量实际上被依此绑定到了序列的每个元素上。
ep,将字符串改写成大写字母的形式:

string s("Hello World!");
for(auto &c:s)      // 注意c是引用
    c=toupper(c);
cout<<s<<endl;

如果想要访问string对象中的单个字符有两种方式:一种是使用下标,另外一种是使用迭代器。
下标运算符([ ])接收的输入参数是string::size_type类型的值,这和参数表示访问的字符的位置;返回值是该位置上字符的引用。string对象的下标大于等于0而小于s.size()
ep,将字符串首字符改为大写形式:

string s("some string")
if(!s.empty())
    s[0]=toupper(s[0]);

ep2,把s的第一个单词改写成大写形式:

for(decltype(s.size())) index=0 ; index!=s.size() && !isspace(s[index]) ; ++index)
        s[index]=toupper(s[index]);  

注意到,for语句条件用到了逻辑与运算符(&&)。如果参与运算的两个运算对象都为真,则逻辑结果为真;否则结果为假。C++语言规定只有当左侧运算对象为真时才会检查右侧运算对象的情况。在本例中,只有在index达到s.size()之前才会执行s[index]。随着index的增加,它永远不会超过s.size()的值,可以确保index比s.size()小。
一种简便易行的方法是,总是设下标类型为string::size_type,因为此类型时无符号数,确保下标不会小于0。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值