C语言:文件输入输出

您所在的位置:网站首页 c语言如何输入文件名 C语言:文件输入输出

C语言:文件输入输出

2023-07-08 15:29| 来源: 网络整理| 查看: 265

文件输入、输出

函数:fopen()、getc()、putc()、exit()、fclose() fprintf()、fscanf()、fgets()、fputs() rewind()、fseek()、ftell()、fflush() fgetpos()、fsetpos()、feof()、ferror() ungetc()、setvbuf()、fread()、fwrite() 如何使用C标准I/O系列的函数处理文件 文件模式和二进制模式、文本和二进制格式、缓冲和无缓冲I/O 使用既可以顺序访问文件也可以随机访问文件的函数

与文件进行通信

C提供了更强大的文件通信方法,可以在程序中打开文件,然后使用特 殊的I/O函数读取文件中的信息或把信息写入文件

文件是什么

文件(file)通常是在磁盘或固态硬盘上的一段已命名的存储区。

文本模式和二进制模式

为了规范文本文件的处理,C 提供两种访问文件的途径:二进制模式和 文本模式。在二进制模式中,程序可以访问文件的每个字节。而在文本模式 中,程序所见的内容和文件的实际内容不同。程序以文本模式读取文件时, 把本地环境表示的行末尾或文件结尾映射为C模式。例如,C程序在旧式 Macintosh中以文本模式读取文件时,把文件中的\r转换成\n;以文本模式写 入文件时,把\n转换成\r。或者,C文本模式程序在MS-DOS平台读取文件 时,把\r\n转换成\n;写入文件时,把\n转换成\r\n。在其他环境中编写的文本 模式程序也会做类似的转换。

除了以文本模式读写文本文件,还能以二进制模式读写文本文件。如果 读写一个旧式MS-DOS文本文件,程序会看到文件中的\r 和\n 字符,不会发 生映射

I/O的级别

底层I/O(low-level I/O)使用操作系统提供的 基本I/O服务。标准高级I/O(standard high-level I/O)使用C库的标准包和 stdio.h头文件定义。因为无法保证所有的操作系统都使用相同的底层I/O模型,C标准只支持标准I/O包。有些实现会提供底层库,但是C标准建立了可 移植的I/O模型,我们主要讨论这些I/O。

标准文件

C程序会自动打开3个文件,它们被称为标准输入(standard input)、标 准输出(standard output)和标准错误输出(standard error output)。在默认 情况下,标准输入是系统的普通输入设备,通常为键盘;标准输出和标准错 误输出是系统的普通输出设备,通常为显示屏

通常,标准输入为程序提供输入,它是 getchar()和 scanf()使用的文件。 程序通常输出到标准输出,它是putchar()、puts()和printf()使用的文件。

标准错误输出提供了 一个逻辑上不同的地方来发送错误消息。例如,如果使用重定向把输出发送 给文件而不是屏幕,那么发送至标准错误输出的内容仍然会被发送到屏幕上。

标准I/O

与底层io相比:

第一,标准 I/O有许多专门的函数简化了处理不同I/O的问题。例如,printf()把不同形式 的数据转换成与终端相适应的字符串输出。第二,输入和输出都是缓冲的。 也就是说,一次转移一大块信息而不是一字节信息(通常至少512字节)。 例如,当程序读取文件时,一块数据被拷贝到缓冲区(一块中介存储区 域)。这种缓冲极大地提高了数据传输速率。程序可以检查缓冲区中的字 节。缓冲在后台处理,所以让人有逐字符访问的错觉(如果使用底层I/O, 要自己完成大部分工作)

实例程序:

#include #include   // 提供 exit()的原型 int main(int argc, char *argv []) { int ch;      // 读取文件时,储存每个字符的地方 FILE *fp;   // “文件指针” unsigned long count = 0; if (argc != 2) { printf("Usage: %s filename\n", argv[0]); exit(EXIT_FAILURE); } if ((fp = fopen(argv[1], "r")) == NULL) { printf("Can't open %s\n", argv[1]); exit(EXIT_FAILURE); } while ((ch = getc(fp)) != EOF) { putc(ch, stdout); // 与 putchar(ch); 相同 count++; } fclose(fp); printf("File %s has %lu characters\n", argv[1], count); return 0; }

检查命令行参数

首先,程序查看是否有命令行参数。 如果没有,程序将打印一条消息并退出程序。字符串 argv[0]是该程序的名 称。显式使用 argv[0]而不是程序名,错误消息的描述会随可执行文件名的 改变而自动改变

xit()函数关闭所有打开的文件并结束程序。exit()的参数被传递给一些 操作系统,包括 UNIX、Linux、Windows和MS-DOS,以供其他程序使用。 通常的惯例是:正常结束的程序传递0,异常结束的程序传递非零值。不同 的退出值可用于区分程序失败的不同原因,这也是UNIX和DOS编程的通常 做法。但是,并不是所有的操作系统都能识别相同范围内的返回值。因此, C 标准规定了一个最小的限制范围。尤其是,标准要求0或宏 EXIT_SUCCESS用于表明成功结束程序,宏EXIT_FAILURE用于表明结束程 序失败。这些宏和exit()原型都位于stdlib.h头文件中。 根据ANSI C的规定,在最初调用的main()中使用return与调用exit()的效 果相同。因此,在main(),下面的语句: return 0; 和下面这条语句的作用相同: exit(0); 但是要注意,我们说的是“最初的调用”。如果main()在一个递归程序 中,exit()仍然会终止程序,但是return只会把控制权交给上一级递归,直至 最初的一级。然后return结束程序。return和exit()的另一个区别是,即使在其 他函数中(除main()以外)调用exit()也能结束整个程序。

fopen

该函数声明 在stdio.h中。它的第1个参数是待打开文件的名称,更确切地说是一个包含该文件名的字符串地址。第 2 个参数是一个字符串,指定待打开文件的模式。

像UNIX和Linux这样只有一种文件类型的系统,带b字母的模式和不带b 字母的模式相同。

新的C11新增了带x字母的写模式,与以前的写模式相比具有更多特 性。第一,如果以传统的一种写模式打开一个现有文件,fopen()会把该文件 的长度截为 0,这样就丢失了该文件的内容。但是使用带 x字母的写模式, 即使fopen()操作失败,原文件的内容也不会被删除。第二,如果环境允许, x模式的独占特性使得其他程序或线程无法访问正在被打开的文件。

警告

如果使用任何一种"w"模式(不带x字母)打开一个现有文件,该文件的 内容会被删除,以便程序在一个空白文件中开始操作。然而,如果使用带x 字母的任何一种模式,将无法打开一个现有文件。

程序成功打开文件后,fopen()将返回文件指针(file pointer),其他I/O函数可以使用这个指针指定该文件。文件指针(该例中是fp)的类型是指向 FILE的指针,FILE是一个定义在stdio.h中的派生类型。文件指针fp并不指向 实际的文件,它指向一个包含文件信息的数据对象,其中包含操作文件的 I/O函数所用的缓冲区信息。因为标准库中的I/O函数使用缓冲区,所以它们 不仅要知道缓冲区的位置,还要知道缓冲区被填充的程度以及操作哪一个文 件。标准I/O函数根据这些信息在必要时决定再次填充或清空缓冲区。fp指 向的数据对象包含了这些信息

getc和putc

getc()和putc()函数与getchar()和putchar()函数类似。所不同的是,要告诉 getc()和putc()函数使用哪一个文件。 下面这条语句的意思是“从标准输入中 获取一个字符”: ch = getchar();

然而,下面这条语句的意思是“从fp指定的文件中获取一个字符”: ch = getc(fp);

与此类似,下面语句的意思是“把字符ch放入FILE指针fpout指定的文件 中”:

putc(ch, fpout);

在putc()函数的参数列表中,第1个参数是待写入的字符,第2个参数是 文件指针。

文件结尾

如果 getc()函数在读取一个字符时发现是文件结尾,它将返 回一个特殊值EOF。所以C程序只有在读到超过文件末尾时才会发现文件的 结尾(一些其他语言用一个特殊的函数在读取之前测试文件结尾,C语言不 同)。

int ch; FILE * fp; fp = fopen("wacky.txt", "r"); while (( ch = getc(fp)) != EOF) { putchar(ch); //处理输入 }

fclose

fclose(fp)函数关闭fp指定的文件,必要时刷新缓冲区。对于较正式的程序,应该检查是否成功关闭文件。如果成功关闭,fclose()函数返回0,否则返回EOF:

if (fclose(fp) != 0) printf("Error in closing file %s\n", argv[1]);

如果磁盘已满、移动硬盘被移除或出现I/O错误,都会导致调用fclose() 函数失败。

一个简单的文件压缩程序

下面的程序示例把一个文件中选定的数据拷贝到另一个文件中。该程序 同时打开了两个文件,以"r"模式打开一个,以"w"模式打开另一个。该程序以保留每3个字符中的第1个字符的方式压缩第1个文件的 内容。最后,把压缩后的文本存入第2个文件。第2个文件的名称是第1个文 件名加上.red后缀(此处的red代表reduced)。使用命令行参数,同时打开多 个文件,以及在原文件名后面加上后缀,都是相当有用的技巧。这种压缩方 式有限,但是也有它的用途(很容易把该程序改成用标准 I/O 而不是命令行参数提供文件名)。

#include #include   // 提供 exit()的原型 #include   // 提供 strcpy()、strcat()的原型 #define LEN 40 int main(int argc, char *argv []) { FILE *in, *out;  // 声明两个指向 FILE 的指针 int ch; char name[LEN];  // 储存输出文件名 int count = 0; // 检查命令行参数 if (argc < 2) { fprintf(stderr, "Usage: %s filename\n", argv[0]); exit(EXIT_FAILURE); } // 设置输入 if ((in = fopen(argv[1], "r")) == NULL) { fprintf(stderr, "I couldn't open the file \"%s\"\n", argv[1]); exit(EXIT_FAILURE); } // 设置输出 strncpy(name, argv[1], LEN - 5);  // 拷贝文件名 name[LEN - 5] = '\0'; strcat(name, ".red");        // 在文件名后添加.red if ((out = fopen(name, "w")) == NULL) {          // 以写模式打开文件 fprintf(stderr, "Can't create output file.\n"); exit(3); } // 拷贝数据 while ((ch = getc(in)) != EOF) if (count++ % 3 == 0) putc(ch, out);// 打印3个字符中的第1个字符 // 收尾工作 if (fclose(in) != 0 || fclose(out) != 0) fprintf(stderr, "Error in closing files\n"); return 0; } 文件I/O:fprint、fscanf、fgets、fputs

文 件I/O函数要用FILE指针指定待处理的文件。与 getc()、putc()类似,这些函 数都要求用指向 FILE 的指针(如,stdout)指定一个文件,或者使用fopen() 的返回值。

fprint和fscanf

文件I/O函数fprintf()和fscanf()函数的工作方式与printf()和scanf()类似, 区别在于前者需要用第1个参数指定待处理的文件。

#include #include #include #define MAX 41 int main(void) { FILE *fp; char words[MAX]; if ((fp = fopen("wordy", "a+")) == NULL) { fprintf(stdout, "Can't open \"wordy\" file.\n"); exit(EXIT_FAILURE); } puts("Enter words to add to the file; press the #"); puts("key at the beginning of a line to terminate."); while ((fscanf(stdin, "%40s", words) == 1) && (words[0] != '#')) fprintf(fp, "%s\n", words); puts("File contents:"); rewind(fp);    /* 返回到文件开始处 */ while (fscanf(fp, "%s", words) == 1) puts(words); puts("Done!"); if (fclose(fp) != 0) fprintf(stderr, "Error closing file\n"); return 0; }

该程序可以在文件中添加单词。使用"a+"模式,程序可以对文件进行读 写操作。首次使用该程序,它将创建wordy文件,以便把单词存入其中。随 后再使用该程序,可以在wordy文件后面添加单词。虽然"a+"模式只允许在 文件末尾添加内容,但是该模式下可以读整个文件。rewind()函数让程序回 到文件开始处,方便while循环打印整个文件的内容。注意,rewind()接受一 个文件指针作为参数。

fprintf()和 fscanf()的工作方式与 printf()和 scanf()类似。但 是,与 putc()不同的是,fprintf()和fscanf()函数都把FILE指针作为第1个参 数,而不是最后一个参数。

fgets与fputs

fgets()的第1个参数和gets()函数一样,也是表 示储存输入位置的地址(char * 类型);第2个参数是一个整数,表示待输入字符串的大小;最后一个参数是文件指针,指定待读取的文件。

fgets(buf, STLEN, fp);

这里,buf是char类型数组的名称,STLEN是字符串的大小,fp是指向 FILE的指针。

fgets()函数读取输入直到第 1 个换行符的后面,或读到文件结尾,或者 读取STLEN-1 个字符(以上面的 fgets()为例)。然后,fgets()在末尾添加一个空字符使之成为一个字符串。字符串的大小是其字符数加上一个空字符。 如果fgets()在读到字符上限之前已读完一整行,它会把表示行结尾的换行符 放在空字符前面。fgets()函数在遇到EOF时将返回NULL值,可以利用这一机 制检查是否到达文件结尾;如果未遇到EOF则之前返回传给它的地址。

fputs()函数接受两个参数:第1个是字符串的地址;第2个是文件指针。 该函数根据传入地址找到的字符串写入指定的文件中。和 puts()函数不同, fputs()在打印字符串时不会在其末尾添加换行符。

随机访问:fseek和ftell

有了 fseek()函数,便可把文件看作是数组,在 fopen()打开的文件中直 接移动到任意字节处。

fseek()有3个参数,返回int类型的值;ftell()函数返回一 个long类型的值,表示文件中的当前位置。

/* reverse.c -- 倒序显示文件的内容 */ #include #include #define CNTL_Z '\032'   /* DOS文本文件中的文件结尾标记 */ #define SLEN 81 int main(void) { char file[SLEN]; char ch; FILE *fp; long count, last; puts("Enter the name of the file to be processed:"); scanf("%80s", file); if ((fp = fopen(file, "rb")) == NULL) {                  /* 只读模式  */ printf("reverse can't open %s\n", file); exit(EXIT_FAILURE); } fseek(fp, 0L, SEEK_END);       /* 定位到文件末尾 */ last = ftell(fp); for (count = 1L; count 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');  // 查找换行符 if (find)          // 如果地址不是NULL, *find = '\0';     // 在此处放置一个空字符 else while (getchar() != '\n') continue; } return ret_val; }

使用二进制I/O进行随机访问

/* randbin.c -- 用二进制I/O进行随机访问 */ #include #include #define ARSIZE 1000 int main() { double numbers[ARSIZE]; double value; const char * file = "numbers.dat"; int i; long pos; FILE *iofile; // 创建一组 double类型的值 for (i = 0; i < ARSIZE; i++) numbers[i] = 100.0 * i + 1.0 / (i + 1); // 尝试打开文件 if ((iofile = fopen(file, "wb")) == NULL) { fprintf(stderr, "Could not open %s for output.\n", file); exit(EXIT_FAILURE); } // 以二进制格式把数组写入文件 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); } // 从文件中读取选定的内容 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);  // 计算偏移量 fseek(iofile, pos, SEEK_SET);    // 定位到此处 fread(&value, sizeof(double), 1, iofile); printf("The value there is %f.\n", value); printf("Next index (out of range to quit):\n"); } // 完成 fclose(iofile); puts("Bye!"); return 0; }

首先,该程序创建了一个数组,并在该数组中存放了一些值。然后,程 序以二进制模式创建了一个名为numbers.dat的文件,并使用fwrite()把数组中 的内容拷贝到文件中。内存中数组的所有double类型值的位组合(每个位组 合都是64位)都被拷贝至文件中。不能用文本编辑器读取最后的二进制文 件,因为无法把文件中的值转换成字符串。然而,储存在文件中的每个值都 与储存在内存中的值完全相同,没有损失任何精确度。此外,每个值在文件 中也同样占用64位存储空间,所以可以很容易地计算出每个值的位置。 程序的第 2 部分用于打开待读取的文件,提示用户输入一个值的索引。 程序通过把索引值和 double类型值占用的字节相乘,即可得出文件中的一个 位置。然后,程序调用fseek()定位到该位置,用fread()读取该位置上的数据 值。注意,这里并未使用转换说明。fread()从已定位的位置开始,拷贝8字 节到内存中地址为&value的位置。然后,使用printf()显示value。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3