标准库类型string

时间:2023-03-09 08:44:14
标准库类型string

定义和初始化string对象

初始化string对象方式:

string s1;//默认初始化,s1是一个字符串
string s2(s1);//s2是s1的副本
string s2 = s1;//等价于s2(s1),s2是s1的副本
string s3("value");//s3是字面值"value"的副本,除了字面值最后的那个空字符外
string s4(n, 'c');//把s4初始化为由连续n个字符串c组成的串
  • 拷贝初始化:使用等号(=)初始化一个变量,编译器把等号右侧的初始值拷贝到新建的对象中去。
  • 直接初始化:不使用等号

当初始值只有一个时,使用直接初始化或拷贝初始化都行。如果初始化要用到的值有多个,一般只能使用直接初始化的方式:

string s5 = "hiya";//拷贝初始化
string s6("hiya");//直接初始化
string s7(, 'c');//直接初始化

用拷贝初始化的方式对于多个值进行初始化,需要显式地创建一个(临时)对象用于拷贝:

string s8 = string(, 'c');//拷贝初始化

这条语句等价于:

string temp(,'c');
string s8 = temp;//将temp拷贝给s8

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中所含的字符完全一样,则它们相等;string对象的相等性对字幕的大小写敏感
s1!=s2 等性判断对字母的大小写敏感
<,<=,>,>= 利用字符在字典中的顺序进行比较,且对字母的大小写敏感

读写string对象:

int main()
{
string s;//空字符串
cin >> s;//将string对象读入s,遇到空白停止
cout << s << endl;//输入s
return ;
}

在执行读取操作时,string对象会自动忽略开头的空白(即空格符、换行符、制表符等)并从第一个真正的字符开始读起,直到遇见下一个空格。

如果程序的输入是“  Hello World!  ”,则输出将是“Hello”,输出结果中没有任何空格。

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

string s1, s2;
cin >> s1 >> s2;//把第一个输入读到s1中,第二个输入读到s2中
cout << s1 << s2 << endl;//输出两个string对象

输出将是“HelloWorld!”。


读取未知数量的string对象:

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

读取的对象是string而非int,如果流有效,也就是没有遇到文件结束标记或非法输入,那么执行while语句内部的操作。


使用getline读取一整行

getline函数的参数是一个输入流和一个string对象,函数从给定的输入流中读入内容,直到遇到换行符位置(注意换行符也被读进来了),然后把所读的内容存入到那个string对象中去(注意不存换行符)。

和输入运算符一样,getline也会返回它的流参数:

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

tip:触发getline函数返回的那个换行符实际上被丢弃掉了,得到的string对象中并不包含换行符。


string的empty和size操作

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

改写之前的程序,只输出非空的行:

//每次读入一整行,遇到空格直接跳过
while (getline(cin, line))
{
if (!line.empty())
cout << line << endl;
}
  • 逻辑非运算发:!,返回与其运算对象相反的结果。

size函数返回string对象的长度:

//每次读入一整行,输出其中超过80个字符的行
while (getline(cin, line))
{
if (line.size()>)
cout << line << endl;
}

string::size_type类型

size函数返回值的类型为string::size_type:

  • 通过作用域操作符来表明size_type是在类string中定义的。
  • string::size_type是一个无符号类型的值且能足够放下任何string对象的大小。
  • 通过auto或decltype来推断变量的类型:
auto len = line.size();//len的类型是string::size_type

tip:如果一条表达式中已经有了size()函数就不要再使用int了,避免混用int和unsigned可能带来的问题。


比较string对象

  • 相等性运算符:==和!=分别检验两个string对象相等或不相等,string对象相等意味着长度相同且所包含的字符也全部相同。
  • 关系性运算符:<、<=、>、>=分别检验一个string对象是否小于、小于等于、大于、大于等于另一个string对象。

上述运算都依照字典顺序:

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

string对象比较的示例:

string str = "Hello";
string phrase = "Hello World";
string slang = "Hiya";
  1. 对象str小于对象phrase
  2. 对象slang既大于phrase也大于str

为string对象赋值

string类允许把一个对象的值赋给另一个对象:

string st1(, 'c'),st2;//st1的内容是10个c,st2的内容是一个空字符串
st1 = st2;//赋值:用st2的副本替换st1的内容
//此时st1和st2都是空字符串

两个string对象相加

两个string对象相加得到一个新的string对象,其内容是把左侧的运算对象与右侧的运算对象串接而成:

string s1 = "hello, ", s2 = "world\n";
string s3 = s1 + s2;//s3的内容是hello, world\n
s1 += s2;//等价于s1=s1+s2;

字面值和string对象相加

标准库允许把字符字面值和字符串字面值转换成string对象:

string s1 = "hello", s2 = "world";//在s1和s2中没有标点符号
string s3 = s1 + ", " + s2 + '\n';

string对象和字符字面值及字符串字面值混在一条语句,每个加法运算符的两侧的运算对象至少有一个是string:

string s4 = s1 + ", ";//正确:把一个string对象和一个字面值相加
string s5 = "hello" + ", ";//错误:两个运算对象都不是string
string s6 = s1 + "," + "world";//正确:每个加法运算符都有一个运算对象是string
string s7 = "hello" + ", " + s2;//错误:不能把字面值直接相加

s6的工作机理和连续输入和连续输出是一样的:

string tmp = s1 + ", ";//正确:加法运算符有一个运算对象是string
s6 = temp + "world";//正确:加法运算符有一个运算对象是string

s7初始化是非法的,根据其语义加上括号的后就成了下面的形式:

string s7= ("hello" + ", " )+ s2;//错误:不能把字面值直接相加

tip:字符串字面值与string是不同类型。


处理string对象中的字符

cctype头文件中的函数:

isalnum(c) 当c是字母或数字时为真
isalpha(c) 当c是字母时为真
iscntrl(c) 当c是控制字符时为真
isdigit(c) 当c是数字时为真
isgraph(c) 当c不是空格但可打印时为真
islower(c) 当c是小写字母时为真
isprint(c) 当c是可以打印字符时为真
ispunct(c) 当c是标点符号时为真
isspace(c) 当c是空白时为真
isupper(c) 当c是大写字母时为真
isxdigit(c) 当c是十六进制数字时为真
tolower(c) 如果c是大写字母,输出对应的小写字母;否则原样输出c
toupper(c) 如果c是小写字母,输出对应的大写字母;否则原样输出c

处理每个字符?使用基于范围的for语句

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

for(declaration:expression)
statement

一个string对象表示一个字符的序列,因此string对象可以作为范围for语句中的expression:

string str("some string");
for (auto c : str)//对应str中的每个字符
cout << c << endl;//输出当前字符,后面紧跟一个换行符

使用范围for语句和ispunct函数统计string对象中标点符号的个数:

string s("Hello World!!!");
decltype(s.size()) punct_cnt = ;//punct_cnt的类型和s.size的返回类型一样
for (auto c : s)//对于s中的每个字符
if (ispunct(c))//如果字符是标点符号
++punct_cnt;//将标点符号的计数值加1
cout << punct_cnt
<< " punctuation characters in " << s << endl;

输出结果:

 punctuation characters in Hello World!!!

使用范围for语句改变字符串中的字符

把整个string对象转换成大写,只要对其中得每个字符调用toupper函数并将结果再赋给原字符:

string s("Hello World!!!");
for (auto& c : s)//对于s中的每个字符(注意:c是引用)
c = toupper(c);//c是一个引用,因此赋值语句将改变s中字符的值
cout << s << endl;

输出结果:

Hello WORLD!!!

只处理一部分?

访问string对象中的单个字符有两种方式:

  • 下标
  • 迭代器

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

tip:string对象的下表必须大于等于0而小于s.size()

使用下标运算符输出string对象中的第一个字符:

if (!s.empty())//确保确实有字符需要输出
cout << s[] << endl;//输出s的第一个字符

将字符串的首字符改成了大写形式:

string s("some string");
if (!s.empty())//确保s[0]的位置确实有字符
s[] = toupper(s[]);//为s的第一个字符赋一个新值

使用下标执行迭代

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

//依次处理s中的字符直至我们处理完全部字符或遇到一个空白
for (decltype(s.size()) index = ;index != s.size() && !isspace(s[index]);++index)
s[index]=toupper(s[index]);//将当前字符改成大写形式

使用下标执行随机访问

0到15之间的十进制转换成对应的十六进制形式:

const string hexdigits = "0123456789ABCDEF";
string result;//用于保存十六进制的字符串
string::size_type n;//用于保存从输入流读取的数
while (cin >> n)
if (n < hexdigits.size())//忽略无效输入
result += hexdigits[n];//得到对应的十六进制数字