热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

《C++Primer》第9章9.5节习题答案

《CPrimer》第9章顺序容器9.5节习题答案额外的string操作练习9.41:编写程序,从一个vector初始化一个string。

《C++ Primer》第9章 顺序容器

9.5节习题答案 额外的string操作

练习9.41:编写程序,从一个vector初始化一个string。

【出题思路】

本题练习从字符数组初始化string。

【解答】

vector提供了data成员函数,返回其内存空间的首地址。将此返回值作为string的构造函数的第一个参数,将vector的size返回值作为第二个参数,即可获取vector中的数据,将其看作一个字符数组来初始化string。

#include
#include
#include using namespace std;int main()
{vector vec &#61; {&#39;H&#39;, &#39;e&#39;, &#39;l&#39;,&#39;l&#39;,&#39;o&#39;};string str1(vec.begin(), vec.end());string str2(vec.data(), vec.size());cout <<"str1&#61;&#61;&#61;" <}

运行结果&#xff1a;

 练习9.42&#xff1a;假定你希望每次读取一个字符存入一个string中&#xff0c;而且知道最少需要读取100个字符&#xff0c;你应该如何提高程序的性能&#xff1f;

【出题思路】

本题练习高效地处理动态增长的string。

【解答】

由于知道至少读取100个字符&#xff0c;因此可以用reserve先为string分配100个字符的空间&#xff0c;然后逐个读取字符&#xff0c;用push_back添加到string末尾。

#include
#include
#include using namespace std;void input_string(string &str)
{str.reserve(100);char c;while (cin >> c) {str.push_back(c);}
}int main()
{string s;input_string(s);cout <<"s&#61;&#61;&#61;" <}

运行结果&#xff1a;

 练习9.43&#xff1a;编写函数&#xff0c;接受三个string参数s、oldVal和newVal。使用迭代器及insert和erase函数将s中所有oldVal替换为newVal。测试你的程序&#xff0c;用它替换通用的简写形式&#xff0c;如&#xff0c;将“tho”替换为“though”&#xff0c;将“thru”替换为“through”。

【出题思路】

本题练习较为复杂的string操作。

【解答】

由于要求使用迭代器&#xff0c;因此使用如下算法&#xff1a;

1&#xff0e;用迭代器iter遍历字符串s。注意&#xff0c;对于s末尾少于oldVal长度的部分&#xff0c;已不可能与oldVal相等&#xff0c;因此无须检查。

2&#xff0e;对每个位置&#xff0c;用一个循环检查s中字符是否与oldVal中的字符都相等。

3&#xff0e;若循环是因为iter2 &#61;&#61; oldVal.end而退出&#xff0c;表明s中iter开始的子串与oldVal相等。则调用erase将此子串删除&#xff0c;接着用一个循环将newVal复制到当前位置&#xff08;tdm-gcc 4.8.1中&#xff0c;返回迭代器的insert只支持单个字符插入&#xff09;。由于insert将新字符插入到当前位置之前&#xff0c;并返回指向新字符的迭代器&#xff0c;因此&#xff0c;逆序插入newVal字符即可。最后将iter移动到新插入内容之后&#xff0c;继续遍历s。

4&#xff0e;否则&#xff0c;iter开始的子串与oldVal不等&#xff0c;递增iter&#xff0c;继续遍历s。

#include
#include
#include using namespace std;void replace_string(string &s, const string &oldVal, const string &newVal)
{auto l &#61; oldVal.size();if(!l) //要查找的字符串为空return;auto iter &#61; s.begin();while(iter <&#61; s.end() - 1) //末尾少于oldVal长度的部分无须检查{auto iter1 &#61; iter;auto iter2 &#61; oldVal.begin();//s中iter开始的子串必须每个字符都与oldVal牙相同while(iter2 !&#61; oldVal.end() && *iter1 &#61;&#61; *iter2){iter1&#43;&#43;;iter2&#43;&#43;;}if(iter2 &#61;&#61; oldVal.end())//oldVal耗尽————字符串相等{iter &#61; s.erase(iter, iter1);//删除s中与oldVal相等部分if(newVal.size())//替换子串是否为空{iter2 &#61; newVal.end();//由后至前逐个插入newVal中的字符do{iter2--;iter &#61; s.insert(iter, *iter2);}while(iter2 > newVal.begin());}iter &#43;&#61; newVal.size();//迭代器移动到新插入内容之后}else{iter&#43;&#43;;}}
}int main()
{string s &#61; "tho thru tho!";replace_string(s, "thru", "through");cout <}

运行结果&#xff1a;

 练习9.44&#xff1a;重写上一题的函数&#xff0c;这次使用一个下标和replace。

【出题思路】

本题练习使用标准库提供的特性更简单地实现string操作。

【解答】

由于可以使用下标和replace&#xff0c;因此可以更为简单地实现上一题的目标。通过find成员函数&#xff08;只支持下标参数&#xff09;即可找到s中与oldVal相同的子串&#xff0c;接着用replace即可将找到的子串替换为新内容。可以看到&#xff0c;使用下标而不是迭代器&#xff0c;通常可以更简单地实现字符串操作。

#include
#include
#include using namespace std;void replace_string(string &s, const string &oldVal, const string &newVal)
{unsigned long p &#61; 0;while((p &#61; s.find(oldVal, p)) !&#61; string::npos) //在s中查找oldVal{s.replace(p, oldVal.size(), newVal); //将找到的子串替换为newVal的内容p &#43;&#61; newVal.size(); //下标调整到新插入的内容之后}
}int main()
{string s &#61; "tho thru tho!";replace_string(s, "thru", "through");cout <}

运行结果&#xff1a;

 练习9.45&#xff1a;编写函数&#xff0c;接受一个表示名字的string参数和两个分别表示前缀&#xff08;如“Mr.”或“Ms.”&#xff09;和后缀&#xff08;如“Jr.”或“III”&#xff09;的字符串。使用迭代器及insert和append函数将前缀和后缀添加到给定的名字中&#xff0c;将生成的新string返回。

【出题思路】

本题练习string的追加操作。

【解答】

通过insert插入到首位置之前&#xff0c;即可实现前缀插入。通过append即可实现将后缀追加到字符串末尾。

#include
#include
#include using namespace std;void name_string(string &name, const string &prefix, const string &suffix)
{name.insert(name.begin(), 1, &#39; &#39;);name.insert(name.begin(), prefix.begin(), prefix.end());//输入前缀name.append(" ");name.append(suffix.begin(), suffix.end());
}int main()
{string s &#61; "James Bond";name_string(s, "Mr.", "II");cout <}

运行结果&#xff1a;

 练习9.46&#xff1a;重写上一题的函数&#xff0c;这次使用位置和长度来管理string&#xff0c;并只使用insert。

【出题思路】

本题继续练习基于位置的string操作。

【解答】

使用insert&#xff0c;0等价于begin()&#xff0c;都是在当前首字符之前插入新字符串&#xff1b;size()等价于end()&#xff0c;都是在末尾追加新字符串。

#include
#include
#include using namespace std;void name_string(string &name, const string &prefix, const string &suffix)
{name.insert(0, " ");name.insert(0, prefix);//输入前缀name.insert(name.size(), " ");name.insert(name.size(), suffix); //插入后缀
}int main()
{string s &#61; "James Bond";name_string(s, "Mr.", "II");cout <}

运行结果&#xff1a;

 练习9.47&#xff1a;编写程序&#xff0c;首先查找string &#xff02;ab2c3d7R4E6&#xff02;中的每个数字字符&#xff0c;然后查找其中每个字母字符。编写两个版本的程序&#xff0c;第一个要使用find_first_of&#xff0c;第二个要使用find_first_not_of。【出题思路】

本题练习string的搜索操作的基本用法。

【解答】

find_first_of在字符串中查找给定字符集合中任一字符首次出现的位置。若查找数字字符&#xff0c;则“给定字符集合”应包含所有10个数字&#xff1b;若查找字母&#xff0c;则要包含所有大小写字母——abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOQRSTUVWXYZ。

#include
#include using namespace std;void find_char(string &s, const string &chars)
{cout <<"在" <}int main()
{string s &#61; "ab2c3d7R4E6";cout <<"查找所有数字" <}

运行结果&#xff1a;

 find_first_not_of查找第一个不在给定字符集合中出现的字符&#xff0c;若用它查找某类字符首次出现的位置&#xff0c;则应使用补集。若查找数字字符&#xff0c;则“给定字符集合”应包含10个数字之外的所有字符——abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ&#xff1b;若查找字母&#xff0c;则要包含所有非字母字符。注意&#xff0c;这一设定仅对此问题要查找的字符串有效——它只包含字母和数字。因此&#xff0c;字母和数字互为补集。若字符串包含任意ASCII字符&#xff0c;可以想见&#xff0c;正确的“补集”可能非常冗长。

#include
#include using namespace std;void find_char(string &s, const string &chars)
{cout <<"在" <}int main()
{string s &#61; "ab2c3d7R4E6";cout <<"查找所有数字" <}

运行结果&#xff1a;

 练习9.48&#xff1a;假定name和numbers的定义如325页所示&#xff0c;numbers.find(name)返回什么&#xff1f;

【出题思路】

理解find与find_first_of、find_first_not_of的区别。

【解答】

s.find(args)查找s中args第一次出现的位置&#xff0c;即第一个与args匹配的字符串的位置。args是作为一个字符串整体在s中查找&#xff0c;而非一个字符集合在s中查找其中字符。因此&#xff0c;对325页给定的name和numbers值&#xff0c;在numbers中不存在与name匹配的字符串&#xff0c;find会返回npos。

练习9.49&#xff1a;如果一个字母延伸到中线之上&#xff0c;如d或f&#xff0c;则称其有上出头部分&#xff08;ascender&#xff09;。如果一个字母延伸到中线之下&#xff0c;如p或g&#xff0c;则称其有下出头部分&#xff08;descender&#xff09;。编写程序&#xff0c;读入一个单词文件&#xff0c;输出最长的既不包含上出头部分&#xff0c;也不包含下出头部分的单词。

【出题思路】

本题练习用搜索操作做一些更复杂的事情。

【解答】

查找既不包含上出头字母&#xff0c;也不包含下出头字母的单词&#xff0c;等价于“排除包含上出头字母或下出头字母的单词”。因此&#xff0c;用find_first_of在单词中查找上出头字母或下出头字母是否出现。若出现&#xff08;返回一个合法位置&#xff0c;而非npos&#xff09;&#xff0c;则丢弃此单词&#xff0c;继续检查下一个单词。否则&#xff0c;表明单词符合要求&#xff0c;检查它是否比之前的最长合法单词更长&#xff0c;若是&#xff0c;记录其长度和内容。文件读取完毕后&#xff0c;输出最长的合乎要求的单词。

#include
#include
#include using namespace std;void find_longest_word(ifstream &in)
{string s;string longest_word;unsigned long max_length &#61; 0;while(in >> s){if(s.find_first_of("bdfghjklpqty") !&#61; string::npos)continue; //包含上出头或下出头字母cout <}int main(int argc, char *argv[])
{ifstream in(argv[1]);if(!in){cerr <<"无法打开输入文件" <}

命令行参数设置

运行结果&#xff1a;

 练习9.50&#xff1a;编写程序处理一个vector&#xff0c;其元素都表示整型值。计算vector中所有元素之和。修改程序&#xff0c;使之计算表示浮点值的string之和。

【出题思路】

本题练习简单的字符串到数值的类型转换&#xff0c;这在开发实际应用程序时是非常常见的操作&#xff0c;是很有用的基本编程技巧。

【解答】

标准库提供了将字符串转换为整型函数stoi。如果希望转换为不同整型类型&#xff0c;如长整型、无符号整型等&#xff0c;标准库也都提供了相应的版本。

#include
#include
#include using namespace std;int main()
{vector vs &#61; {"123", "&#43;456", "-789"};int sum &#61; 0;for(auto iter &#61; vs.begin(); iter !&#61; vs.end(); &#43;&#43;iter)sum &#43;&#61; stoi(*iter);cout <<"和&#xff1a;" <}

运行结果&#xff1a;

 标准库也提供了将字符串转换为浮点数的函数&#xff0c;其中stof是转换为单精度浮点数。简单修改上面的程序即可实现本题的第二问。注意&#xff0c;当给定的字符串不能转换为数值时&#xff08;不是所需类型数值的合法表示&#xff09;&#xff0c;这些转换函数会抛出invalid_argument异常&#xff1b;如果表示的值超出类型所能表达的范围&#xff0c;则抛出一个out_of_range异常。这两个程序均未捕获、处理这两个异常&#xff0c;读者可尝试编写捕获并处理异常的版本&#xff0c;并用不合要求的字符串进行测试。

#include
#include
#include using namespace std;int main()
{vector vs &#61; {"12.3", "-4.56", "&#43;7.8e-2"};int sum &#61; 0;for(auto iter &#61; vs.begin(); iter !&#61; vs.end(); &#43;&#43;iter)sum &#43;&#61; stof(*iter);cout <<"和&#xff1a;" <}

 运行结果&#xff1a;

练习9.51&#xff1a;设计一个类&#xff0c;它有三个unsigned成员&#xff0c;分别表示年、月和日。为其编写构造函数&#xff0c;接受一个表示日期的string参数。你的构造函数应该能处理不同数据格式&#xff0c;如January 1,1900、1/1/1900、Jan 1 1900等。

【出题思路】

本题看似简单&#xff0c;但实际上较为复杂。在实际应用程序开发中&#xff0c;编写从文本中提取格式数据的程序片段&#xff0c;是非常烦琐、很容易出错的工作。因为这部分程序不能只会解析格式正确的数据&#xff0c;还应检查格式错误&#xff0c;给出错误信息。

【解答】

在头文件中定义了date类。构造函数date(string &ds)从字符串中解析出年、月、日的值&#xff0c;大致步骤如下&#xff1a;

1&#xff0e;若首字符是数字&#xff0c;则为格式2&#xff0c;用stoi提取月份值&#xff0c;若月份值不合法&#xff0c;抛出异常&#xff0c;否则转到步骤6。

2&#xff0e;若首字符不是数字&#xff0c;表明是格式1或3&#xff0c;首先提取月份值。

3&#xff0e;将ds开始的子串与月份简称进行比较&#xff0c;若均不等&#xff0c;抛出异常&#xff08;若与简称不等&#xff0c;则不可能与全称相等&#xff09;。

4&#xff0e;若与第i个月简称相等&#xff0c;且下一个字符是合法间隔符&#xff0c;返回月份值。

5&#xff0e;否则&#xff0c;检查接下来的子串是否与全称剩余部分相等&#xff0c;若不等&#xff0c;抛出异常&#xff1b;否则&#xff0c;返回月份值。

6&#xff0e;用stoi提取日期值和年份值&#xff0c;如需要&#xff0c;检查间隔符合法性。

读者需要特别注意的是&#xff0c;在解析过程中&#xff0c;如何调整偏移量p。

此程序已经较为复杂&#xff0c;但显然离“完美”还差很远&#xff0c;只能解析3种格式&#xff0c;且进行了很多简化。程序中已经给出了几种格式错误&#xff0c;读者可尝试构造其他可能的格式错误。并尝试补充程序&#xff0c;支持其他格式&#xff0c;如“2006年7月12日”。此外&#xff0c;程序中也涉及类、异常等相关的编程知识&#xff0c;读者可自行分析。头文件date.h如下所示&#xff1a;

#ifndef PROGRAM09_51_H
#define PROGRAM09_51_H#include
#include
#include using namespace std;class date
{
public:friend ostream& operator <<(ostream&, const date&);date() &#61; default;date(string &ds);unsigned y() const { return year; }unsigned m() const { return month; }unsigned d() const { return day; }private:unsigned year, month, day;
};//月份全称
const string month_name[] &#61; {"January", "February", "March", "April", "May", "June","July", "August", "September", "October", "November", "December"};//月份简写
const string month_abbr[] &#61; {"Jan", "Feb", "Mar", "Apr", "May", "Jun","Jul", "Aug", "Sept", "Oct", "Nov", "Dec"};//每月天数
const int days[] &#61; {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};inline int get_month(string &ds, int &end_of_month)
{int i, j;for(i &#61; 0; i <12; &#43;&#43;i){//检查每个字符是否与月份简写相等for(j &#61; 0; j }inline int get_day(string &ds, int month, int &p)
{size_t q;int day &#61; stoi(ds.substr(p), &q);//从p开始的部分转换为日期值if(day <1 || day > days[month])throw invalid_argument("不是合法日期值");p &#43;&#61; q;//移动到日期值之后return day;
}inline int get_year(string &ds, int &p)
{size_t q;int year &#61; stoi(ds.substr(p), &q);//从p开始的部分转换为年if(p &#43; q }date::date(string &ds)
{int p;size_t q;if((p &#61; ds.find_first_of("0123456789")) &#61;&#61; string::npos)throw invalid_argument("没有数字&#xff0c;非法日期");if(p > 0)//月份名格式{month &#61; get_month(ds, p);day &#61; get_day(ds, month, p);if(ds[p] !&#61; &#39; &#39; && ds[p] !&#61; &#39;,&#39;)throw invalid_argument("非法间隔符");p&#43;&#43;;year &#61; get_year(ds, p);}else//月份值格式{month &#61; stoi(ds, &q);p &#61; q;if(month <1 || month > 12)throw invalid_argument("不是合法月份值");if(ds[p&#43;&#43;] !&#61; &#39;/&#39;)throw invalid_argument("非法间隔符");day &#61; get_day(ds, month, p);if(ds[p&#43;&#43;] !&#61; &#39;/&#39;)throw invalid_argument("非法间隔符");year &#61; get_year(ds, p);}
}ostream & operator <<(ostream& out, const date& d)
{out <}#endif /* PROGRAM09_51_H */

主程序&#xff1a;

#include
#include "program09_51.h"using namespace std;int main()
{string dates[] &#61; {"Jan 1,2014", "February 1 2014", "3/1/2014",//"Jcn 1,2014",//"Janvary 1,2014",//"Jan 32,2014",//"Jan 1/2014","3 1 2014"};try{for(auto ds: dates){date dl(ds);cout <}

运行结果&#xff1a;

 





推荐阅读
author-avatar
V陈冬梅_717
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有