前言:
此刻我们对“c语言各函数”可能比较重视,各位老铁们都需要分析一些“c语言各函数”的相关文章。那么小编同时在网上网罗了一些有关“c语言各函数””的相关资讯,希望同学们能喜欢,你们快快来学习一下吧!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 81void 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 1000int 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):
The value there is 1.000000.
Next index (out of range to quit):
-1
Bye!
标签: #c语言各函数