22.1 流
说明:在C语言中,术语流意味着任意输入的源或任意输出的目的地。stdio.h
中的许多函数不仅可以处理表示成文件的流,还可以处理所有其它形式的流。
流:流常常表示为磁盘上的文件,但却可以和其它类型的设备相关联:调制解调器、网络端口、打印机、光盘驱动器等。
22.1.1 文件指针
文件指针(file pointer):File *
(File
定义在stdio.h
中)
用途:C程序中流的访问是通过文件指针
实现的
限制:操作系统通常会限制在任意某时刻可以打开的流的数量(但是一个程序中可以声明任意数量的File *
型变量)。
22.1.2 标准流和重定向
标准流
说明:由stdio.h
提供,一共3种
文件指针 | 流 | 默认的含义 |
---|---|---|
stdin | 标准输入 | 键盘 |
stdout | 标准输出 | 屏幕 |
stderr | 标准错误 | 屏幕 |
相关的函数:printf
、scanf
、putchar
、getchar
、puts
、gets
重定向
说明:某些操作系统(比如UNIX/Linux/DOS)允许通过所谓的重定向(redirextion)
机制来改变标准流默认的含义。
分类 | 说明 | 备注 |
---|---|---|
输入重定向(input redirection) | 使stdin流 表示为文件(而非键盘) |
程序不回意识到正在从文件读取数据 |
输出重定向(output redirection) | 使stdout流 和stderr流 表示为文件(而飞屏幕) |
程序不会意识到正在向文件中写数据 |
1 | // 输入重定向 |
22.1.3 文本文件与二进制文件
说明:stdio.h
支持两种类型的文件(文本文件和二进制文件)
存储方式:文本文件和二进制文件都是字节的序列,不同点在于存储的数据类型。
分类 | 数据类型(假设字符集为ASCII,16位机器) | 空间利用率 |
---|---|---|
文本文件(text file) | 字符(占一个字节) | 低 |
二进制文件(binary file) | 字符(占一个字节)、整数(两个字节)、浮点数(四个字节)等 | 高 |
结束符:
- DOS系统:文本文件和二进制文件不同
结束符分类 | 文本文件 | 二进制文件 |
---|---|---|
行的结尾 | 回行符+回车符 | 回行符 |
文件末尾 | Ctrl+Z(\x1a),但不是必需的(有的编辑器会加上 | 无 |
- UNIX系统:对文本文件和二进制文件不进行区分
结束符分类 | 说明 |
---|---|
行的结尾 | 换行符 |
文件末尾 | 无 |
注意:
- 在屏幕上显示文件内容的程序会假设文件为文本文件
- 复制文件时如果设定文件为文本文件,只会复制到出现文件末尾符出现的地方
技巧:在无法确定文件是文本文件还是二进制文件时,安全的做法是把文件假设为二进制文件。
22.2 文件操作
22.2.1 打开文件
fopen函数
说明:用流的方式打开文件
原型:stdio.h
1 | /** |
技巧:在DOS中的文件名中含有”\”字符要用”\替代”
注意:永远不能假设可以打开文件,为了确保不回返回空指针,需要测试fopen
函数的返回值。
1 | FILE *fp = fopen("c:\\project\\test1.dat", r); // 以只读方式打开 |
22.2.2 模式
说明:模式字符串依据文件是文本文件还是二进制文件分为两大类。
注意:可读且可写的模式(包含+
)存在如下限制
- 调用
文件定位函数
后,可读才能转换为可写 - 调用
文件定位函数
或fflush函数
后,可写才能转换为可读
文本文件
模式字符串 | 含义 | 是否需要文件存在 | 如果文件存在 |
---|---|---|---|
r | 可读 | 是 | |
w | 可写 | 否 | |
a | 追加 | 否 | |
r+ | 读和写 | 否 | 从文件头开始(追加到头部) |
w+ | 读和写 | 否 | 截去(覆盖) |
a+ | 读和写 | 否 | 追加 |
二进制文件
模式字符串 | 含义 | 是否需要文件存在 | 如果文件存在 |
---|---|---|---|
rb | 可读 | 是 | |
wb | 可写 | 否 | |
ab | 追加 | 否 | |
r+b或rb+ | 读和写 | 否 | 从文件头开始(追加到头部) |
w+b或wb+ | 读和写 | 否 | 截去(覆盖) |
a+b或ab+ | 读和写 | 否 | 追加 |
22.2.3 关闭文件
fclose函数
说明:关闭不再使用的文件
原型:stdio.h
1 | /** |
1 |
|
22.2.4 为流附加文件
freopen函数
说明:为已经打开的流附加一个不同的文件
应用:把文件和一个标准流相关联
原型:stdio.h
1 | /** |
1 | // 1.如果stdout通过命令行重定向或者freopen函数已经和其它文件关联,则先关闭与stdout相关联的文件 |
22.2.5 从命令行获取文件名
1 | /** |
1 | # argv[0]: demo |
22.2.6 程序:检查文件是否可以打开
说明:若文件存在就可以打开进行读入,在运行程序时,用户将给出要检测的文件的名字。
1 | /** |
1 | $ canopen f1.dat |
22.2.7 临时文件
说明:只在程序运行时存在的文件。stdio.h
提供了两个函数用来处理临时文件,即tmpfile
和tmpname
tmpfile函数
说明:产生临时文件,这些临时文件将存到文件关闭时或程序终止时。
原型:stdio.h
1 | /** |
1 | FILE *tmpptr; |
tmpfile函数
说明:为临时文件产生名字。
用途:解决tmpfile函数
无法知道临时文件的名字的问题。
原型:stdio.h
1 | /** |
参数为NULL
1 | char *filename; |
参数为字符数组
说明:tmpnam函数
会把声称的临时文件名复制到程序员提供的字符数组中,而且仍然会返回指向临时文件名的指针。
注意:作为参数的字符数组长度至少为L_tmpnam
,产生的临时文件名的最大数量不能超过TMP_MAX
。
L_tmpnam
:在stdio.h
中定义的一个宏,保存着临时文件名的字符数组的长度。TMP_MAX
:在stdio.h
中定义的一个宏,保存着程序执行期间tmpnam函数
产生的临时文件名的最大数量。
1 | char filename[L_tmpnam]; |
22.2.8 文件缓冲
说明:缓冲发生在屏幕的后台,而且通常不用担心它的操作。然而,极少的情况下可能需要我们承担更主动的作用,需要使用fflush函数
、setbuf函数
和setbuf函数
。
输入流缓存:从硬盘或磁盘读取,包含来自输入设备(键盘或磁盘)的数据
写入(输出)流缓存:向输出设备(屏幕或磁盘)写入,包含来自
fflush函数
说明:针对输出(写入)流,把缓冲区的内容传递给磁盘(当缓冲区满了或者关闭文件时,缓冲区会自动“清洗”)
原型:stdio.h
1 | /** |
1 | fflush(fp); // 为fp指向的文件 |
setvbuf函数
说明:改变缓冲流的方式,控制缓冲区的大小和位置
原型:stdio.h
1 | /** |
参数要点:
第N个参数 | 含义 | 说明 |
---|---|---|
2 | 缓冲区的地址 | |
3 | 缓冲区的类型 | 值为定义在stdio.h 中的宏 |
4 | 缓冲区的大小 | 较大的缓冲区可以提供更好的性能,而较小的缓冲区可以节约时间。 |
缓冲区的类型(宏) | 名字 | 说明 |
---|---|---|
_IOFBF | 满缓冲 | 当缓冲区为空时,从流读入数据;活着当缓冲区满时,向流写入数据 |
_IONLF | 行缓冲 | 每次从流读入数据活着直接向流写入数据 |
_IOLBF | 无缓冲 | 每次从流读入数据活着直接向流卸乳数据,而没有缓冲区 |
缓冲区的存储特点 | 说明 |
---|---|
静态存储期限 | 存在于程序运行的整个过程中 |
自动存储期限 | 允许在它的空间在块退出时被自动重声明(?) |
动态分配 | 在不需要时可以释放缓冲区 |
限制:必需在打开stream之后,在stream上执行任何操作之前调用setvbuf
。
1 | // 1.创建缓冲区 |
22.2.9 其他文件操作
remove函数
说明:根据文件名删除文件
原型:stdio.h
1 | /** |
1 | remove("foo"); // 删除名为foo的文件 |
rename函数
说明:文件重命名
原型:stdio.h
1 | /** |
限制:一定要确保在调用rename函数
之前文件是关闭的,否则无法对文件重命名。
1 | rename("foo", "bar"); |
22.3 格式化的输入/输出
22.3.1 …printf类函数
printf函数
说明:向stdout
输出,利用格式串控制输出的形式
原型:stdio.h
1 | /** |
1 | printf("Total: %d\n", total); |
fprintf函数
说明:和printf函数
唯一的不同就是,printf函数
始终向标准输出流stdout
向中写入,而fprintf函数
则向第一个参数说明的流(任何输出流)中写输出。
应用:向标准错误stderr
写出错信息。
扩展:stdio.h
中还有其他两种函数也可以向流写入格式化的输出,分别是vfprintf函数
和vprintf函数
,而且它们都还依赖stdarg.h
(26.1)。
原型:stdio.h
1 | /** |
1 | // 向磁盘中写入 |
22.3.2 …printf类函数的转换说明
说明:对已知的转换说明内容进行回顾,并把剩余的内容补充完整。
注意:格式串必需遵守规则编写,许多看似可能的转换说明(%le、%lf、%lg
等)实际上是无效的。
转换说明:%# 012.5Lg
% | # 0 | 12 | .5 | L | g |
---|---|---|---|---|---|
转换说明提示符 | ➊标志 | ➋最小字段宽度 | ➌精度 | ➍长度修饰符 | ➎转换说明符 |
必需 | 可选(可多于一个) | 可选 | 可选 | 可选 | 必需 |
转换说明提示符:标记格式串的开始
➊标志:设置对齐方式、前缀、进制、填充
标志 | 含义 |
---|---|
- |
左对齐 |
+ |
用+ 作为正数的前缀 |
空格 |
用空格 作为正数的前缀 |
# 0 、# 0x(X) |
# 0 (8进制)、# 0x(X) (16进制),转换说明g(G) 转换出的尾部0不能删除 |
0 |
除非转换说明为d、i、o、u、x(X) 且制定了精度,否则用前导0在字段宽度内进行填充 |
➋最小字段宽度:值为有效整数
或*
有效整数
:**字符数少于最小字段宽度时对字符填充,默认右对齐(在左侧填充空格
);大于最小字段宽度则完整显示。*
:格式串中的n个*
对应参数2
~参数n-1
,可以是宏
➌精度:值为.整数
或.*
(精度的含义即依赖于转换说明符,也依赖于自身的值)
说明符 | 精度值:.整数 |
精度值:.* |
---|---|---|
d、i、o、u、x(X) |
最小数字位数(如果数字位数少于精度值,则添加前导0) | 同最小字段宽度 ❷中* 的含义 |
e(E)、f |
小数点后的数字位数 | (同上) |
g(G) |
最大有效数字位 | (同上) |
s |
最大字符数 | (同上) |
➍长度修饰符:共3个,只能和一些转换说明符搭配
长度修饰符 | 转换说明 | 含义 |
---|---|---|
h |
整数(d、i、o、u、x(X)、n ) |
short int |
l(L) |
整数(d、i、o、u、x(X)、n ) |
long int |
l(L) |
e(E)、f、g(G) |
long double |
➎转换说明符:当对带有可变实参的函数(比如printf
)传参时,会发生默认的实际参数的提升。float
会转换为double
,char
会转换为int
。
转换说明符 | 对应实参类型 | 格式化后的形式 |
---|---|---|
d、i |
signed int | 十进制形式 |
o、u、x(X) |
unsigned int | o (8进制)、u (10进制)、x (16进制,a-f来显示)、X (16进制,A-F来显示) |
f |
double | 十进制形式(默认的精度为小数点后显示6位) |
e(E) |
double | 科学计数法表示的double,默认精度为小数点后显示6位(e 表示指数前为e,E 表示指数前为E) |
g(G) |
double | -4 >= 指数部分 < 精度值 则相当于e (对应g )或者E (对应G );否则相当于f |
c |
unsigned int | 无符号整数 |
s |
指向字符串的指针 | 按照void * 型显示,达到精度值(如果存在)或空字符(\0 )时停止写操作 |
p |
* | 转化为可显示格式的void * 型值 |
n |
int * (指向int型数的指针) |
...printf类函数 返回值(不会输出到屏幕,而是存储到所指向的int型数中) |
% |
无对应参数 | 字符串”%” |
22.3.3 …printf类函数的转换说明示例
标志➊
最小字段宽度➋ + 精度➌
转换说明➎
最小字符宽度❷和精度❸中*
1 | // 最小字符宽度❷和精度❸中*的用法示例 |
转换说明p
和n
1 | // p |
1 | // n |
22.3.4 …scanf类函数
scanf函数
说明:从stdin
(键盘)读入内容,根据格式串中的转换说明进行转换并存储在指针指定的位置上。
原型:stdio.h
1 | /** |
1 | // 循环读取一串整数,在首个"?"处停止 |
fscanf函数
说明:scanf函数
从stdin
读入数据,而fscanf函数
则从它自己的第一个实参所指定的流中妇孺内容。
原型:stdio.h
1 | /** |
22.3.5 …scanf类函数的格式化字符串
比较 | ...printf类函数 |
...scanf类函数 |
---|---|---|
格式串的作用 | 转换数据形式并拼接 | 模式匹配和数据类型转换 |
数据源 | 指定的实参(不定参数部分)的值 | stdin (键盘输入) |
数据源类型 | 多种类型 | 字符 |
格式串
例子:ISBN %d-%d-%ld-%d
ISBN | 空格 | %d | — | %d | — | %ld | — | %d |
---|---|---|---|---|---|---|---|---|
ISBN | 空白字符(0或多个) | 一个整数 | — | 一个整数 | — | 一个长整数 | — | 一个整数 |
允许整数前存在空白字符 | 允许整数前存在空白字符 | 允许整数前存在空白字符 | 允许整数前存在空白字符 |
转换说明
说明:类似...printf函数
格式串中中的转换说明。
特点:
- 大多数转换说明会在输入项的开始出跳过空白字符(
%[、%c、%n
除外) - 转换说明从来不回跳过尾部的空白字符(遇到换行符时,不会读区之并停止匹配返回)
空白字符
说明:格式串中的一个(或多个)空白字符匹配0个或多个输入流中的空白字符。
非空白字符
说明:非空白字符和输入流中相同字符进行匹配(%
除外)
22.3.6 …scanf类函数的转换说明
转换说明:%*12Ld
% | * | 12 | L | d |
---|---|---|---|---|
转换说明提示符 | ➊赋值屏蔽 | ➋最小字段宽度 | ➌长度修饰符 | ❹转换说明符 |
必需 | 可选 | 可选 | 可选 | 必需 |
❶赋值屏蔽(assignment suppression):使用符号*
,匹配空白符之外的连续字符,直到遇到空白符为止。
- 匹配的数据项会被读入,但不会被赋值给变量
- 用*匹配到的数据相不回包含在
...scanf类函数
返回的计数中
❷最大字段宽度:限制转换说明匹配的输入项的字符数量(不计算跳过的空白符),达到限制的字符数量后便停止当前输入项的转换。
❸长度修饰符:共3个,只能和一些转换说明符搭配,同时长度修饰符的选择取决于要存储为相匹配实参所指向的变量类型。
长度修饰符 | 转换说明 | 对应实参(指针)的类型 |
---|---|---|
h |
整数(d、i、o、u、x(X)、n ) |
short int |
l(L) |
整数(d、i、o、u、x(X)、n ) |
long int |
l |
e(E)、f、g(G) |
double |
L |
e(E)、f、g(G) |
long double |
❹转换说明符:
转换说明符 | 对应实参指针应当指向类型 | 匹配的字符 |
---|---|---|
d | 整数 | 十进制整数 |
i | 整数 | 自动判断进制(0打头:8进制;0x(X)打头:16进制;否则十进制) |
o | unsigned int | 八进制整数 |
u | unsigned int | 十进制整数 |
x(X) | unsigned int | 十六进制整数 |
e(E)、f、g(G) | float | float型小数 |
s | char *(自动在末尾添加\0 ) |
一系列非空白字符 |
[ | char *(自动在末尾添加\0 ) |
来自扫描集合 的非空字符序列。扫描集合 可以包含任何字符集,特别的是如果扫描集合 中包含] ,则要放在首位,例如[]abc] ([abc] :表示匹配只含有字母a、b、c 的字符序列;[^abc] :表示匹配a、b、c 都不存在的字符序列) |
c | char *(指定❷最大字段宽度 n,则在末尾添加\0 ,否则不添加) |
指定❷最大字段宽度 n,则匹配n个字符,否则旧就匹配一个字符 |
p | void * | ...printf类函数 可以打印出的指针值(地址) |
n | int(不指定❸长度修饰符 )、short int(❸长度修饰符 为h )、long int(❸长度修饰符 为l ) |
不匹配任何字符,因而也不回影响...scanf类函数 的放回值(对应的变量存储的是到目前为止已经读入的字符数) |
% | char | 匹配字符% |
strtol函数(26.2.1)
说明:将字符串根据参数base来转换成长整型数
原型:stdio.h
1 | /** |
和...scanf类函数
转换说明符之间的对应关系
转换说明符 | strol的参数base的值 |
---|---|
d | 10 |
i | 0 |
o | 8 |
u | 10 |
x(X) | 16 |
e(E)、f、g(G) | 10 |
22.3.7 …scanf函数的示例
转换说明、空白字符、非空白字符组合效果
赋值屏蔽和指定字段宽度效果
难懂的转换说明:i、[、n
22.3.8 检测文件末尾和错误条件
错误指示器(error indicator):打开流时被清除,遇到错误时会被设置。
文件末尾指示器(end of file indicator):打开流时被清除,遇到文件末尾时被设置。...scanf类函数
出错分类:当...scanf类函数
的返回值小于不定参数(要匹配)的数量时,由3中可能
- 提前遇到文件末尾:函数在完全匹配格式串之前遇到了文件末尾
- 匹配失败:数据相的格式错误(比如函数在搜索整数的第一个数字期间遇到了一个字母)
- 错误:错误的发生超出了函数控制的范围
clearerr函数
说明:清除文件末尾指示器和错误指示器
注意:Q&A
某些其他库函数因为副作用可以清除某种指示器或两种都可以清除,所以不回需要经常使用该函数。
原型:stdio.h
1 | /** |
feof函数
说明:检测末尾指示器
,判断是否已经到达输入流(文件或stdin
)末尾。
原型:stdio.h
1 | /** |
ferror函数
说明:检测错误指示器
,判断输入过程是否发生错误
原型:stdio.h
1 | /** |
1 | /** |
1 | // 调用 |
22.4 字符的输入/输出
说明:本节的所有函数用于文本流和二进制流是等效的。
22.4.1 输出函数
说明:和...scanf类函数
一样,fputc、putc、putchar
出现错误都会为流设置错误指示器
并返回EOF
。
fputc函数
说明:向任意流写字符(putc
和putchar
更通用的版本)
原型:stdio.h
1 | /** |
putc宏
说明:向任意流写字符(宏实现)
优点和缺点:14.3
原型:stdio.h
1 | /** |
putchar宏
说明:向标准输出流stdout
(屏幕)写一个字符,通常作为宏来实现(底层是fputc
,因此性能不如putc
)。
原型:stdio.h
1 |
1 | /** |
22.4.2 输入函数
注意:Q&A
把char型变量与EOF
比较可能会产生错误的结果。
getc宏
说明:从指定流中读入一个字符(宏实现)。
原型:stdio.h
1 | /** |
1 | // 从文件读入字符直到遇到文件末尾 |
fgetc函数
说明:从指定的流中读如一个字符。
原型:stdio.h
1 | /** |
1 | ch = fgetc(fp); // 从fp中读取一个字符 |
getchar(宏)
说明:从标准输出流stdout
(键盘)中获得一个字符(通常是用宏实现)。
原型:stdio.h
1 |
22.4.3 程序:复制文件
说明:采用”rb”和”wb”作为文件的模式使fcopy
程序既可以复制文本文件也可以复制二进制文件。
1 | /** |
1 | $ ./fcopy source.dat dest.dat |
22.5 行的输入/输出
说明:读和写行的苦函数(虽然也可以有效地用于二进制文件流,但多数用于文本流)。
22.5.1 输出函数
puts函数(13.3)
说明:向标准输出流stdout
写入一串字符(总会在后面添加一个换行符)。
原型:stdio.h
1 | /** |
1 | puts("Hi, there!"); // 向stdout(屏幕)输出"Hi, there!" |
fputs函数
说明:向指定的流输出一串字符(不会在后面自动添加换行符)
原型:stdio.h
1 | /** |
1 | fputs("Hi, there!", fp); // 向fp输出"Hi. there!" |
22.5.2 输入函数
返回值:无论gets
还是fgets
,如果出现了错误,活着使在存储人和字符之前大道了输入流的末尾,都会返回空指针NULL
;否则,返回指向读入字符串的指针。
末尾空字符:两个函数都会在字符串的末尾存储空字符。
技巧:大多数情况下用fgets
而不是gets
(只有在确保读入的字符正好适合数组大小时才使用),因为后者会超出接收数组范围的可能
gets函数(13.3)
说明:从标准输入流stdin
中读取一串字符(逐个读取字符,并且把它们存储在字符串中,直到读取到换行符为止,因此不存储换行符)
注意:只有在确保读入的字符正好适合数组大小时才使用,因为会有超出接收数组范围的可能。
原型:stdio.h
1 | /** |
fgets函数
说明:从指定的流中读取一串字符(有时会存储换行符)
原型:stdio.h
1 | /** |
优点:
- 可以从任意流中读取信息
- 比
gets
更加安全,因为可以限制要存储的字符的数量
1 | // 逐个读入字符,在遇到首个换行符时或已经读入sizeof(str)-1个字符时结束操作 |
22.6 块的输入/输出
说明:fread
和fwrite
允许程序在一次读和写大的数据块(任意数据类型)。
用途:当程序在终止之前使用fwrite函数
把数据存储到文件中,稍后,程序可以把数据从文件读入到内存。
注意:Q&A
小心使用fread
函数和fwrite
函数可以用于文本流,但它们主要还是用于二进制的流。
fread函数
说明:从流读入数据块。
原型:stdio.h
1 | /** |
1 | // 从fp指向的流位置读出数组并存储到数组a中 |
fwrite函数
说明:把内存中的数据块复制到流。
原型:stdio.h
1 | /** |
1 | // 将内存中数组a复制到fp指向的流(磁盘文件)中 |
22.7 文件的定位
说明:stdio.h
提供了5个函数允许程序确定当前的文件位置或者改变文件位置,通过这些函数可以实现文件的随机访问(任意访问)。
注意:文件定为函数最适合二进制文件,处理文本流可能出现操作系统差异。
文件位置(file position):每个流都由文件位置,可以看作当前访问到的位置。在执行读或者写操作时,文件位置会自动推进。
fseek函数
说明:改变指定流的文件位置,相关的宏有3个
- SEEK_SET:文件的起始处
- SEEK_CUR:文件的当前位置
- SEEK_END:文件的末尾处
注意:fseek函数对流是文本型
还是二进制型
非常敏感
- 文本型:参数必须是以下两种情景之一
- offset必须为0(即只能移动到文件的起始处或末尾)
- whence必须是
SEEK_SET
,且offset是通过ftell函数
获取的(即返回前一次访问到的位置)
- 二进制型:不要求支持whience是SEEK_END
原型:stdio.h
1 | /** |
1 | // 文件位置移动到文件起始处 |
ftell函数
说明:以长整型返回当前文件位置
注意:二进制文件
和文本文件
的返回值情况有所不同
- 二进制文件:以字节计算返回当前位置
- 文本文件:不一定按照字节计数
用途:可能会存储返回的值并且稍后将其提供给fseek函数
原型:stdio.h
1 | /** |
rewind函数
说明:把文件位置设置到文件起始处,几乎等价于fseek(fp, 0L, SEEK_SET)
,差异是该函数没有返回值,但会为fp清除掉错误指示器
。
1 | /** |
fgetpos函数
说明:将指定流的文件位置存储到fpos_t型变量中
原型:
1 | /** |
fsetpos函数
说明:为指定流设置文件fpos_t型位置
原型:stdio.h
1 | /** |
1 | fpos_t file_pos; |
程序:修改零件记录文件
1 | /** |
22.8 字符串的输入/输出
扩展:还有一个依赖stdarg.h
定义va_list
的vsprintf函数
(26.1.2)
sprintf函数
说明:类似printf函数
和fprintf函数
,唯一的不同是该函数会把输出写入指定字符数组而不是流中(会在末尾添加一个空字符)。
原型:stdio.h
1 | /** |
1 | sprintf(str, "%d/%d/%d", 9, 20, 94); // str: 9/20/94 |
sscanf函数
说明:类似scanf函数
和fscanf函数
,唯一的不同是该函数是从字符数组而不是流中读取数据。
原型:stdio.h
1 | /** |
1 | // 使用fgets函数来获取一行输入,然后把此行数据传递给scanf函数进一步处理 |
1 | // 从字符串中读取日期 |