下面是一些其他的转换控制符:
l %d:读取一个十进制整数。
l %o、%x:读取一个八进制或十六进制整数。
l %f、%e、%g:读取一个浮点数。
l %c:读取一个字符(不会忽略空格)。
l %s:读取一个字符串。
l %[]:读取一个字符集合(见下面的说明)。
l %%:读取一个%字符。
类似于printf,scanf的转换控制符里也可以加上对输入数据字段宽度的限制。长度限定符(h对应于短整数,l对应于长整数)指明接收参数的长度是否比默认情况更短或更长。也就是说,%hd表示要读入一个短整数,%ld表示要读入一个长整数,而%lg表示要读入一个双精度浮点数。
以星号(*)打头的控制符表示对应位置上的输入数据将被忽略,也就是说,这个数据不会被保存,因此不需要使用一个变量来接收它。
我们使用%c控制符从输入中读取一个字符。它不会跳过起始的空白字符。
我们使用%s控制符来扫描字符串,但使用时必须小心。它会跳过起始的空白字符,但会在字符串里出现的第一个空白字符处停下来,所以,我们最好还是用它来读取单词而不是一般意义上的字符串。此外,如果没有使用字段宽限定符,它能够读取的字符串的长度是没有限制的,所以接收字符串必须有足够的空间来容纳输入流中可能的最长字符串。较好的选择是使用一个字段限制符,或者结合使用fgets和sscanf,从输入中读入一行数据,再对它进行扫描。这样可以避免可能被恶意用户利用而造成缓冲区溢出的情况。
我们使用%[]控制符读取一个由一个字符集合中的字符构成的字符串。格式字符串%[A-Z]将读取一个由大写字母构成的字符串。如果字符集中的第一个字符是^,就表示将读取一个由不属于该字符集合中的字符构成的字符串。因此读取一个其中带空格的字符串,并且在遇到第一个逗号时停止,可以用%[^,]。
给定下面输入行:
下面的scanf调用会正确读入四个数据项:
scanf函数的返回值是它成功读取的数据项个数,如果在读第一个数据项时失败了,返回值就将是零。如果在匹配第一个数据项之前就已经到达了输入的结尾,就会返回EOF。如果文件流发生读错误,流错误标志就会被设置并且错误变量errno将被设置以指明错误类型。详细情况请参考3.6.4节的内容。
一般来说,对scanf系列函数的评价并不高,这主要有三方面原因:
l 从历史来看,它们的具体实现都有漏洞。
l 它们的使用不够灵活。
l 使用它们编写的代码不容易看出究竟要读取什么。
尽量使用其他函数,如fread或fgets来读取输入行,再用字符串函数把输入分割成需要的数据项。
3.6.3 其他流函数
stdio函数库里还有一些其他的函数,它们使用流参数或标准流stdin、stdout和stderr:
l fgetpos:获得文件流的当前(读写)位置。
l fsetpos:设置文件流的当前(读写)位置。
l ftell:返回文件流当前(读写)位置的偏移值。
l rewind:重置文件流里的读写位置。
l freopen:重新使用一个文件流。
l setvbuf:设置文件流的缓冲机制。
l remove:相当于unlink函数,但如果它的path参数是一个目录的话,其作用就相当于rmdir函数。
所有这些库函数在man使用手册的第三节中都有说明。
你可以使用文件流函数重新实现前面的文件拷贝程序,这次使用库函数。请看下面的copy_stdio.c程序。
实验:另一个文件拷贝程序
这个程序与前面的版本很相似,但逐个字符的拷贝工作改为通过调用stdio.h头文件里定义的函数来完成: