C语言标准I/O系列的7个函数
liebian365 2024-11-21 17:35 15 浏览 0 评论
ANSI标准库的标准I/O系列有几十个函数。虽然在这里无法一一列举,但是我们会简要地介绍一些,让读者对它们有一个大概的了解。这里列出函数的原型,表明函数的参数和返回类型。我们要讨论的这些函数,除了setvbuf(),其他函数均可在ANSI之前的实现中使用。参考资料V的“新增C99和C11的标准ANSI-C库”中列出了全部的ANSI C标准I/O包。
1 int ungetc(int c, FILE *fp)函数
int ungetc()函数把c指定的字符放回输入流中。如果把一个字符放回输入流,下次调用标准输入函数时将读取该字符(见图13.2)。例如,假设要读取下一个冒号之前的所有字符,但是不包括冒号本身,可以使用getchar()或getc()函数读取字符到冒号,然后使用ungetc()函数把冒号放回输入流中。ANSI C标准保证每次只会放回一个字符。如果实现允许把一行中的多个字符放回输入流,那么下一次输入函数读入的字符顺序与放回时的顺序相反。
2 int fflush()函数
fflush()函数的原型如下:
int fflush(FILE *fp);
调用fflush()函数引起输出缓冲区中所有的未写入数据被发送到fp指定的输出文件。这个过程称为刷新缓冲区。如果fp是空指针,所有输出缓冲区都被刷新。在输入流中使用fflush()函数的效果是未定义的。只要最近一次操作不是输入操作,就可以用该函数来更新流(任何读写模式)。
3 int setvbuf()
函数setvbuf()函数的原型是:
int setvbuf(FILE * restrict fp, char * restrict buf, int mode, size_t size);
setvbuf()函数创建了一个供标准I/O函数替换使用的缓冲区。在打开文件后且未对流进行其他操作之前,调用该函数。指针fp识别待处理的流,buf指向待使用的存储区。如果buf的值不是NULL,则必须创建一个缓冲区。例如,声明一个内含1024个字符的数组,并传递该数组的地址。然而,如果把NULL作为buf的值,该函数会为自己分配一个缓冲区。变量size告诉setvbuf()数组的大小(sizet是一种派生的整数类型,第5章介绍过)。mode的选择如下:IOFBF表示完全缓冲(在缓冲区满时刷新);IOLBF表示行缓冲(在缓冲区满时或写入一个换行符时);IONBF表示无缓冲。如果操作成功,函数返回0,否则返回一个非零值。 假设一个程序要存储一种数据对象,每个数据对象的大小是3000字节。可以使用setvbuf()函数创建一个缓冲区,其大小是该数据对象大小的倍数。
4 二进制I/O:fread()和fwrite()
介绍fread()和fwrite()函数之前,先要了解一些背景知识。之前用到的标准I/O函数都是面向文本的,用于处理字符和字符串。如何在文件中保存数值数据?用fprintf()函数和%f转换说明只是把数值保存为字符串。例如,下面的代码:
double num = 1./3.; fprintf(fp,"%f", num);
把num存储为8个字符:0.333333。使用%.2f转换说明将其存储为4个字符:0.33,用%.12f转换说明则将其存储为14个字符:0.333333333333。改变转换说明将改变存储该值所需的空间数量,也会导致存储不同的值。把num存储为0.33后,读取文件时就无法将其恢复为更高的精度。一般而言,fprintf()把数值转换为字符数据,这种转换可能会改变值。 为保证数值在存储前后一致,最精确的做法是使用与计算机相同的位组合来存储。因此,double类型的值应该存储在一个double大小的单元中。如果以程序所用的表示法把数据存储在文件中,则称以二进制形式存储数据。不存在从数值形式到字符串的转换过程。对于标准I/O,fread()和fwrite函数用于以二进制形式处理数据。
实际上,所有的数据都是以二进制形式存储的,甚至连字符都以字符码的二进制表示来存储。如果文件中的所有数据都被解释成字符码,则称该文件包含文本数据。如果部分或所有的数据都被解释成二进制形式的数值数据,则称该文件包含二进制数据(另外,用数据表示机器语言指令的文件都是二进制文件)。 二进制和文本的用法很容易混淆。ANSI-C和许多操作系统都识别两种文件格式:二进制和文本。能以二进制数据或文本数据形式存储或读取信息。可以用二进制模式打开文本格式的文件,可以把文本存储在二进制形式的文件中。可以调用getc()拷贝包含二进制数据的文件。然而,一般而言,用二进制模式在二进制格式文件中存储二进制数据。类似地,最常用的还是以文本格式打开文本文件中的文本数据(通常文字处理器生成的文件都是二进制文件,因为这些文件中包含了大量非文本信息,如字体和格式等)。
5 sizet fwrite()
函数fwrite()函数的原型如下:
size_t fwrite(const void * restrict ptr, size_t size, size_t nmemb,
FILE * restrict fp);
fwrite()函数把二进制数据写入文件。sizet是根据标准C类型定义的类型,它是sizeof运算符返回的类型,通常是unsignedint,但是实现可以选择使用其他类型。指针ptr是待写入数据块的地址。size表示待写入数据块的大小(以字节为单位),nmemb表示待写入数据块的数量。和其他函数一样,fp指定待写入的文件。例如,要保存一个大小为256字节的数据对象(如数组),可以这样做:
char buffer[256];
fwrite(buffer, 256, 1, fp);
以上调用把一块256字节的数据从buffer写入文件。另举一例,要保存一个内含10个double类型值的数组,可以这样做:
double earnings[10];
fwrite(earnings, sizeof (double), 10, fp);
以上调用把earnings数组中的数据写入文件,数据被分成10块,每块都是double的大小。 注意fwrite()原型中的const void * restrict ptr声明。fwrite()的一个问题是,它的第1个参数不是固定的类型。例如,第1个例子中使用buffer,其类型是指向char的指针;而第2个例子中使用earnings,其类型是指向double的指针。在ANSI C函数原型中,这些实际参数都被转换成指向void的指针类型,这种指针可作为一种通用类型指针(在ANSI C之前,这些参数使用char *类型,需要把实参强制转换成char *类型)。 fwrite()函数返回成功写入项的数量。正常情况下,该返回值就是nmemb,但如果出现写入错误,返回值会比nmemb小。
6 sizet fread()函数
sizet fread()函数的原型如下:
size_t fread(void * restrict ptr, size_t size, size_t nmemb,
FILE * restrict fp);
fread()函数接受的参数和fwrite()函数相同。在fread()函数中,ptr是待读取文件数据在内存中的地址,fp指定待读取的文件。该函数用于读取被fwrite()写入文件的数据。例如,要恢复上例中保存的内含10个double类型值的数组,可以这样做:
double earnings[10];
fread(earnings, sizeof (double), 10, fp);
该调用把10个double大小的值拷贝进earnings数组中。fread()函数返回成功读取项的数量。正常情况下,该返回值就是nmemb,但如果出现读取错误或读到文件结尾,该返回值就会比nmemb小。
7 int feof(FILE *fp)和int ferror(FILE*fp)函数
如果标准输入函数返回EOF,则通常表明函数已到达文件结尾。然而,出现读取错误时,函数也会返回EOF。feof()和ferror()函数用于区分这两种情况。当上一次输入调用检测到文件结尾时,feof()函数返回一个非零值,否则返回0。当读或写出现错误,ferror()函数返回一个非零值,否则返回0。
8 一个程序示例
接下来,我们用一个程序示例说明这些函数的用法。该程序把一系列文件中的内容附加在另一个文件的末尾。该程序存在一个问题:如何给文件传递信息。可以通过交互或使用命令行参数来完成,我们先采用交互式的方法。下面列出了程序的设计方案。
- 询问目标文件的名称并打开它。
- 使用一个循环询问源文件。
- 以读模式依次打开每个源文件,并将其添加到目标文件的末尾。
为演示setvbuf()函数的用法,该程序将使用它指定一个不同的缓冲区大小。下一步是细化程序打开目标文件的步骤:
1.以附加模式打开目标文件;
2.如果打开失败,则退出程序;
3.为该文件创建一个4096字节的缓冲区;
4.如果创建失败,则退出程序。
与此类似,通过以下具体步骤细化拷贝部分:
1.如果该文件与目标文件相同,则跳至下一个文件;
2.如果以读模式无法打开文件,则跳至下一个文件;
3.把文件内容添加至目标文件末尾。
最后,程序回到目标文件的开始处,显示当前整个文件的内容。 作
为练习,我们使用fread()和fwrite()函数进行拷贝。程序append.c给出了这个程序
/* append.c -- appends files to a file */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 4096
#define SLEN 81
void append(FILE *source, FILE *dest);
char * s_gets(char * st, int n);
int main(void)
{
FILE *fa, *fs; // fa for append file, fs for source file
int files = 0; // number of files appended
char file_app[SLEN]; // name of append file
char file_src[SLEN]; // name of source file
int ch;
puts("Enter name of destination file:");
s_gets(file_app, SLEN);
if ((fa = fopen(file_app, "a+")) == NULL)
{
fprintf(stderr, "Can't open %sn", file_app);
exit(EXIT_FAILURE);
}
if (setvbuf(fa, NULL, _IOFBF, BUFSIZE) != 0)
{
fputs("Can't create output buffern", stderr);
exit(EXIT_FAILURE);
}
puts("Enter name of first source file (empty line to quit):");
while (s_gets(file_src, SLEN) && file_src[0] != '0')
{
if (strcmp(file_src, file_app) == 0)
fputs("Can't append file to itselfn",stderr);
else if ((fs = fopen(file_src, "r")) == NULL)
fprintf(stderr, "Can't open %sn", file_src);
else
{
if (setvbuf(fs, NULL, _IOFBF, BUFSIZE) != 0)
{
fputs("Can't create input buffern",stderr);
continue;
}
append(fs, fa);
if (ferror(fs) != 0)
fprintf(stderr,"Error in reading file %s.n",
file_src);
if (ferror(fa) != 0)
fprintf(stderr,"Error in writing file %s.n",
file_app);
fclose(fs);
files++;
printf("File %s appended.n", file_src);
puts("Next file (empty line to quit):");
}
}
printf("Done appending. %d files appended.n", files);
rewind(fa);
printf("%s contents:n", file_app);
while ((ch = getc(fa)) != EOF)
putchar(ch);
puts("Done displaying.");
fclose(fa);
?
return 0;
}
?
void append(FILE *source, FILE *dest)
{
size_t bytes;
static char temp[BUFSIZE]; // allocate once
?
while ((bytes = fread(temp,sizeof(char),BUFSIZE,source)) > 0)
fwrite(temp, sizeof (char), bytes, dest);
}
?
char * s_gets(char * st, int n)
{
char * ret_val;
char * find;
?
ret_val = fgets(st, n, stdin);
if (ret_val)
{
find = strchr(st, 'n'); // look for newline
if (find) // if the address is not NULL,
*find = '0'; // place a null character there
else
while (getchar() != 'n')
continue;
}
return ret_val;
}
?
如果setvbuf()无法创建缓冲区,则返回一个非零值,然后终止程序。可以用类似的代码为正在拷贝的文件创建一块4096字节的缓冲区。把NULL作为setvbuf()的第2个参数,便可让函数分配缓冲区的存储空间。 该程序获取文件名所用的函数是sgets(),而不是scanf(),因为scanf()会跳过空白,因此无法检测到空行。该程序还用sgets()代替fgets(),因为后者在字符串中保留换行符。 以下代码防止程序把文件附加在自身末尾:
if (strcmp(file_src, file_app) == 0)
fputs("Can't append file to itselfn",stderr);
参数fileapp表示目标文件名,filesrc表示正在处理的文件名。append()函数完成拷贝任务。该函数使用fread()和fwrite()一次拷贝4096字节,而不是一次拷贝1字节:
void append(FILE *source, FILE *dest)
{
size_t bytes;
static char temp[BUFSIZE]; // allocate once
?
while ((bytes = fread(temp,sizeof(char),BUFSIZE,source)) > 0)
fwrite(temp, sizeof (char), bytes, dest);
}
因为是以附加模式打开由dest指定的文件,所以所有的源文件都被依次添加至目标文件的末尾。注意,temp数组具有静态存储期(意思是在编译时分配该数组,不是在每次调用append()函数时分配)和块作用域(意思是该数组属于它所在的函数私有)。 该程序示例使用文本模式的文件。使用"ab+"和"rb"模式可以处理二进制文件。
9 用二进制I/O进行随机访问
随机访问是用二进制I/O写入二进制文件最常用的方式,我们来看一个简短的例子。程序randbin.c中的程序创建了一个存储double类型数字的文件,然后让用户访问这些内容。
Listing 13.6 The randbin.c Program
/* randbin.c -- random access, binary i/o */
#include <stdio.h>
#include <stdlib.h>
#define ARSIZE 1000
?
int main()
{
double numbers[ARSIZE];
double value;
const char * file = "numbers.dat";
int i;
long pos;
FILE *iofile;
?
// create a set of double values
for(i = 0; i < ARSIZE; i++)
numbers[i] = 100.0 * i + 1.0 / (i + 1);
// attempt to open file
if ((iofile = fopen(file, "wb")) == NULL)
{
fprintf(stderr, "Could not open %s for output.n", file);
exit(EXIT_FAILURE);
}
// write array in binary format to file
fwrite(numbers, sizeof (double), ARSIZE, iofile);
fclose(iofile);
if ((iofile = fopen(file, "rb")) == NULL)
{
fprintf(stderr,
"Could not open %s for random access.n", file);
exit(EXIT_FAILURE);
}
// read selected items from file
printf("Enter an index in the range 0-%d.n", ARSIZE - 1);
while (scanf("%d", &i) == 1 && i >= 0 && i < ARSIZE)
{
pos = (long) i * sizeof(double); // calculate offset
fseek(iofile, pos, SEEK_SET); // go there
fread(&value, sizeof (double), 1, iofile);
printf("The value there is %f.n", value);
printf("Next index (out of range to quit):n");
}
// finish up
fclose(iofile);
puts("Bye!");
?
return 0;
}
首先,该程序创建了一个数组,并在该数组中存放了一些值。然后,程序以二进制模式创建了一个名为numbers.dat的文件,并使用fwrite()把数组中的内容拷贝到文件中。内存中数组的所有double类型值的位组合(每个位组合都是64位)都被拷贝至文件中。不能用文本编辑器读取最后的二进制文件,因为无法把文件中的值转换成字符串。然而,存储在文件中的每个值都与存储在内存中的值完全相同,没有损失任何精确度。此外,每个值在文件中也同样占用64位存储空间,所以可以很容易地计算出每个值的位置。 程序的第2部分用于打开待读取的文件,提示用户输入一个值在数组中的索引。程序通过把索引值和double类型值占用的字节相乘,即可得出文件中的一个位置。然后,程序调用fseek()定位到该位置,用fread()读取该位置上的数据值。注意,这里并未使用转换说明。fread()从已定位的位置开始,拷贝8字节到内存中地址为&value的位置。然后,使用printf()显示value。下面是该程序的一个运行示例:
Enter an index in the range 0-999.
500
The value there is 50000.001996.
Next index (out of range to quit):
900
The value there is 90000.001110.
Next index (out of range to quit):
0
The value there is 1.000000.
Next index (out of range to quit):
-1
Bye!
相关推荐
- 4万多吨豪华游轮遇险 竟是因为这个原因……
-
(观察者网讯)4.7万吨豪华游轮搁浅,竟是因为油量太低?据观察者网此前报道,挪威游轮“维京天空”号上周六(23日)在挪威近海发生引擎故障搁浅。船上载有1300多人,其中28人受伤住院。经过数天的调...
- “菜鸟黑客”必用兵器之“渗透测试篇二”
-
"菜鸟黑客"必用兵器之"渗透测试篇二"上篇文章主要针对伙伴们对"渗透测试"应该如何学习?"渗透测试"的基本流程?本篇文章继续上次的分享,接着介绍一下黑客们常用的渗透测试工具有哪些?以及用实验环境让大家...
- 科幻春晚丨《震动羽翼说“Hello”》两万年星间飞行,探测器对地球的最终告白
-
作者|藤井太洋译者|祝力新【编者按】2021年科幻春晚的最后一篇小说,来自大家喜爱的日本科幻作家藤井太洋。小说将视角放在一颗太空探测器上,延续了他一贯的浪漫风格。...
- 麦子陪你做作业(二):KEGG通路数据库的正确打开姿势
-
作者:麦子KEGG是通路数据库中最庞大的,涵盖基因组网络信息,主要注释基因的功能和调控关系。当我们选到了合适的候选分子,单变量研究也已做完,接着研究机制的时便可使用到它。你需要了解你的分子目前已有哪些...
- 知存科技王绍迪:突破存储墙瓶颈,详解存算一体架构优势
-
智东西(公众号:zhidxcom)编辑|韦世玮智东西6月5日消息,近日,在落幕不久的GTIC2021嵌入式AI创新峰会上,知存科技CEO王绍迪博士以《存算一体AI芯片:AIoT设备的算力新选择》...
- 每日新闻播报(September 14)_每日新闻播报英文
-
AnOscarstatuestandscoveredwithplasticduringpreparationsleadinguptothe87thAcademyAward...
- 香港新巴城巴开放实时到站数据 供科技界研发使用
-
中新网3月22日电据香港《明报》报道,香港特区政府致力推动智慧城市,鼓励公私营机构开放数据,以便科技界研发使用。香港运输署21日与新巴及城巴(两巴)公司签署谅解备忘录,两巴将于2019年第3季度,开...
- 5款不容错过的APP: Red Bull Alert,Flipagram,WifiMapper
-
本周有不少非常出色的app推出,鸵鸟电台做了一个小合集。亮相本周榜单的有WifiMapper's安卓版的app,其中包含了RedBull的一款新型闹钟,还有一款可爱的怪物主题益智游戏。一起来看看我...
- Qt动画效果展示_qt显示图片
-
今天在这篇博文中,主要实践Qt动画,做一个实例来讲解Qt动画使用,其界面如下图所示(由于没有录制为gif动画图片,所以请各位下载查看效果):该程序使用应用程序单窗口,主窗口继承于QMainWindow...
- 如何从0到1设计实现一门自己的脚本语言
-
作者:dong...
- 三年级语文上册 仿写句子 需要的直接下载打印吧
-
描写秋天的好句好段1.秋天来了,山野变成了美丽的图画。苹果露出红红的脸庞,梨树挂起金黄的灯笼,高粱举起了燃烧的火把。大雁在天空一会儿写“人”字,一会儿写“一”字。2.花园里,菊花争奇斗艳,红的似火,粉...
- C++|那些一看就很简洁、优雅、经典的小代码段
-
目录0等概率随机洗牌:1大小写转换2字符串复制...
- 二年级上册语文必考句子仿写,家长打印,孩子照着练
-
二年级上册语文必考句子仿写,家长打印,孩子照着练。具体如下:...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- wireshark怎么抓包 (75)
- qt sleep (64)
- cs1.6指令代码大全 (55)
- factory-method (60)
- sqlite3_bind_blob (52)
- hibernate update (63)
- c++ base64 (70)
- nc 命令 (52)
- wm_close (51)
- epollin (51)
- sqlca.sqlcode (57)
- lua ipairs (60)
- tv_usec (64)
- 命令行进入文件夹 (53)
- postgresql array (57)
- statfs函数 (57)
- .project文件 (54)
- lua require (56)
- for_each (67)
- c#工厂模式 (57)
- wxsqlite3 (66)
- dmesg -c (58)
- fopen参数 (53)
- tar -zxvf -c (55)
- 速递查询 (52)