输出sprintf snprintf sprintf_s和输入scanf sscanf函数使用详解

news/2024/7/2 23:19:51

一、sprintf snprintf sprintf_s函数使用

众所周知,sprintf不能检查目标字符串的长度,可能造成众多安全问题,所以都会推荐使用snprintf.自从snprintf代替了sprintf,相信大家对snprintf的使用都不会少。

linux和windows下是不同的。linux下用的是snprintf();而windows下用的是_snprintf();

1、sprintf()

sprintf指的是字符串格式化命令.

函数声明为 
int sprintf(char *string, char *format [,argument,…]);
参数:
str为要写入的字符串;
format为格式化字符串,与printf()函数相同;
argument为变量。

sprintf 跟printf 在用法上几乎一样,只是打印的目的地不同而已, 前者打印到字符串中, 后者则直接在命令行上输出。

1、sprintf()把整数打印到字符串中

如:

sprintf(s, “%d”, 123); //把整数123打印成一个字符串保存在s中

sprintf(s, “%8x”, 4567); //小写16进制,宽度占8个位置,右对齐

2、打印16 进制内容时,通常想要一种左边补0 的等宽格式

sprintf(s, “%08X”, 4567); //产生:“000011D7”

很简单,在表示宽度的数字前面加个0 就可以了。

3、控制浮点数打印格式

浮点数的打印和格式控制是sprintf 的又一大常用功能,浮点数使用格式符”%f”控制,默认保留小数点后6 位数字,比如:

sprintf(s, “%f”, 3.1415926); //产生"3.141593"

4、控制打印的宽度和小数位数

”%m.nf”格式,其中m 表示打印的宽度,n 表示小数点后的位数。比如:

    sprintf(s, “%10.3f”, 3.1415626); //产生:" 3.142"
    sprintf(s, “%-10.3f”, 3.1415626); //产生:"3.142 "
    sprintf(s, “%.3f”, 3.1415626); //不指定总宽度,产生:“3.142”

C语言对数组进行操作时并不检测数组的长度,如果str的长度不够,sprintf()很容易造成缓冲区溢出,带来意想不到的后果,黑客经常利用这个弱点攻击看上去安全的系统:

#include <stdio.h>
main()
{
    char buf[10];
    sprintf(buf, "The length of the string is more than 10")
    printf("%s", buf);
}

运行结果为:”The length of the string is more than 10“,

同时系统提示程序已经停止。原因就是要写入的字符串的长度超过了buf的长度,造成缓冲区溢出。

使用snprintf()来代替sprintf()将能够很好的解决这个问题。

2、snprintf()

snprintf()函数用于将格式化的数据写入字符串

原型为: int snprintf(char *str, int n, char * format [, argument, …]);

参数: str为要写入的字符串;n为要写入的字符的最大数目,超过n会被截断;format为格式化字符串,与printf()函数相同;argument为变量。

返回值: 成功则返回参数str 字符串长度,失败则返回-1,错误原因存于errno 中。

snprintf()比sprintf()多了一个参数,能够控制要写入的字符串的长度,更加安全,只要稍加留意,不会造成缓冲区的溢出。

 #include <stdio.h> 
 int main()
 {
  char buffer[50];
  char* s = "runoobcom";
  int j = snprintf(buffer, 6, "%s\n", s);// 读取字符串并存储在 buffer 中
  printf("string:\n%s\ncharacter count = %d\n", buffer, j); // 输出 buffer及字符数
   return 0;
  }

输出结果

string:

abcde

character count = 10

对于返回值,需要注意的是snprintf的返回值是欲写入的字符串(即源字符串)长度,而不是实际写入的字符串度。如:

char test[8];
int ret= snprintf(test,5,"1234567890");
printf("%d|%s\n",ret,test);
#include <stdio.h>
 
int main () {
  char a[16];
  size_t i;
  char path[216] = {0};
  i = snprintf(a, 13, "%012d", 12345);  // 第 1 种情况
  printf("i = %lu, a = %s\n", i, a);    // 输出:i = 12, a = 000000012345
 
  i = snprintf(a, 9, "%012d", 12345);   // 第 2 种情况
  printf("i = %lu, a = %s\n", i, a);    // 输出:i = 12, a = 00000001
 
  i = snprintf(path, sizeof(path), "%s/%s.%s.so","/system/lib64/hw/love", "tanghanyue", "default");
  printf("i = %lu, path = %s\n", i, path);    // 输出:i = 43, path = /system/lib64/hw/love/tanghanyue.default.so
  return 0;
}

输出的结果为:

10|1234

3、sprintf_s

int sprintf_s(char *restrict buffer, rsize_t bufsz,

const char *restrict format, ...);

将数据格式化输出到字符串,sprintf_s()是sprintf()的安全版本,通过指定缓冲区长度来避免sprintf()存在的溢出风险。

sprintf_s原先只有windows的编译器才只支持,并不是C中的标准函数。

C11标准中加入了对该函数的支持,但是是可选的,并非强制加入。

C11中规定,如果编译器实现了__STDC_LIB_EXT1__ 宏,那么就要支持对该函数的实现。

gcc编译器只是部分的支持C11标准,本人测试在ubuntu的gcc 5.4.0版本中也没有实现__STDC_LIB_EXT1__ 。

gcc中可以用snprintf函数简单替代sprintf_s,但是注意2者在实现上是有一定的区别,不是完全相同。

int snprintf( char *restrict buffer, int bufsz,

const char *restrict format, ... );

二、scanf sscanf函数使用

1、 scanf()

scanf函数是一个标准库函数,它的函数原型在头文件“stdio.h”中。与printf函数相同,C语言也允许在使用scanf函数之前不必包含stdio.h文件。

scanf函数的一般形式为:scanf(“格式控制字符串”, 地址表列);

scanf(“%d”,&a);

&是一个取地址运算符,&a是一个表达式,其功能是求变量的地址。

表示输入数据的类型,其格式符和意义如下

%d: 输入十进制整数

%o: 输入八进制整数

%x: 输入十六进制整数

%u: 输入无符号十进制整数

%f或e: 输入实型数(用小数形式或指数形式)

%c: 输入单个字符

%s: 输入字符串

scanf(“%d %*d %d”,&a,&b);

用以表示该输入项,读入后不赋予相应的变量,即跳过该输入值。

当输入为:1 2 3时,把1赋予a,2被跳过,3赋予b。

scanf(“%d%d”,&r,&c);

将接受输入 10 20,但遇到 10,20 则失败。

2、 sscanf()

sscanf()的作用: 从一个字符串中读进与指定格式相符的数据.

**定义函数 int sscanf (const char *str,const char * format,…);**函数说明:

sscanf()会将参数str的字符串根据参数format字符串来转换并格式化数据。格式转换形式请参考scanf()。转换后的结果存于对应的参数内。

一、描述

sscanf通常被用来解析并转换字符串,其格式定义灵活多变,可以实现很强大的字符串解析功能。

sscanf的原型

#include <stdio.h>

int sscanf(const char *str, const char *format, ...);

str:待解析的字符串;

format:字符串格式描述;

其后是一序列数目不定的指针参数,存储解析后的数据.

二、示例用法

1. sscanf的基本用法

整形数转换

int year, month, day;
int converted = sscanf("20191103", "%04d%02d%02d", &year, &month, &day);
printf("converted=%d, year=%d, month=%d, day=%d/n", converted, year, month, day);

输出结果:

converted=3, year=2019, month=11, day=03

"%04d%02d%02d"是用来解析字符串的格式,%表示格式转换的开始,d表示转换为一个整数,04作为d的修饰,表示这是一个长度为4位的整数,不足4位时以0补齐。

例子返回结果等于3,表示有3个数据成功转换,转换成功数目同时取决于被解析的字符串以及其转换格式,如果我们把例子中的格式改为"%04d%02d",那么sscanf将只返回2,day的数值不会被sscanf更改。

浮点数转换

double longitude, latitude;
int converted = sscanf("113.123456789 31.123456789", "%lf %lf", &longitude, &latitude);
printf("converted=%d, longitude=%.9lf, latitude=%lf/n", converted, longitude, latitude);

输出结果:

converted=2, longitude=113.123456789, latitude=31.123457

sscanf的格式字符串中,f表示这是一个浮点数,其修饰词l表示这是一个double的浮点数。

2. sscanf的高级用法

数字+字符串

char str[32] = "";
sscanf("123456abcdedf", "%31[0-9]", str);
printf("str=%s/n", str);

输出结果:

str=123456

上面的格式中,[0-9]表示这是一个仅包含0-9这几个字符的字符串,前面使用数字31修饰词表示这个字符串缓冲区的最大长度(这也是sscanf最为人诟病的地方,很容易出现缓冲区溢出错误,实际上sscanf是可以避免出现缓冲区溢出的,只要在书写任何字符串解析的格式时,注意加上其缓冲区尺寸的限制)。

char str[32] = "";
sscanf("123456abcdedf", "%31[0-9a-z]", str);
printf("str=%s/n", str);

输出结果:

str=123456abcdedf

在格式[]中增加了a-z的描述。

使用^示例:

char str[32] = "";
sscanf("123456abcdedf", "%31[^a-z]", str);
printf("str=%s/n", str);

输出结果:

str=123456

在[]中增加表示相反的意思,上面的[a-z]表示一个不包含任何a-z之间的字符串。

使用*的例子:

char str[32] = "";
int ret = sscanf("123456abcdedf", "%*[0-9]%31[a-z]", str);
printf("ret=%d, str=%s/n",ret, str);

输出结果:

ret=1, str=abcdedf

加上*修饰表示一个被忽略的数据,同时也不需要为它准备空间存放解析结果。如上面的例子中,我们就只使用了str一个参数存放%31[a-z]的解析结果,而sscanf也只返回1,表示只解析了一个数据。

掌握了[], ^, *如何使用后,我们会发现sscanf原来是一个如此强大的工具,很多我们原先认为必须使用正则表达式的地方,很可能使用sscanf就可以实现。

sscanf的具体用法:

#include<stdio.h>
#include<stdlib.h>
#include<string.h> 
int main()
{
     char str[100];
     //用法一:取指定长度的字符串
     sscanf("12345","%4s",str);
     printf("str=%s",str);
     
      //用法二:格式化时间
     int year,month,day,hour,minute,second;
     sscanf("2021/10/18 14:55:34","%d/%d/%d %d:%d:%d",&year,&month,&day,&hour,&minute,&second);
     printf("time = %d-%d-%d %d:%d:%d\n",year,month,day,hour,minute,second); 
     
     //用法三:读入字符串
     sscanf("12345","%s",str);
     printf("str = %s\n",str);
     
     //用法四:%*d 和 %*s 加了星号 (*) 表示跳过此数据不读入. (也就是不把此数据读入参数中)
     sscanf("12345acc","%*d%s",str);
     printf("str = %s\n",str);
     sscanf("hello, world", "%*s%s", buf);
     printf("%s\n", buf);
     
     //用法五:取到指定字符为止的字符串。如在下例中,取遇到任意小写字母为止的字符串。
     sscanf("123456abcdedf","%[^a-z]",buf);
     printf("%s\n",buf);
     // %*s表示第一个匹配到的%s被过滤掉,即hello,被过滤了,如果没有空格则结果为NULL。
       
     //用法六:取到指定字符集为止的字符串。如在下例中,取遇到小写字母为止的字符串。
     sscanf("12345+acc121","%[^a-z]",str);
     printf("nstr = %s\n",str);
     
     return 0;
}

输出:

str=1234

time = 2021-10-18 14:55:34

str = 12345

str = acc

world

str = 123456

str = 12345+


http://lihuaxi.xjx100.cn/news/804043.html

相关文章

关于 JSON 数据格式的完全使用指南

前言 本文将对 JSON 的语法、解析、序列化进行详细的说明&#xff0c;帮助大家掌握 JSON 的使用方式。 如果文中有不对、疑惑或者错字的地方&#xff0c;欢迎在评论区留言指正&#x1f33b; 一、JSON简介 在 JSON 之前&#xff0c;XML 曾经一度成为互联网上传输数据的事实标…

Navicat Monitor 3.0 现已上市 | 欢迎下载试用

Navicat Monitor 3.0 现已上市Navicat Montior 3.0 现已发布&#xff01;一经发布&#xff0c;受到广大专业运维人员的关注与选择! 五大新亮点带给运维团队最为实用且有效地提升监控能力。其具备 PostgreSQL 服务器监控能力、支持优化慢查询、构建自定义指标、性能分析工具优化…

uniapp(一)

一、初识微信小程序1、什么是微信小程序微信小程序简称小程序&#xff0c;英文名Mini Program&#xff0c;是一种不需要下载安装即可使用的应用&#xff0c;它实现了应用“触手可及”的梦想&#xff0c;用户扫一扫或搜一下即可打开应用小程序是一种新的开放能力&#xff0c;开发…

JAVA打印数字二进制编码逻辑说明

在我们学习算法的过程中&#xff0c;我们首先必须要知道的就是数据(尤其是数字)类型在底层保存的方式。因为这样才能使我们的算法变的更加高效。 在JAVA中我们常用的数字类型是int类型&#xff0c;有过基础的同学应该知道int数据类型的长度为32bit。但实际使用时需要注意只有3…

PCI驱动程序框架

PCI驱动程序框架 文章目录PCI驱动程序框架参考资料&#xff1a;一、 PCI驱动框架二、 RK3399驱动致谢参考资料&#xff1a; 《PCI Express Technology 3.0》&#xff0c;Mike Jackson, Ravi Budruk; MindShare, Inc.《PCIe扫盲系列博文》&#xff0c;作者Felix&#xff0c;这是…

0201源码详解-ConcurrentHashMap-线程安全集合类-并发编程(Java)

文章目录1 概述2 主要属性及内部类3 构造函数3.1 无参3.2 带参3.3 tableSizeFor()4 get()-获取值4.1 get()主方法4.2 pread()方法4.3 Node hash值4.4 Node find()方法4.4.1 单链表结点4.4.2 ForwardingNode4.4.3 TreeBin4.4.4 TreeNode5 put()-放入键值对5.1 putVal()-放入键值…

分享12个面向前端开发人员的设计生产力工具

大家好&#xff0c;我们除了埋头写代码&#xff0c;有时候我们会有更多的选择&#xff0c;调整我们的开发和设计流程&#xff0c;借助这些生产力工具&#xff0c;可以大大提高我们的开发效率&#xff0c;好了废话不多说&#xff0c;今天我看了一篇关于这方面的文章&#xff0c;…

SQLServer数据库导出指定表里所有数据成insert语句

以sql server 2008 R2,数据库是Northwind数据库为例,导出指定表所有数据的sql脚本 目标:把Northwind数据库的Orders表导出成insert语句。1. 选择Northwind数据库,右键-任务-生成脚本:2. 在弹出的“生成和发布脚本”的简介窗口,按“下一步”按钮:3. 在“选择对象”窗口,…