技术标签: C/C++ 语言 c++ c string 编程初学者
一、string
string 是 C++ 提供的字符串类型,和 C 的字串相比,除了有不限长度的优点外,还有其他许多方便的功能。要使用 string, 必须先加入这一行:
#include <string>
接下來要定义一个字串变量,可以写成:
string s;
我们也可以在定义的同时初始化字串:
string s = "you";
而要取得其中某一个字元,和传统C 的字串一样是用 s[i] 的方式取得。比较不一样的是如果 s 有三个字元,传統 C 的字串的 s[3] 是'\0' 字符,但是 C++ 的 string 则是只到 s[2] 这个字符而已。
做一个对照:
操作 | string | 字符数组 |
定义字符串 |
string s; | char s[100]; |
取得第i个字符 | s[i] | s[i] |
字符串长度 | s.length() 或 s.size() |
strlen(s) |
读入一行 | getline(cin, s); | gets(s); |
赋值 | s = "you"; | strcpy(s, "you"); |
字符串连接 | s = s + "you"; s += "you"; |
strcat(s, "you"); |
字符串比较 | s == "you" | strcmp(s, "you"); |
两个string常用的方法是find和substr。在下面的代码当中:find函数从str的第3个位置查起,找到ssdf这个子串后,返回子串的位置。而substr函数从pos位置开始,截取5个字符,赋值给str2。也就是说,str2之后的内容将是ssdfs。
string str = "aaaaddddssdfsasdf";
size_t pos = str.find("ssdf", 3);
//可以用if(pos == string::npos) 用来判断是否找到子串。
//传送门: http://blog.csdn.net/sunshineacm/article/details/78075135
string str2 = str.substr(pos, 5);
二、stringstream
stringstream是 C++ 提供的另一个字串型的串流(stream)物件,和之前学过的iostream、fstream有类似的操作方式。要使用stringstream, 必须先加入这一行:
#include <sstream>
stringstream主要是用在將一个字符串分割,可以先用.clear( )以及.str( )將指定字串设定成一开始的內容,再用>>把个別的资料输出。
举个例子:
題目:输入的第一行有一个数字 N 代表接下來有 N 行资料,每一行资料里有不固定个数的整数(最多20个,每行最大200个字元),编程將每行的总和打印出來。
输入:
3
1 2 3
20 17 23 54 77 60
111 222 333 444 555 666 777 888 999
输出:
6
251
4995
代码:
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main()
{
string s;
stringstream ss;
int n;
cin >> n;
getline(cin, s); //读取换行
for (int i = 0; i < n; i++)
{
getline(cin, s);
ss.clear();
ss.str(s);
int sum = 0;
while (1)
{
int a;
ss >> a;
if(ss.fail())
break;
sum += a;
}
cout << sum << endl;
}
return 0;
}
三、使用stringstream简化类型转换
C++标准库中的<sstream>提供了比ANSI C的<stdio.h>更高级的一些功能,即单纯性、类型安全和可扩展性。接下来,我将举例说明怎样使用这些库来实现安全和自动的类型转换。
一个例子:
#include <stdio.h>
int main()
{
int n = 10000;
char s[10];
sprintf(s, "%d", n);
//s中的内容为“10000”
//到目前为止看起来还不错。但是,对上面代码的一个微小的改变就会使程序发生错误
printf("%s\n", s);
sprintf(s, "%f", n);
//错误的格式化符
printf("%s\n", s);
return 0;
}
输出:
在这种情况下,由于错误地使用了 %f 格式化符来替代了%d。因此,s在调用完sprintf()后包含了一个不确定的字符串。要是能自动推导出正确的类型,那不是更好吗?
进入stringstream:
由于n和s的类型在编译期就确定了,所以编译器拥有足够的信息来判断需要哪些转换。<sstream>库中声明的标准类就利用了这一点,自动选择所必需的转换。而且,转换结果保存在stringstream对象的内部缓冲中。你不必担心缓冲区溢出,因为这些对象会根据需要自动分配存储空间。
<sstream>库定义了三种类:istringstream、ostringstream和stringstream,分别用来进行流的输入、输出和输入输出操作。另外,每个类都有一个对应的宽字符集版本。简单起见,我主要以stringstream为中心,因为每个转换都要涉及到输入和输出操作。
注意,<sstream>使用string对象来代替字符数组。这样可以避免缓冲区溢出的危险。而且,传入参数和目标对象的类型被自动推导出来,即使使用了不正确的格式化符也没有危险。
1、string到int的转换
string result = "10000";
int n = 0;
stream << result;
stream >> n; //n等于10000
2.重复利用stringstream对象
如果你打算在多次转换中使用同一个stringstream对象,记住在每次转换前要使用clear()方法。
在多次转换中重复使用同一个stringstream(而不是每次都创建一个新的对象)对象最大的好处在于效率。stringstream对象的构造和析构函数通常是非常耗费CPU时间的。
3.在类型转换中使用模板
你可以轻松地定义函数模板来将一个任意的类型转换到特定的目标类型。例如,需要将各种数字值,如int、long、double等等转换成字符串,要使用以一个string类型和一个任意值t为参数的to_string()函数。to_string()函数将t转换为字符串并写入result中。使用str()成员函数来获取流内部缓冲的一份拷贝。
template<class T>
void to_string(string &result, const T &t)
{
ostringstream oss; //创建一个流
oss << t; //把值传递入流中
result = oss.str(); //获取转换后的字符并将其写入result
}
//这样,你就可以轻松地将多种数值转换成字符串了
to_string(s1, 10.5); //double到string
to_string(s2, 123); //int到string
to_string(s3, true); //bool到string
//可以更进一步定义一个通用的转换模板,用于任意类型之间的转换。函数模板convert()含有两个模板参数out_type和in_value,功能是将in_value值转换成out_type类型:
template<class out_type, class in_value>
out_type convert(const in_value & t)
{
stringstream stream;
stream << t; //向流中传值
out_type result; //这里存储转换结果
stream >> result; //向result中写入值
return result;
}
测试代码:
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
template<class T>
void to_string(string &result, const T &t)
{
ostringstream oss;
oss << t;
result = oss.str();
}
template<class out_type, class in_value>
out_type convert(const in_value & t)
{
stringstream stream;
stream << t;
out_type result;
stream >> result;
return result;
}
int main()
{
//to_string实例
string s1, s2, s3;
to_string(s1, 10.5); //double到string
to_string(s2, 123); //int到string
to_string(s3, true); //bool到string
cout << s1 << endl << s2 << endl << s3 << endl << endl;
//convert()例子
double d;
string salary;
string s = "12.56";
d = convert <double> (s); //d等于12.56
salary = convert <string> (9000.0); //salary等于"9000"
cout << d << endl << salary << endl;
return 0;
}
输出:
4.结论
在过去留下来的程序代码和纯粹的C程序中,传统的<stdio.h>形式的转换伴随了我们很长的一段时间。但是,如文中所述,基于stringstream的转换拥有类型安全和不会溢出这样的特性,使我们有充足得理由去使用<sstream>。<sstream>库还提供了另外一个特性—可扩展性。你可以通过重载来支持自定义类型间的转换。
5.一些实例
stringstream通常是用来做数据转换的。相比c库的转换,它更加安全,自动和直接。
例子一: 基本数据类型转换例子 int 转 string
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main()
{
stringstream ss;
string s;
int i = 1000;
ss << i;
ss >> s;
cout << s << endl;
return 0;
}
运行结果:
例子二: 除了基本类型的转换,也支持char *的转换
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main()
{
stringstream ss;
char s[10];
ss << 8888;
ss >> s;
cout << s << endl;
return 0;
}
运行结果:
例子三: 再进行多次转换的时候,必须调用stringstream的成员函数.clear()
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main()
{
stringstream ss;
int first = 0, second = 0;
ss << "456"; // 插入字符串
ss >> first; //转换成int
cout << first << endl;
ss.clear(); //在进行多次转换前, 必须清除ss
ss << true;
ss >> second;
cout << second << endl;
return 0;
}
运行结果:
运行.clear()结果
没有运行.clear()结果
6.使用误区
如果stringstream使用不当,当心内存出问题。试试下面的代码,运行程序前打开任务管理器,看看内存变化。
复制代码,把 stream.str(""); 那一行的注释去掉,再运行程序,内存就正常了。
看来stringstream似乎不打算主动释放内存( 或许是为了提高效率 ),但如果你要在程序中用同一个流,反复读写大量的数据,将会造成大量的内存消耗,因此这时候,需要适时地清除一下缓冲 ( 用 stream.str("") )。
另外不要企图用 stream.str().resize(0) 或 stream.str().clear() 来清除缓冲,使用它们似乎可以让stringstream的内存消耗不要增长得那么快,但仍然不能达到清除stringstream缓冲的效果(做个实验就知道了,内存的消耗还在缓慢的增长)
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
std::stringstream stream;
string str;
while(1)
{
//clear()这个名字让很多人想当然地认为它会清除流的内容。
//实际上它并不清空任何内容,它只是重置了流的状态标志。
stream.clear();
//去掉下面这行注释,清空stringstream的缓冲,每次循环内存消耗将不再增加。
//stream.str("");
stream << "you see see you";
stream >> str;
// 去掉下面这行注释,看看每次循环,你的内存消耗会增加多少
//cout << "Size of stream = " << stream.str().length() << endl;
}
return 0;
}
参考链接:
http://blog.csdn.net/zhang_xueping/article/details/47846807
http://blog.csdn.net/u014097230/article/details/52089530
文章浏览阅读2.5k次,点赞2次,收藏15次。编译是大部分工程师的烦恼,大家普遍喜欢去写业务代码。但我觉得基本的编译流程,我们还是需要掌握的,希望遇到相关问题,不要退缩,尝试去解决。天下文章一大抄,百度能解决我们90%的问题。_gdb编译
文章浏览阅读1.8k次,点赞4次,收藏6次。python简易爬虫v1.0作者:William Ma (the_CoderWM)进阶python的首秀,大部分童鞋肯定是做个简单的爬虫吧,众所周知,爬虫需要各种各样的第三方库,例如scrapy, bs4, requests, urllib3等等。此处,我们先从最简单的爬虫开始。首先,我们需要安装两个第三方库:requests和bs4。在cmd中输入以下代码:pip install requestspip install bs4等安装成功后,就可以进入pycharm来写爬虫了。爬
文章浏览阅读2.6k次。解决方法:解决方法可以去github重新下载一个pyflakes.vim。执行如下命令git clone --recursive git://github.com/kevinw/pyflakes-vim.git然后进入git克降目录,./pyflakes-vim/ftplugin,通过如下命令将python目录下的所有文件复制到~/.vim/ftplugin目录下即可。cp -R ...._freetorn.vim
文章浏览阅读210次,点赞7次,收藏3次。本文简述了hello.c源程序的预处理、编译、汇编、链接和运行的主要过程,以及hello程序的进程管理、存储管理与I/O管理,通过hello.c这一程序周期的描述,对程序的编译、加载、运行有了初步的了解。_hit csapp
文章浏览阅读1w次,点赞2次,收藏27次。来源:机器人小妹 很多时候企业拥有重复,乏味且困难的工作流程,这些流程往往会减慢生产速度并增加运营成本。为了降低生产成本,企业别无选择,只能自动化某些功能以降低生产成本。 通过数字化..._人工智能平台
文章浏览阅读2.2k次。热加载能够在每次保存修改的代码后自动刷新 electron 应用界面,而不必每次去手动操作重新运行,这极大的提升了开发效率。安装 electron 热加载插件热加载虽然很方便,但是不是每个 electron 项目必须的,所以想要舒服的开发 electron 就只能给 electron 项目单独的安装热加载插件[electron-reloader]:// 在项目的根目录下安装 electron-reloader,国内建议使用 cnpm 代替 npmnpm install electron-relo._electron-reloader
文章浏览阅读3.8w次,点赞107次,收藏993次。点击上方“Python爬虫与数据挖掘”,进行关注回复“书籍”即可获赠Python从入门到进阶共10本电子书今日鸡汤昔闻洞庭水,今上岳阳楼。大家好,我是小F。Python是目前最好的编程语言之一。由于其可读性和对初学者的友好性,已被广泛使用。那么要想学会并掌握Python,可以实战的练习项目是必不可少的。接下来,我将给大家介绍20个非常实用的Python项目,帮助大家更好的..._python项目
文章浏览阅读1.3k次。在网站的导航资源里看到了一个非常好用的东西:Android Asset Studio,可以在线生成各种图标。之前一直在用一个叫做Android Icon Creator的插件,可以直接在Android Studio的插件里搜索,这个工具的优点是可以生成适应各种分辨率的一套图标,有好几种风格的图标资源,遗憾的是虽然有很多套图标风格,毕竟是有限的。Android Asset Studio可以自己选择其..._在线 android 图标
文章浏览阅读514次。无限轮播广告位没有录屏,将就将就着看,效果就是这样主要代码KsBanner.java/** * 广告位 * * Created by on 2016/12/20. */public class KsBanner extends FrameLayout implements ViewPager.OnPageChangeListener { private List
文章浏览阅读2.2k次,点赞2次,收藏6次。继续上次的echart博客,由于省会流向图是从echart画廊中直接取来的。所以直接上代码<!DOCTYPE html><html><head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /&_java+echart地图+物流跟踪
文章浏览阅读1.4k次。一、OSD模块简介1.1 消息封装:在OSD上发送和接收信息。cluster_messenger -与其它OSDs和monitors沟通client_messenger -与客户端沟通1.2 消息调度:Dispatcher类,主要负责消息分类1.3 工作队列:1.3.1 OpWQ: 处理ops(从客户端)和sub ops(从其他的OSD)。运行在op_tp线程池。1...._ceph 发送数据到其他副本的源码
文章浏览阅读7.9k次,点赞3次,收藏22次。一 定义这是最早出现的置换算法。该算法总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面予以淘汰。该算法实现简单,只需把一个进程已调入内存的页面,按先后次序链接成一个队列,并设置一个指针,称为替换指针,使它总是指向最老的页面。但该算法与进程实际运行的规律不相适应,因为在进程中,有些页面经常被访问,比如,含有全局变量、常用函数、例程等的页面,FIFO 算法并不能保证这些页面不被淘汰。这里,我_进程调度fifo算法代码