C++11标准库中cstdio头文件新增的5个格式化I/O函数学习

刚开始学网络编程,稍微扩展书上的简单C/S程序时,发现以前太忽略标准I/O这一块,查官网发现C++11新增了几个格式化I/O函数。

  • snprintf    将格式化输出写入到有大小限制的缓存中
  • vfscanf     从流中读取数据到可变参数列表中
  • vscanf      读取格式化数据到可变参数列表中
  • vsnprintf  从可变参数列表中写入数据到有大小限制的缓存中
  • vsscanf     从字符串中读取格式化数据到可变参数列表中

主要谈谈snprintf,后面4个都是辅助可变参数列表的。

int snprintf ( char * s, size_t n, const char * format, ... );

百度一下会发现一些过时的文章写到VC上是_snprintf而gcc上是snprintf,VC的_snprintf不会在复制完的字符串后面补上一个‘\0‘。

这是很多C新手会在Win平台会出现烫烫烫的原因,因为输出字符数组时没有遇到‘\0‘结尾,所以会一直输出,甚至是未初始化的内存,默认为0xcccccccc,变成字符串就是“烫烫烫”。具体参考文章【考据】“烫烫烫”与“锟斤拷”的原理

但是新标准已经将snprintf标准化了函数签名如下,所以也不用担心那个问题。

snprintf和sprintf功能基本一致,但是更安全,参数多了一个size_t n,代表写入缓存的数据大小。比如char buf[10]; 如果n超过10,就会在编译期提醒错误,所以在VS中写C++,如果使用fopen等函数会编译不通过,提示你使用fopen_s(类似的还有其他_s后缀的函数)等安全(safe)函数(会在编译期检测错误)的原因。题外话,解决方案解决use -D_SCL_SECURE_NO_WARNINGS的问题 ,当然更简单的做法就是每次新建C++项目时把默认SDL Check一栏的勾勾去掉。

回正题,也就是说,除了跟sprintf一样,到格式化字符串结尾会停止写入缓存外,当写入字符数量到达n时也会停止写入缓存,防止越界。这里的n有一点要注意,假如输入是合法的n,实际写入的字符数量是n-1,因为最后1个字符要留给‘\0‘。

返回值是如果缓存足够大,所应能出现的字符数。(注意!这和sprintf不同!)如果格式化字符串有误,返回负数。

#include <cstdio>

int main(int argc, char** argv)
{
	const int n = 10;
	char buf[n];
	int ret = snprintf(buf, n, "%d %s", 47, "is a good number");
	printf("%d:%s\n", ret, buf);
	return 0;
}

上述代码返回19,即整个字符串(47 is a good number)的长度,因为只写入了10个字符。返回值和n无关!是固定的!

所以当缓存大小BUFSIZE > retValue时(在这里即n>ret),字符串的n个字符被完全地写入了缓存中,再加上‘\0‘,占用了缓存的n+1个位置。

再以vfscanf为例

int vfscanf ( FILE * stream, const char * format, va_list arg );

和fscanf的区别在于,fscanf第三个参数是...,也就是C语言的可变参数列表,即<stdarg.h>的va_list、va_start、va_arg、va_end实现),对于va_list的描述,Objects of this type shall only be used as argument for the va_startva_argva_end and va_copy macros, or functions that use them, like the variable argument functions in <cstdio> (vprintfvscanfvsnprintfvsprintf and vsscanf).

当va_start初始化一个va_list后,在va_end之前调用v开头的格式化I/O函数,将可变参数列表的每个参数都进行printf或scanf操作。以vfscanf为例

#include <cstdio>
#include <cstdarg>

int my_fscanf(FILE* const stream, const char* const format, ...)
{
	va_list ap;
	va_start(ap, format);
	int ret = vfscanf(stream, format, ap);
	va_end(ap);
	return ret;
}

int main(int argc, char** argv)
{
	int i;
	double d;
	char s[BUFSIZ];
	my_fscanf(stdin, "%d, %lf, %s", &i, &d, s);
	printf("i = %d, d = %lf, s = %s\n", i, d, s);
	return 0;
}

类似上面的代码,相当于vxxx的函数都是用来配合那几个va_list、va_start、va_end实现xxx函数,主要作用还是用来转发可变参数列表,因为...无法作为参数传递,只能借助va_list的形式作为参数传递。因此通过vsnprintf能够轻松实现通过格式化字符串生成std::string的功能,代码如下:

include <cstdio>
#include <cstdarg>
#include <cstring>
#include <memory>
#include <string>

std::string str_format(const char *fmt, ...)
{
	int old_size = strlen(fmt);
	std::unique_ptr<char[]> buf(new char[old_size]);
	va_list ap;

	va_start(ap, fmt);
	int new_size = vsnprintf(buf.get(), old_size, fmt, ap);
	va_end(ap);
	if (new_size < 0)
		return "";

	buf.reset(new char[new_size + 1]);
	va_start(ap, fmt);
	new_size = vsnprintf(buf.get(), new_size + 1, fmt, ap);
	va_end(ap);
	if (new_size < 0)
		return "";

	return std::string(buf.get());
}

int main()
{
	auto ret = str_format("%d %lf %s", 1, 3.14, "hello world");
	printf("%s\n", ret.c_str());  // 1 3.140000 hello world
	return 0;
}

  

时间: 2024-08-04 12:15:43

C++11标准库中cstdio头文件新增的5个格式化I/O函数学习的相关文章

C++标准库名字和头文件

C++ Primer 中文版 第5版中表A.1列出的标准库名字和头文件 原文地址:https://www.cnblogs.com/merlindu/p/9395477.html

c/c++标准库中的文件操作总结

1 stdio.h是c标准库中的标准输入输出库 2 在c++中调用的方法 直接调用即可,但是最好在函数名前面加上::,以示区分类的内部函数和c标准库函数. 3 c标准输入输出库的使用 3.1 核心结构体 FILE结构体 打开一个文件的时候获取它,然后就可以不用管它了. 3.2 核心方法 3.2.1 fopen 第一个字符串是文件的路径. 第二个参数是一个字符串,表示操作该文件的模式,"rb"表示read binary,即以二进制的形式来读该文件. 3.2.2 fseek 第一个参数是F

C语言中的头文件

1.头文件#include <> :表示引用标准库头文件,编译器会从系统配置的库环境中去寻找 2.头文件#include "":一般表示用户自己定义使用的头文件,编译器默认会从当前文件夹中寻找,如果找不到,则到系统默认库环境中去寻找. ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 在C语言家族程序中,头文件被大量使用.一般而言,每个C++/C程序通常由头文件(header files)和

参考C++STL标准库中对了的使用方法

http://www.cppblog.com/zhenglinbo/archive/2012/09/18/191170.html 参考:http://www.cppblog.com/zhenglinbo/archive/2012/09/18/191170.html 当然是使用c++中的STL 的queue啦.下面简要介绍一下使用方法. 1 准备工作 头文件 #include<queue> 2 声明和定义的方法.STL的队列是泛型模板,支持任何内置和构造类型. 比如对于刚才那个牛奶问题.我把状态

STL笔记(6)标准库:标准库中的排序算法

STL笔记(6)标准库:标准库中的排序算法 标准库:标准库中的排序算法The Standard Librarian: Sorting in the Standard Library Matthew Austern http://www.cuj.com/experts/1908/austern.htm?topic=experts 用泛型算法进行排序    C++标准24章有一个小节叫“Sorting and related operations”.它包含了很多对已序区间进行的操作,和三个排序用泛型

理解C++中的头文件和源文件的作用【转】

一.C++编译模式通常,在一个C++程序中,只包含两类文件--.cpp文件和.h文件.其中,.cpp文件被称作C++源文件,里面放的都是C++的源代码:而.h文件则被称作C++头文件,里面放的也是C++的源代码.C+ +语言支持"分别编译"(separatecompilation).也就是说,一个程序所有的内容,可以分成不同的部分分别放在不同的.cpp文件里..cpp文件里的东西都是相对独立的,在编译(compile)时不需要与其他文件互通,只需要在编译成目标文件后再与其他的目标文件做

Windows 驱动程序工具包中的头文件

MSDN原文:https://msdn.microsoft.com/zh-cn/library/windows/hardware/ff554695(v=vs.85).aspx Windows 驱动程序工具包 (WDK) 包含构建内核模式和用户模式驱动程序所需的所有头文件(.h 文件).头文件在 WDK 安装文件夹中的 Include 文件夹中.示例:C:\Program Files (x86)\Windows Kits\10\Include. 头文件包含版本信息,因此不论驱动程序在哪个版本的 W

AVAudioPlayer播放音乐文件及读取ipod库中的音乐文件

下面是ipad上的调试效果 下面是代码,代码中都有注释: #import <UIKit/UIKit.h>  #import <AVFoundation/AVFoundation.h>  #import <MediaPlayer/MediaPlayer.h>   @interface RootViewController : UIViewController <AVAudioPlayerDelegate> {     AVAudioPlayer *player

iOS中中UIView头文件详细解析

@interface UIView : UIResponder<NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem> /** *  通过一个frame来初始化一个UI控件 */ - (id)initWithFrame:(CGRect)frame; // YES:能够跟用户进行交互 @property(nonatomic,getter=isUserInteractionEnabled) BOOL userInteraction