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

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

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

  1. 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不同!)如果格式化字符串有误,返回负数。

  1. #include <cstdio>
  2.  
  3. int main(int argc, char** argv)
  4. {
  5. const int n = 10;
  6. char buf[n];
  7. int ret = snprintf(buf, n, "%d %s", 47, "is a good number");
  8. printf("%d:%s\n", ret, buf);
  9. return 0;
  10. }

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

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

再以vfscanf为例

  1. 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为例

  1. #include <cstdio>
  2. #include <cstdarg>
  3.  
  4. int my_fscanf(FILE* const stream, const char* const format, ...)
  5. {
  6. va_list ap;
  7. va_start(ap, format);
  8. int ret = vfscanf(stream, format, ap);
  9. va_end(ap);
  10. return ret;
  11. }
  12.  
  13. int main(int argc, char** argv)
  14. {
  15. int i;
  16. double d;
  17. char s[BUFSIZ];
  18. my_fscanf(stdin, "%d, %lf, %s", &i, &d, s);
  19. printf("i = %d, d = %lf, s = %s\n", i, d, s);
  20. return 0;
  21. }

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

  1. include <cstdio>
  2. #include <cstdarg>
  3. #include <cstring>
  4. #include <memory>
  5. #include <string>
  6.  
  7. std::string str_format(const char *fmt, ...)
  8. {
  9. int old_size = strlen(fmt);
  10. std::unique_ptr<char[]> buf(new char[old_size]);
  11. va_list ap;
  12.  
  13. va_start(ap, fmt);
  14. int new_size = vsnprintf(buf.get(), old_size, fmt, ap);
  15. va_end(ap);
  16. if (new_size < 0)
  17. return "";
  18.  
  19. buf.reset(new char[new_size + 1]);
  20. va_start(ap, fmt);
  21. new_size = vsnprintf(buf.get(), new_size + 1, fmt, ap);
  22. va_end(ap);
  23. if (new_size < 0)
  24. return "";
  25.  
  26. return std::string(buf.get());
  27. }
  28.  
  29. int main()
  30. {
  31. auto ret = str_format("%d %lf %s", 1, 3.14, "hello world");
  32. printf("%s\n", ret.c_str()); // 1 3.140000 hello world
  33. return 0;
  34. }

  

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

  1. c++标准库都有哪些文件

    from:http://topic.csdn.net/u/20090201/16/3bd41b72-5694-474e-a68b-98b2f070e76b.html C++标准库的所有头文件都没有扩展 ...

  2. 彻底弄清c标准库中string.h里的常用函数用法

    在我们平常写的c/c++程序,一些算法题中,我们常常会用到c标准库中string.h文件中的函数,这些函数主要用于处理内存,字符串相关操作,是很有用的工具函数.而且有些时候,在笔试或面试中也会出现让你 ...

  3. C标准库中atoi的一种可能的实现

    为避免与标准库中的atoi产生歧义, 我将自己编写的函数命名为strToInt, 以下是示例代码 #include <stdio.h> int strToInt(const char *s ...

  4. cocos项目导入其它源文件时加入依赖库时,头文件提示找不到文件夹中的文件

    cocos项目导入其它源文件时加入依赖库时,头文件提示找不到文件夹中的文件解决方法: 选择项目属性->c/c++->常规,在附加包括项目中加上对应的文件夹 cocos test项目的库(所 ...

  5. VC中添加头文件以及库

    原文:http://blog.csdn.net/lwb102063/article/details/52068389   附加头文件包含 VC6.0中: VC6.0默认include包含路径:Tool ...

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

    1 stdio.h是c标准库中的标准输入输出库 2 在c++中调用的方法 直接调用即可,但是最好在函数名前面加上::,以示区分类的内部函数和c标准库函数. 3 c标准输入输出库的使用 3.1 核心结构 ...

  7. 在Qt Creator的项目中添加头文件和库

    在Qt Creator中的工程中,工程通过.pro文件管理. 额外需要连接的连接库unix:LIBS += -L your_lib_path -lyour_libwin32:LIBS += your_ ...

  8. Linux中常用头文件的作用--转

    http://blog.sina.com.cn/s/blog_5c93b2ab0100q62k.html 1. Linux中一些头文件的作用: <assert.h>:ANSI C.提供断言 ...

  9. C语言中的头文件

    1.头文件#include <> :表示引用标准库头文件,编译器会从系统配置的库环境中去寻找 2.头文件#include "":一般表示用户自己定义使用的头文件,编译器 ...

随机推荐

  1. Spring入门2. IoC中装配Bean

    Spring入门2. IoC中装配Bean 20131125 前言: 上一节学习了Spring在JavaProject中的配置,通过配置文件利用BeanFactory和ApplicationConte ...

  2. SpringBoot全家桶

    前言 Spring简化了Java的开发,而SpringBoot简化了Spring.本文用SpringBoot采用分层的结构整合了filter,aspect,mybaits,logback,redis, ...

  3. bzoj4945

    题解: 一眼看过去还以为是3-sat 其实d只有8 那么我们可以枚举每一个x选择哪一个 然后再用2-sat处理 代码: #include<bits/stdc++.h> using name ...

  4. web应用后台发生的事

    浏览器里输入网址后浏览器会通过访问的域名找出其IP地址,给Web服务器发送一个HTTP请求,得到一个服务的永久重定向响应,然后浏览器跟踪重定向地址,发送另一个请求,服务器接收到获取请求,然后处理并返回 ...

  5. Centos下找不到eth0设备的解决方法

    问题描述: ifconfig命令无法找到eth0设备,且/etc/sysconfig/network-scripts/中只有ifcfg-lo文件,而没有ifcfg-eth0. 临时解决方法一: 使用命 ...

  6. shell getopts学习

    #!/bin/bash while getopts i:vh name do case $name in i) opt=1 echo $OPTARG;; v) opt=2 echo 2;; h) op ...

  7. L151

    In Toothy Prequel, Piranha-Like Fish Menaced Jurassic Seas You can call it a prehistoric prequel.Sci ...

  8. Linux:cd命令详解

    cd 用来变更用户所在目录的 绝对路径:路径的写法一定由根目录 "/" 写起.例如 /usr/local/mysql 这就是绝对路径.相对路径:路径的写法不是由根目录 " ...

  9. SpringMVC札集(06)——转发和重定向

    自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onL ...

  10. ss-libev 源码解析udp篇 (1)

    shadowsocks-libev udp转发原理简介 ss_local作为一个sock5服务器,接收来自socks5客户端的数据包.在ss_local启动后,即创建一个udp socket,并bin ...