当前位置: C语言 -- 附录 -- vscanf_s

vscanf_s函数


概要:
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdarg.h>
#include <stdio.h>
int vscanf_s(const char * restrict format,
      va_list arg);

描述:

vscanf_s函数等价于scanf_s函数,区别在于vscanf_s函数使用va_list类型参数arg替换可变参数列表。参数arg应已经使用宏va_start初始化(以及后续可能调用宏va_arg。);但vscanf_s函数不会调用va_end宏。

该函数从标准输入流中读取输入,参数format指向的字符串用于指定允许的输入序列以及如何转换输入序列用于赋值。

格式序列由0至多个指令构成,函数调用时按顺序执行格式序列中的指令。当所有指令执行完或者某个指令执行失败,函数将返回。函数调用失败分为两种情况:输入失败(input failures)和匹配失败(matching failures)。输入失败是由于发生编码错误或者输入字符不可用引起的;匹配失败是由于输入不当引起的。如果接收对象中的元素数量不足以容纳转换后的输入(包括任何尾随空字符。),将会发生匹配失败。

如果格式序列对应的参数数量不足,函数行为是未定义的;如果参数数量过多,多余参数会被评估,否则将被忽略。

该函数等价于vscanf函数,区别在于cs[转换说明符有差异(使用赋值屏蔽符*的情况除外),在vscanf_s函数中上述转换说明对应两个参数,第1个参数与vscanf函数相同,第2个参数的类型为rsize_t类型,指明第1个参数指向数组的元素数。如果第1个参数指向一个标量对象,它被认为是一个元素的数组。


运行约束:

参数format以及所有指向存储输入的对象的参数都不能是空指针。

如果存在运行约束冲突,vscanf_s函数不会尝试继续执行输入;发现运行约束冲突前,vscanf_s函数执行输入到什么程度,ISO/IEC 9899:2018标准未作明确说明。

由于实现可能会将未定义行为视为运行约束冲突,所以实现可能会将参数format指向字符串中实现不支持的转换说明作为运行约束冲突。


参数:
const char * restrict format

char类型指针,指向格式字符串,格式字符串应为多字节字符序列,其开始和结束都为初始移位状态(initial shift state)。构成格式序列的指令分为以下三种情况:

- 由空格字符(white-space character)构成的指令。

执行该指令时,函数从标准输入流中读取字符直至第一个非空格字符(第一个非空格字符不会被读取。),或者直至没有字符可以读取。空格字符可以使用isspace函数验证。单个空格字符能够匹配从标准输入流中读取的一个或者多个空格字符。该指令永远不会失败。

:默认环境中(即“C”语言环境),空格字符包括空格符(' ')、换页符('\f')、换行符('\n')、回车符('\r')、水平制表符('\t')和垂直制表符('\v')。


- 由普通多字节字符(字符%和空格字符除外)构成的指令。

执行该指令时,函数从标准输入流中读取字符,如果读取的字符与构成指令的字符不同,指令执行失败;不同字符以及其后字符保持未读取状态。如果到达文件末尾、发生编码错误或者读取错误导致无法读取字符,指令执行失败。


- 由转换说明构成的指令。

该指令定义了匹配的输入序列。该指令将按以下步骤执行:

除非转换说明包含[c或者n转换说明符,否则将跳过输入的空格字符。跳过的空格字符不计算在字段宽度内。

如果转换说明不包含转换说明符n,函数将从标准输入流中读取输入项(input item)。输入项是匹配的最长输入字符序列;在指定字段宽度的情况下,其长度不超过字段宽度。输入项后的第一个字符(如果存在的话。)保持未读取状态。如果输入项长度为0,指令执行失败。如果发生编码错误或者读取错误导致不能从标准输入流中读取输入,这类失败属于输入失败;否则将属于匹配失败。

除转换说明符为%的情况外,输入项将转换为与转换说明相对应类型。如果输入项不是匹配序列,指令执行失败;这种失败属于匹配失败。在未使用赋值屏蔽符*的情况下,转换结果将存储到format参数后、尚未接收转换结果的第一个参数指向的对象中。如果该对象不具备合适的类型,或者转换结果无法使用该对象表示,函数行为是未定义的。


转换说明遵循以下格式:

%赋值屏蔽符opt字段宽度符opt长度修饰符opt转换说明符

其中带opt的都是可选的。


1、赋值屏蔽符及其意义

赋值屏蔽符 描述
*

从标准输入流中读取数据,但该数据会被忽略;也就是说读取的数据不会被保存,例如:scanf_s("%d%*c%d", &one, &two);,假设输入是123a456,结果one的值为123two的值为456


2、字段宽度符及其意义

字段宽度符 描述
数字

该数字为大于0的十进制整数,用以指定最大字段宽度(字符形式)。


3、长度修饰符及其意义

长度修饰符 描述
hh

对于diouxXn转换说明符,该字符用于指向signed char或者unsigned char类型对象的指针参数。

h

对于diouxXn转换说明符,该字符用于指向short int或者unsigned short int类型对象的指针参数。

l

对于diouxXn转换说明符,该字符用于指向long int或者unsigned long int类型对象的指针参数。对于aAeEfFgG转换说明符,该字符用于指向double类型对象的指针参数。对于cs[转换说明符,该字符用于指向wchar_t类型对象的指针参数。

ll

对于diouxXn转换说明符,该字符用于指向long long int或者unsigned long long int类型对象的指针参数。

j

对于diouxXn转换说明符,该字符用于指向intmax_t或者uintmax_t类型对象的指针参数。

z

对于diouxXn转换说明符,该字符用于指向size_t或者对应的有符号整数类型对象的指针参数。

t

对于diouxXn转换说明符,该字符用于指向ptrdiff_t或者对应的无符号整数类型对象的指针参数。

L

对于aAeEfFgG转换说明符,该字符用于指向long double类型对象的指针参数。

如果长度修饰符与除上面指定的转换说明符一起使用,其行为是未定义的。


4、转换说明符及其意义

转换说明符 描述
d

匹配符号可选的十进制整数,其格式与基数为10strtol函数主题序列的预期格式相同。对应参数应为指向有符号整数的指针。

i

匹配符号可选的八进制、十进制或者十六进制整数,其格式与基数为0strtol函数主题序列的预期格式相同。对应参数应为指向有符号整数的指针。

o

匹配符号可选的八进制整数,其格式与基数为8strtoul函数主题序列的预期格式相同。对应参数应为指向无符号整数的指针。

u

匹配符号可选的十进制整数,其格式与基数为10strtoul函数主题序列的预期格式相同。对应参数应为指向无符号整数的指针。

x

匹配符号可选的十六进制整数,其格式与基数为16strtoul函数主题序列的预期格式相同。对应参数应为指向无符号整数的指针。

a、e、f、g

匹配符号可选的浮点数、无穷大或者非数值,其格式与strtod函数主题序列的预期格式相同。对应参数应为指向浮点数的指针。

c

匹配由字段宽度指定长度的字符序列;如果不存在字段宽度符,字段宽度为1

如果不存在l长度修饰符,对应的第1个参数应为一个指向字符数组初始元素的指针,并且指向的字符数组足以容纳字符序列,不会在数组末尾添加空字符。

如果存在l长度修饰符,输入应为初始移位状态(initial shift state)开始的多字节字符序列,序列中的每个多字节字符就像反复调用mbrtowc函数一样转换成宽字符,第一个多字节字符转换前,将mbstate_t对象描述的转换状态初始化为0。对应的第1个参数应为一个指向wchar_t类型数组初始元素的指针,并且指向的数组足以容纳转换后的宽字符序列,不会在数组末尾添加空宽字符。

s

匹配非空格字符序列。

如果不存在l长度修饰符,对应的第1个参数应为一个指向字符数组初始元素的指针,并且指向的字符数组足以容纳字符序列和终止空字符(终止空字符会被自动添加。)。

如果存在l长度修饰符,输入应为初始移位状态(initial shift state)开始的多字节字符序列,序列中的每个多字节字符就像反复调用mbrtowc函数一样转换成宽字符,第一个多字节字符转换前,将mbstate_t对象描述的转换状态初始化为0。对应的第1个参数应为一个指向wchar_t类型数组初始元素的指针,并且指向的数组足以容纳转换后的宽字符序列和终止空宽字符(终止空宽字符会被自动添加。)。

[

匹配非空字符序列,该序列中的字符均来源于指定字符集合(扫描集)。

如果不存在l长度修饰符,对应的第1个参数应为一个指向字符数组初始元素的指针,并且指向的字符数组足以容纳字符序列和终止空字符(终止空字符会被自动添加。)。

如果存在l长度修饰符,输入应为初始移位状态(initial shift state)开始的多字节字符序列,序列中的每个多字节字符就像反复调用mbrtowc函数一样转换成宽字符,第一个多字节字符转换前,将mbstate_t对象描述的转换状态初始化为0。对应的第1个参数应为一个指向wchar_t类型数组初始元素的指针,并且指向的数组足以容纳转换后的宽字符序列和终止空宽字符(终止空宽字符会被自动添加。)。

该转换说明符包括format字符串中的所有后续字符,直至并包括匹配的右括号]。括号间的字符([]之间的字符)构成指定字符集合(扫描集);如果左括号([)后是^符号,指定字符集合(扫描集)包括除^]之间字符以外的所有字符。如果转换说明符以[]或者[^]开头,右括号(])将包含在指定字符集合(扫描集)中,下一个右括号(])则是结束转换说明符的右括号(]);否则第一个右括号(])则是结束转换说明符的右括号(])。对于字符-,如果不属于[-[^--]这些情况,其行为将由实现定义。

p

匹配实现定义的序列集,该序列集与vprintf_s函数%p转换说明生成的序列集相同。对应参数应为void *类型指针。输入项以实现定义的方式转换为指针值。如果输入项是同一程序执行期间较早的转换值,则结果指针应等于该值;否则%p转换说明行为是未定义的。

n

没有输入被消耗。对应参数应为一个指向有符号整数的指针,通过调用此函数从标准输入流中读取的字符数将写入该整数。对于该转换说明符,没有参数被转换,但消耗一个参数。执行%n指令不会增加函数执行完成时返回的赋值总数。如果转换说明包含字段宽度符或者赋值屏蔽符,其行为是未定义的。

%

匹配单个%字符;不会发生转换或者赋值。完整转换说明应为%%


长度修饰符、转换说明符和对应的参数类型如下表所示:

d i u o x a e f g c s [] [^] p n
int * unsigned int * float * char * void ** int *
hh signed char * unsigned char * signed char *
h short int * unsigned short int * short int *
l long int * unsigned long int * double * wchar_t * long int *
ll long long int * unsigned long long int * long long int *
j intmax_t * uintmax_t * intmax_t *
z size_t * size_t * size_t *
t ptrdiff_t * ptrdiff_t * ptrdiff_t *
L long double *

该处类型为指向与size_t类型对应的有符号整数的指针类型。

该处类型为指向与size_t类型对应的有符号整数的指针类型。

该处类型为指向与ptrdiff_t类型对应的无符号整数的指针类型。


如果转换说明无效,函数行为是未定义的。

字符AEFGX都是有效的转换说明符,其行为与对应的aefgx转换说明符相同。

除非与指令匹配,否则尾随空格(包括换行符)将保持未读取状态。文字匹配和赋值屏蔽是否成功只能通过%n指令来确定。


va_list arg

va_list类型对象。


返回值:

如果存在运行约束冲突,或者转换前发生输入失败,函数返回宏EOF;否则函数返回赋值的输入项数目。如果发生匹配失败,赋值的输入项数目可能小于提供的输入项数目,甚至为0


范例:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
/*安全函数vscanf_s范例*/

#define __STDC_WANT_LIB_EXT1__ 1
#include <stdarg.h>
#include <stdio.h>

int getInteger(const char *format, ...)
{
    int count;
    va_list args;
    va_start(args, format);
    count = vscanf_s(format, args);
    va_end(args);

    return count;
}

int main(void)
{
    int integer[10];
    int number;

    number = getInteger("%i%i%i", integer, (integer+1), (integer+2));

    for(int i=0; i<number; ++i)
        printf_s("integer[%d]: %d\n", i, integer[i]);

    return 0;
}


结果:

假设键盘输入为:

101 0x101 -0101

将输出:

integer[0]: 101

integer[1}: 257

integer[2]: -65

注:使用Visual Studio编译。


相关内容:
vfscanf_s 从流中读取输入的安全函数。
vsscanf_s 从数组中读取输入的安全函数。