0. 引入
作为一名OIer,相信屏幕前的你在入门的时候接触过这样一个头文件,叫做输入输出流头文件iostream
现在我们来看看它的结构:
* io:字母i代表input输入,字母o代表output输出
* stream:溪流,顾名思义,用到的符号是<<和>>,仿佛在表示溪流流动的方向
这便是为何iostream叫做输入输出流的原因。
那么,我们可以提出一个问题:
> 很明显,io偏向于功能性介绍。在C++中,还有一个头文件叫做fstream是文件读写流,也是以stream结尾。那么,是否还存在其它以stream为后缀的头文件,且用到<<和>>这两个符号呢?
当然有!它就是我们今天的主角——字符串流sstream
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1. 什么是SSTREAM
sstream在上文已经提到了,是字符串流。我们可以进行拆分:
* s代表字符串string的缩写
* stream说明其用到符号<<和>>
注意,sstream是头文件的名字,不是类型名字!!!
sstream是头文件,它的类型名称叫做stringstream。这就好比,fstream是头文件,但类型是ifstream和ofstream一样。
不难发现,stringstream由string和stream合成而来,因此,取各部分首字母s和s,拼在一起,变成ss,就是一般情况下用到stringstream类型时我们起的变量名。
代码:
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2. 字符串流的用途
字符串流,在平时我们接触的比较少,甚至于有很多人闻所未闻(至少在进入这个帖子之前是这样)。作者曾经碰到过一个老师,他就没有用过字符串流,貌似还是听作者介绍的它的功能。从这个例子中不难看出它是一个较为冷门的东西,但是在有些场合下还是可以用到的。
作者也是从一本书上学到的这个东西。个人曾经在欢乐赛中用到过,觉得部分功能对新手还是很友好的,也很快就能够入门。在此,先向各位介绍一下它的几个用途,并发表一下个人观点。大家可以以下面的用途为本,以我的个人看法为辅,决定是否“入手”字符串流。
2.1. 用途1:类型转换
绝对有人在代码里犯过这样的错!
或者在Python中类似的情况如下:
这时,Python中解决方案是加上str()函数,C++中的方案则是带上to_string()函数。
当然,除此之外,在C++中,我们还可以用其他方式完成这个目的。例如,我们亲爱的字符串流就可以把数据转换成字符串。
【例1】把整数123转换为字符串类型,并输出在“欢迎”和“加入团队”两者之间。
这就是字符串流的第一个操作:将其他类型的变量转换为字符串。
> Note:
> 不只是int类型变量可以,long long、float、double等变量都可以通过字符串流转成字符串
不过,如果你以为这就是它的极限,那我也不会放在这里了。它神就神在还能够将字符串转成对应的整数(注意不是哈希!)
【例2】把字符串"123"转换为整数类型,并输出加上456后的结果。
这是【例1】中展示的操作的逆向操作。这是真真实实把字符串"123"变成了整型123!
> Note:
> 不只是int类型变量可以,long long、float、double等变量都可以通过字符串流从字符串转换而来
作者这里只不过展示一个用法。具体场景还是看题目。
> 作者锐评:
> 如果数据转换成字符串,那么我to_string()函数就能够搞定,为什么还要多此一举?犹如跳梁小丑。
> 然而,如果字符串转换为对应数据,那么我认为还是很值得入手字符串流的。(但显然用的次数不会多,毕竟不太可能在信奥中碰到字符串转其它的情况。可能以后用C++做开发会用到,但我完全可以考虑换成Python做开发,直接int()或者float()函数搞定了啊。)
2.2. 用途2:特定进制转换
这就是作者说的在欢乐赛中用字符串流的一个案例:A26240.16进制加法
这个是欢乐赛#26的T7,在欢乐赛#18往后,顾名思义,还是很简单的一道题。
回归正题。
我们都知道,计算器中提供的程序员模式包含四种进制:二进制(BinaryBinaryBinary,简称 BinBinBin)、八进制(OctalOctalOctal,简称 OctOctOct)、十进制(DecimalDecimalDecimal,简称 DecDecDec)和十六进制(HexadecimalHexadecimalHexadecimal,简称 HexHexHex),那么字符串流的任务就是将十进制整数和八进制或者十六进制整数进行互换。
还是一样,在代码中看它的实现方式。
【例3】输入整数n,输出十六进制下的n。
效果:
输入:100
输出:0x64
如果不适用showbase则不会带有前缀。
【例4】输入十六进制下的整数n,输出十进制下的n。
效果:
输入:0x64
输出:100
> Note:
>
> 1. 对于十六进制转十进制的效果而言,输入数据中,前缀中的x小写或大写均可,甚至省略前缀也可,也就是说,不管输入是0x64还是0X64亦或是64,输出都是100
> 如果输入中出现了字母a到f,那么大写小写都可以,比如输入0xa和0xA,结果都是10
> 2. 如果十进制转换为十六进制,注意它输出结果中默认给字母a到f都是小写,需要手动异或32转换大写或者利用transform(s.begin(),s.end(),s.begin(),::toupper)转换
> 3. 字符串流只能够转换八进制和十六进制,不能够转换二进制。可以手写递归函数转换为二进制或者利用bitset来转换
一样的,这里只是给出用法。对字符串流感兴趣的读者可以考虑用字符串流完成欢乐赛#26的T7,上面给出了超链接。
> 作者锐评:
> 转换八进制这一块是真正的神;转换十六进制还得看题目要求,如果最终结果要求小写,那么字符串流依旧是神,但如果要求大写,想利用字符串流就手动调输出结果或者自动转换,不想用那还是老老实实手写。在此不多作评论,还是要看你自己的想法。
2.3. 用途3:字符串和布尔值互换
先来看将布尔值化为字符串:
【例5】输入布尔值0或1,输出对应的字符串,0代表false,1代表true。
(本帖主要目标是科普字符串流的用法,所以还是呈现一下代码,虽然真的很傻的亦驼)
【例6】输入字符串,输出对应的布尔值true或false。(字符串中含有非0开头的数即为true,否则为false)
【例7】输入字符串,输出对应的布尔值true或false。(字符串中含有"true"子串即为true,否则为false)
> 作者锐评:
> 没人会用字符串流来把布尔值变成对应字符串的,是个正常人写个三目运算符或者if判断码量都比这个少。此处只是为了科普一下RE,请勿见怪。
> 至于字符串转布尔值,谁家好人的题目中会出现啊?
> 在这一块,字符串流就是一个勒瑟。
2.4. 用途4:分割字符串
这可能是一个较为冷门的话题:分割字符串。
很显然,在Python中这是一个再简单不过的话题,直接用s.split(sep)即可。
但在C++中我们没有专门为这个而生的split函数。此时我们有两个选择:
1. 手写一个split
2. 利用字符串流
【例8】输入一行字符串 sss,再输入一个字符 ccc,输出 sss 被 ccc 分割后的所有子串,一行一个子串。
样例输入:
样例输出:
下面展示了如何使用字符串流进行操作:
这段代码的核心是getline函数,它具有三个参数。
第一个参数必须是各种stream类型的变量。上面的cin就是istream类型的,因此可以作为getline函数的第一个参数。ss同理。
第二个参数是目标字符串,要让string类型的变量接收。
第三个参数是分割字符,必须是 字符 而非字符串,默认为\n,因此getline(cin,s)就是读入一行,它以\n作为分割字符。
> 作者锐评:
> 从这段代码不难看出,字符串流配合getline函数进行字符串分割是具有一定局限性的。它只能把char类型的变量当做分割符,不能用string类型变量分割。
> 如果以char类型的变量作为分割,那么我直接遍历字符串+判断当前字符即可:
>
> 这样手写也能够做到。
> 那如果以字符串作为分割符呢?
> 这可能需要用到 KMP\tt{KMP}KMP 算法这种高级算法,用分割符字符串 ttt 建立 nxtnxtnxt 数组,然后通过在待分割字符串 sss 中匹配 ttt 来找到所有 ttt 的位置,最后遍历 sss 完成分割。
> 但明显这个用字符串流是做不到的。
3. 总结
字符串流,听上去是一个高大上的东西。它可以帮助我们完成:
1. 字符串型和整型、浮点型等其它数据类型的互相转化
2. 十进制整数和八进制、十六进制数的互相转化
3. 0/1布尔值或字符串中的布尔值转化为true/false形式布尔值
4. 以字符为分割符对字符串进行分割
但是,上述功能许多可以通过手写函数搞定。
1. 整型、浮点型转化为字符串型可以通过to_string()函数搞定
2. 进制转换可以通过手写函数搞定,而且手写函数还不局限于十六进制和八进制
3. 0/1布尔值转true/false形式一个三目运算符就搞定了,字符串中的布尔值可以通过遍历字符串进行转化
4. 分割字符串可以遍历字符串搞定,就算是以字符串作为分割符也可以用 KMP\tt{KMP}KMP 算法匹配+遍历字符串搞定
因此,综上所述,字符串流可能更偏向于一个娱乐性大于实用性的工具。很多功能可以通过手写的代码进行替换。
个人认为,在导入万能头的情况下,如果字符串流的代码量少于手写的码量,可以考虑入手;但一旦码量不比手写的码量少,或者不导入万能头又不想再导入sstream头文件的时候,它的性价比过于低,不建议入手。(当然,最终还是看你自己的意愿!)
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
参考文献:
《普林斯顿微积分(乱入)》
《从Scratch到C++轻松学》
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
貌似还没有人写过这个,那么我就来写一篇新鲜出炉的创作计划,主题就是字符串流。不喜勿喷,但欢迎提建议。