snprintf、stringstream、strstream的对比分析

C++学习(十二)stringstream istringstream ostringstream C++的字符串读写流 istringstream类用于执行C++风格的字符串流的输入操作。 ostringstream类用于执行C++风格的字符串流的输出操作。 strstream类同时可以支持C++风格的串流的输入输出操作。 1、空格会成为字符串参数的内部分界。 2、要使用上述类,必须包含 sstream 头文件。 注意:由于stringstream构造函数会特别消耗内存,似... 阅读详情

这两天在看《C++编程思想》IO部分,里面有讲到strstream的使用,但在g++里却编译不通。上网一查才发现strstream已经被stringstream替代了,后者在类型转换中有巧妙的应用。但这二者差别是很大的。

对sprintf问题的奥威尔式的严格考察,最终以我们对snprintf、std::stringstream、std::strstream以及非标准但极度优雅的boost::lexical_cast的一番对比分析结束。

Guru问题

1. 比较下面这些替代方案的优点和弱点,使用第2条中的分析和示例代码。

(a)snprintf

(b)std::stringstream

(c)std::strstream

(d)boost::lexical_cast

解决方案

替代方案#1:snprintf

1. 比较下面这些替代方案的优点和弱点,使用第2条中的分析和示例代码。

(a)snprintf

在所有的选择当中,与sprintf最相近的选择当然是snprintf了。snprintf只是在sprintf上增加了一项功能,不过是一项重要功能,即用户可以给出输出缓冲区的最大长度,从而避免缓冲区溢出。当然,如果缓冲区太小的话,输出结果就会被截断。

长久以来,在大多数C实现上,snprintf都是作为一个非标准的扩展存在的。随着C99标准的颁布[C99],snprintf终于浮上台面而成为合法功能,目前snprintf已经是C99标准中的正式一员。不过,除非你的编译器是符合C99标准的,否则可能仍然必须使用供应商提供的非标准扩展,如_snprintf。

坦白地说,早该使用snprintf来取代sprintf了,即使在snprintf还没有标准化之前。大多数良好的编码标准都不推荐你使用像sprintf这样的不检查长度的函数,而且该原则是很有道理的。使用不做检查的sprintf长久以来会引起一些声名狼藉的常见问题,它通常会导致程序崩溃[1],尤其会导致安全脆弱问题[2]。

借助于snprintf,我们就可以正确编写刚才一直试图实现的带长度检查的PrettyFormat()版本。

// 示例3-1:在C中使用snprintf来字符串化某些数据

//

void PrettyFormat(int i, char* buf, int buflen) {

 // 这就是代码,简洁优雅,关键是比以前要安全得多:

 snprintf(buf, buflen, "%4d", i);

}

注意,即便这样做了,仍然还存在另一种出错的可能,即调用者将缓冲区长度搞错了。这意味着跟那些具有资源管理功能的替代方案相比,snprintf还算不上百分之百地杜绝缓冲区溢出可能性,不过跟sprintf相比它显然要安全多了,在“长度是否安全?”这个问题上应该算是合格的。使用sprintf没有合适的途径来绝对避免缓冲区溢出,而通过snprintf,我们则可以(很大程度上)杜绝缓冲区溢出。

注意,snprintf的一些标准化之前版本的行为稍有不同。尤其是在一个主要实现中,如果输出结果填满或者大于缓冲区容量,缓冲区里的串就不会以'\0'结尾。这种情况下,我们的PrettyFormat()函数就得稍作调整以应付这种非标准的行为:

// 在C中使用一个并不十分遵从C99标准的_snprintf来将数据字符串化

//

void PrettyFormat(int i, char* buf, int buflen) {

 // 这里是代码,简洁优雅,而且安全得多

 if(buflen > 0) {

   _snprintf(buf, buflen-1, "%4d", i);

   buf[buflen-1] = '\0';

 }

}

在其他任何方面,sprintf和snprintf都是一样的。综上所述,snprintf跟sprintf的比较如表3-1所示。

表3-1  snprintf与sprintf的比较

 

snprintf

sprintf

标准吗

易用吗,代码清晰明确吗

高效吗,无额外的内存分配吗

长度安全吗

类型安全吗

可用于模板之中吗

是:仅[C99],不过也可能进入C++0x

是

是

是

否

否

是:[C90],[C++03],[C99]

是

是

否

否

否

从这些比较当中,我们可以给出如下的建议:

准则:永远不要使用sprintf。

如果你真的决定使用C的stdio设施的话,一定要记住,使用那些进行长度检查的函数,如snprintf。即便在你的编译器上它们只是作为非标准扩展存在,也得使用它们,因为使用它们没有任何损失,还能够带来实实在在的好处。

我曾在C++大会上将该主题作为演讲稿的材料,一开始我便惊讶地发现,通常在一场大会的所有到场人员当中只有百分之十的人听说过snprintf。然而,几乎每次,当我问到关于sprintf在实际项目当中导致的问题时,总会有人立即举手,描述他们最近在项目当中发现一些缓冲区溢出bug,而当他们在整个项目中将sprintf全部替换为snprintf之后再去进行测试,发现不但这些bug消失了,就连其他一些早就报告了的、已经在bug队列里面呆了很长时间却一直没人能够解决的神秘bug也随之消失了。

结论,正如我一直所说的,是永远不要使用sprintf。

替代方案#2:std::stringstream

(b)std::stringstream

C++中用于字符串化的最常见设施就是stringstream这一族的类了。示例3-1如果用ostringstream来替代sprintf的话看起来就会像这样:

// 示例3-2:在C++中进行字符串化,使用ostringstream

//

void PrettyFormat(int i, string& s) {

 // 不如原先的简洁优雅

 ostringstream temp;

 temp << setw(4) << i;

 s = temp.str();

}

相对于sprintf来说,stringstream具有一些优点,但同时也有缺点。在sprintf光芒四射的那些地方,stringstream显得并不那么出色。

议题#1:易用性和清晰性。使用stringstream不仅让原先的一行代码变成了三行,而且我们还得引入一个临时变量。使用stringstream的做法有几个优势,不过代码的清晰性并非其中之一。这并不是说像setw(4)这样的流操纵子难于学习,实际上它们跟sprintf的格式化标志一样易学,只不过前者通常更为笨拙冗长一些。我发现那些到处“点缀”着像<<setprecision(9)和<<setw(14)这样的长名字的代码会难于阅读(我是说,跟%14.9这种格式化字符串相比),即便所有的操纵子都整齐排列也无济于事。

议题#2:效率(能否直接利用现有缓冲区)。stringstream会自己另外分配一份单独的缓冲区来存放结果,另外还需要使用一些辅助性的对象,通常所有这些都意味着需要进行额外的内存分配。我在两个当前流行的编译器上测试了示例3-2的代码,同时让::operator new统计总共的分配次数。结果发现在某个平台上有两次动态内存分配,另一个平台上则是三次。

而在sprintf一筹莫展的那些地方,stringstream则大显身手。

议题#3:长度安全性。stringstream内部的basic_stringbuf缓冲区类会根据需要自动增长,以便容纳需要存放的数据。

议题#4:类型安全性。使用operator<<和重载决议,即便是对于那些提供了自己的流插入操作符的自定义流类型,也总能够实现类型安全性。不会因为类型不符而导致一些神秘的运行时错误。

议题#5:模板亲和性。既然编译器会自动调用正确的operator<<,那么将PrettyFormat泛化为可接受任何类型的数据应当是举手之劳:

template<typename T>

void PrettyFormat(T value, string& s) {

 ostringstream temp;

 temp << setw(4) << value;

 s = temp.str();

}

综上所述,stringstream跟sprintf的比较如表3-2所示。

表3-2  stringstream与sprintf的比较

 

stringstream

sprintf

标准吗

 

易用吗,代码清晰明确吗

高效吗,无额外内存分配吗

长度安全吗

类型安全吗

可用于模板之中吗

是:[C++03]

 

否

否

是

是

是

是:[C90],

[C++03],[C99]

是

是

否

否

否

替代方案#3:std::strstream

(c)std::strstream

不管这种说法公平与否,strstream都是要被遗弃的。由于[C++03]标准将它标明为deprecated(不赞成的),因而优秀的C++书籍顶多也只是略微提及一下(见[Josuttis99]的第649页),大多数则是根本不提(见[Stroustrup00]),甚至明确地表态说不会讨论这方面的内容,因为strstream是官方规定的“替补”(见[Langer00]的第587页)。标准委员会觉得stringstream可以取代strstream,因为stringstream更好地封装了内存管理,所以他们将strstream标明为deprecated,然而strstream仍然还是标准的法定成员,任何符合C++标准的实现都必须提供它[3]。

由于strstream仍然是标准的,所以为了完整起见这里还是提一下它。碰巧它也的确提供了一些优点。使用strstream的话,示例3-1看起来就会像这样:

// 示例3-3:在C++中使用ostrstream进行字符串化

//

void PrettyFormat(int i, char* buf, int buflen) {

 // 不算太差,不过别忘了最后还要输出结束符

 ostrstream temp(buf, buflen);

 temp << setw(4) << i << ends;

}

议题#1:易用性和清晰性。strstream在易用性跟代码的清晰性方面略逊stringstream一筹。两者都要求建立一个临时对象,不过strstream要求你记得手动输出一个结束符来结束字符串,这除了令人感觉有点不愉快之外,还有点危险,因为如果一不小心忘记了,同时在读取结果串的时候又期望该串是以'\0'字符结尾的话,你就面临着读取超过结果串末尾之后的内存数据的危险,而就算sprintf也没这么脆弱,它总是会在结果串的末尾加上结束符。不过,按照示例3-3所展示的方式那样使用strstream至少有一个优点,即无需在最后调用c_str()来获取结果串。(当然,如果让strstream创建自己的缓冲区,其内存只是部分封装的,你除了得在最后调用.str()来将其中的结果串取出来之外,还得加上一次.freeze(false)调用,否则strstreambuf在析构的时候是不会释放内存的。)

议题#2:效率(能否直接利用现有缓冲区)。我们只需在创建ostrstream对象的时候传递一个指向现有缓冲区的指针,就可以避免任何额外的内存分配,ostrstream会将它的结果直接输出到该缓冲区当中。这跟stringstream相比是一个非常重要的区别,在能否将结果串直接输出到现有的目标缓冲区(从而避免额外的内存分配)这个问题上,stringstream根本无法与strstream比拟[4]。当然,如果你并没有现成可利用的缓冲区,ostrstream也可以使用自己动态分配的缓冲区,你只需调用它的默认构造函数即可[5]。确实,strstream是我们所讨论的所有可选方案当中惟一能够提供这种选择自由的方案。

议题#3:长度安全性。ostrstream内部的strstreambuf缓冲区会自动检查它的长度以确保不会写超过给定缓冲区之外的内存区域。而如果我们使用的是一个默认构造的ostrstream对象的话,其内部的strstreambuf缓冲区就会根据需要自动增长以容纳有待存储的值。

议题#4:类型安全性。strstream跟stringstream一样,完全是类型安全的。

议题#5:模板亲和性。完全可以!正如stringstream一样。例如:

template<typename T>

void PrettyFormat(T value, char* buf, int buflen) {

 ostrstream temp(buf, buflen);

 temp << setw(4) << value << ends;

}

总之,strstream与sprintf的比较结果如表3-3

C++字符串拼接效率比较(+=、append、stringstream、spintf) 事情起因很简单,自己的代码中使用了stringstream对象进行字符串的拼接,然后被老同事质疑效率低下。借着这个机会了解下为什么? 一、+=、append、stringsteam、sprintf四种字符串拼接方法比较 C/C++中字符串拼接的使用场景非常多,字符串拼接的方法也非常多,这里简单的比对下上述四种方法的效率。 测试方法:分别哟经+=、append、stringstream、sprintf的方式来拼接字符串。s1=“aaaaa”,s2=“bbbbb”,s3=“ccccc”。内层循环将这三个字 阅读详情

相关推荐

C语言字符串格式化:sprintf及其替代方案分析

本文深入探讨了C语言中使用sprintf进行数据格式化及其潜在风险,并对比分析了snprintf、std::stringstream、std::strstream以及boost::lexical_cast等多种替代方案的优缺点。文章强调了选择合适字符串格式化方法的重要性,并提供了实用的建议。...

weixin_30591519的博客 512

c++ strstream

strstream

qq_29070399的博客 4192

C语言字符串格式化:sprintf及其替代方案

本文深入探讨了C语言中常用的字符串格式化函数sprintf及其多种替代方案。从sprintf的优缺点出发,介绍了snprintf、std::stringstream、std::strstream和boost::lexical_cast等替代方案,详细比较了它们在易用性、效率、长度安全、类型安全和模板化能力等方面的特点,并提供了使用建议。...

weixin_26907223的博客 701

C字符串流

C字符串流 在C++中,有一个stream这个类,所有的I/O都以这个“流”类为基础的,里面包括了所有的输入输出类,今天我们就来介绍一下sstream.h(字符串流)这个类: C++引入了ostringstream、istringstream、stringstream这三个类,要使用他们创建对象就必须包含sstream.h头文件。 istrings...

weixin_34342992的博客 351

C++标准库中sstream与strstream的区别详细解析

在C++有两种字符串流,一种在sstream中定义,另一种在strstream中定义。它们实现的东西基本一样。 strstream里包含class strstreambuf;class istrstream;class ostrstream;class strstream;它们是基于C类型字符串char*编写的 sstream中包含class istringstream;class ostringstream;class stringbuf;class stringstream;class …….它们是基于std::string编写的 因此ostrstream::str()返回的是char*类

科研党必看!Zotero+Claude3插件实战:如何用AI高效阅读文献(附详细配置步骤)

本文详细介绍了如何通过Zotero与Claude3插件的深度整合,实现科研文献的高效阅读与管理。从环境配置到核心功能实战,包括智能摘要生成、精准学术翻译和论文写作辅助等,帮助科研人员大幅提升工作效率。特别适合需要处理大量文献的研究者使用。

o0p1q2r3的博客 434

从字符串中读写的方法:c语言中的sscanf、sprintf函数,c++中的I/O流strstream、stringstream

众所周知,c语言中我们常使用的标准输入输出流函数为scanf()、printf(),默认从键盘上输入数据、向屏幕输出数据。此外,c语言中还提供了另一组库函数sscanf()、sprintf()函数,它们的功能与前者相同,只不过输入输出的对象为字符串。这两个函数的原型为除了第一个参数外,其他参数的含义和用法实际上是与scanf()、printf()相同的。下面给举个例子。运行结果为。

baidu_38797690的博客 1372

RPG MV游戏数据修改指南:从JSON解析到脚本进阶

游戏数据修改是游戏开发与逆向工程中的常见实践,其核心原理在于解析和操作游戏资源文件。许多现代游戏引擎采用结构化数据格式(如JSON)存储游戏配置,这为外部修改提供了可能。通过理解文件结构、数据格式与游戏逻辑的关联,开发者或爱好者可以调整游戏平衡、修复问题或实现个性化定制,这不仅能深化对游戏设计机制的理解,也具备实际的技术学习价值。本文以基于Web技术栈的RPG Maker MV(RPG MV)引擎为例,其游戏数据多以明文JSON存储,结构清晰。文章将具体阐述如何安全地修改角色属性、物品效果、敌人数据及技能公

weixin_29001419的博客 406

c++ stringstream(老好用了)

前言:  以前没有接触过stringstream这个类的时候,常用的字符串和数字转换函数就是sscanf和sprintf函数。开始的时候就觉得这两个函数应经很叼了,但是毕竟是属于c的。c++中引入了流的概念,通过流来实现字符串和数字的转换方便多了。在这里,总结之前的,并介绍新学的。 常见格式串:     %% 印出百分比符号,不转换。  %c 整数转成对应的 ASCII 字元...

aipiannian6725的博客 2万+

[GXYCTF2019]BabyUpload1 -- .htaccess文件解析与蚁剑实战指南

本文深入解析了[GXYCTF2019]BabyUpload1文件上传漏洞的实战利用。通过绕过后缀名与内容检测,上传包含PHP代码的.jpg文件,并利用.htaccess文件强制服务器将其解析为PHP脚本。文章详细介绍了.htaccess的原理、构造方法、Content-Type绕过技巧,并演示了如何使用中国蚁剑连接Webshell获取Flag,为Web安全学习提供了完整实战指南。

weixin_29016315的博客 510

电磁炉线圈加热仿真全流程:用Ansys Maxwell分析涡流效应与热损耗

本文详细介绍了使用Ansys Maxwell进行电磁炉线圈加热仿真的全流程。通过构建2D轴对称涡流场模型,深入分析了高频下的涡流效应、趋肤效应与热损耗分布,为优化线圈设计、提升加热效率与均匀性提供了量化依据和实用指南。

sss66的博客 966

[转]各显神通的字符串格式器

[声明]:本英文资料源自于Herb Sutter 创建的“Guru of the Week(GOTW)”栏目,“C++ 翻译小组”的翻译作品供学习交流与参考用途,不得用于任何商业用途。未经Herb Sutter同意,不得转载;对于违反以上条款,翻译小组对此不负任何责任;特此声明。 文章来源:http://www.

珍爱生命 远离C++ 1197

VS2022 中 std::mutex 静态初始化陷阱:一次诡异崩溃的排查记录

VS2022中使用std::mutex时遇到的静态初始化陷阱问题。该问题源于VS2022 17.4版本为std::mutex添加constexpr支持后,全局静态初始化可能导致系统资源未就绪时锁失效或崩溃。文章分析了问题现象、影响范围,并提供了解决方案。最后给出了排查清单和核心建议

qq_34552942的博客 952

The String Formatters of Manor Farm(zz)

The String Formatters of Manor Farm  This article appeared in C/C++ Users Journal, 19(11), November 2001. "All animals are equal, but some animals are more equal than others."               

afxid的专栏 1935

【调优】OpenClaw从零开始高阶调优之skill篇

本文详细解析了OpenClaw技能系统的核心机制。首先介绍了技能的分层加载逻辑,按照优先级从高到低依次为:工作区专属技能、全局共享技能、出厂自带技能和自定义路径技能。通过流程图展示了从扫描到注册的完整加载流程,并解释了同名技能覆盖规则。随后深入剖析了SKILL.md文件结构,包含YAML元数据定义技能属性和Markdown正文说明使用方法,以公众号写作技能为例展示了具体配置。文章还提供了技能优先级切换的实用技巧,帮助用户灵活管理不同版本的技能。

海兰 239

RK3568平台开发系列讲解(安卓篇)LED呼吸灯系统流程

1.app层封装后使用NotificationManager发出:NotificationManager.notify() 2.Android 系统架构中:一个manager类对应一个aidl接口,一个manager对应一个底层的服务,notification也是这个架构

内核笔记 3646

std::strstream和std::stringstream

开始 #include //std::stringstream(推荐使用) #include //std::strstream(已废弃) #include template std::string ToString(Type_ num) { std::ostringstream ss; ss << std::fixed; ss.precision(9); ss

zhang的专栏 3804

【中项第三版】系统集成项目管理工程师 | 第 1 章 信息化发展

【中项第三版】系统集成项目管理工程师 | 第 1 章 信息化发展

静谧、淡雅 6577

sstream和strstream以及fstream

在C++有两种字符串流,也称为数组I/O流,一种在sstream中定义,另一种在strstream中定义。它们实现的东西基本一样。strstream里包含class strstreambuf;class istrstream;class ostrstream;class strstream;它们是基于C类型字符串char*编写的sstream中包含

1647

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

sstream和strstream的用法

在C++有两种字符串流,也称为数组I/O流,一种在 strstream 中定义,另一种在 sstream 中定义。它们实现的东西基本一样。strstream里包含:class strstreambuf;class istrstream;class ostrstream;class strstream;它们是基于C类型字符串char*编写的sstream中包含:class stringbuf;cla...

qian_youyou的博客 2421

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

stringstream 的用法介绍

stringstream 主要有两个作用:简化类型转换和一次性读入数据:   一、使用stringstream对象简化类型转换 C++标准库中的提供了比ANSI C的更高级的一些功能,即单纯性、类型安全和可扩展性。在本文中,我将展示怎样使用这些库来实现安全和自动的类型转换。 为什么要学习 如果你已习惯了风格的转换,也许你首先会问:为什么要花额外的精力来学习基于的类型 转换呢?也许对下面一

某花 2722

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

使用stringstream对象简化类型转换

on error resume next ShockMode = (IsObject(CreateObject("ShockwaveFlash.ShockwaveFlash.5"))) 如何打印: 请在您的浏览器中选择 文件 菜单中的 打印 选项------------------------------------------

awzzz的专栏 2529

stringstream用法总结

目录 一、概念 二、基本功能 功能一:预定的格式将程序中的数据保存在一个string 中 功能二:实现类型转换 功能三:实现任意类型转换 三、重要功能 1.数字由空格或逗号分开。 2.数字由逗号分开 3.数字间不止一类符号分隔 一、概念 C++引入了ostringstream、istringstream、stringstream这三个类,要使用他们创建对象就必须包含sstream.h头文件。 istringstream类用于执行C++风格的串流的输入操作。ost...

qq_41687938的博客 7416

C++ istringstream、ostringstream、strstream

istringstream、ostringstream、stringstream

weixin_45806636的博客 3617

std::stringstream 取代 sprintf

#include #include using namespace std; void main(int /*argc*/, char* /*argv*/[]) {     char temp[256];     sprintf_s(temp,"%d",10);     //有了sstream就可以去掉上面的写法了。     std::stringstream stream;

cwjjamk的专栏 1106
上一篇: operator C++ 操作符重载
下一篇: c++ fstream open 失败原因
beyondlpf
博客等级 码龄18年 103粉丝 72原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值