C语言文件的输入/输出
对文件的输入/输出学习是C语言中的一块重要内容,因为当你的程序变得复杂时,难免要处理一些文件,涉及到文件的读取和写入。C语言提供了强大的文件输入/输出功能,其标准I/O包中包含了很多专用的函数,可以很方便地读取和写入文件,我将在下面介绍几种常用的文件I/O函数,并最后给出一个模拟压缩文件的例子。
在介绍函数之前,首先要说明C语言处理输入和输出,是采用“流“的形式,而且常常会有缓冲区,缓冲区的存在可以提高输入/输出处理的高效性,举个简答的例子,如果没有缓冲,下面这个代码的执行效果会很尴尬!
while((ch=getchar())!=" ") putchar(ch); putchar(" ");
当你输入“abc[回车的时候]”,屏幕显示的是"aabbcc",因为没有缓冲的存在,一输入马上得到输出。那么如何理解文件流的形式,可以这么理解,字符的输入就像注入一段水流,这些调用函数就在岸边去抽取自己想要的输入,然后显示,我以一个常见的小错误代码来说明
char ch; int num1, num2; while((ch=getchar())!=" "){ putchar(ch); scanf("%d%d", &num1, &num2); printf("%d %d", num1, num2); } putchar(" ");
当你输入“a 42 24[回车]”的时候,这段字符序列“a【空格】42【空格】24【回车】”就向水流一样流进缓冲区,其中a被ch=getchar()捕获,然后进入循环,由于scanf()自动跳过空白字符,所以捕获两个整型量42和24,遇到【回车】会刷新缓冲区,所以屏幕输出“a42 24”,然后就结束循环了,因为[回车]被ch=getchar()捕获,不满足循环条件,跳出循环。
所以大家在分析输入输出的时候也可以照着这种方法去分析,其实良好的输入输出功能是一个是需要程序员很细心的去处理,涉及到很多细节。
接下来,介绍三个标准文件:stdin,stdout,stderr。可以将这3个文件理解成C语言自带的用于处理键盘输入,屏幕显示,标准错误的立即显示。stdin表示标准输入,通常指键盘输入,stdout表示标准输出,通常指屏幕显示,stderr表示标准错误,不经过缓冲区,直接输出到屏幕,举几个简单例子:
fprintf(stdout, "Hello World! "); printf("Hello World! "); fgets(char_array, MAX_SIZE, stdin);
上述fprintf()语句和printf()功能相同,都是向屏幕输出"Hello World!", fgets()函数则是将键盘输入的字符串放到容量为MAX_SIZE大小的字符数组中。
通过上述描述,初学者大概对输入输出流有了一个基本的概念,也了解了C语言的3个标准文件。其实大家可以发现C语言本质是把键盘输入,屏幕输出当成文件来处理的,这和UNIX和Linux的理念很像,一切设备均被视为文件。因为C语言的两位创始人Dennis M. Ritchie和Brain W. Kernighan也是UNIX的创始人啊,真的是佩服的五体投地,大家感兴趣的话可以查一查UNIX和C语言的历史,Linux又和UNIX很有渊源呢!
标准I/O包中含有很多个函数,这里介绍常用的几个:
fopen()函数,第一个参数是要打开的文件名(包含该文件名的字符串的地址),第二个参数是用于指定文件打开模式的一个字符串。模式字符串包括:“r”,“w”,“a”,“r+”,“w+”,“a+”,其中r表示只读,w表示写入,a表示追加,若带有+则表示即可读取也可写入,尤其要注意w参数,是将指定文件的原有内容清空,重新写入,若是希望对原有文件进行修改(保留原有文件内容),需要使用“a”参数;该函数的返回值为FILE类型的指针。
fclose()函数,参数只有一个,即FILE类型的指针指定的文件,其功能为关闭文件,并刷新缓冲区,文件成功关闭返回0,否则返回EOF。
fseek()定位文件当前的位置,参数为3个,第1个为表示文件的FILE类型指针,第2个为偏移量(long类型,正数表示向前偏移,负数表示向后偏移),第3个为起始点模式(可以称之为基准,共有3个,SEEK_SET表示文件的开始,SEEK_CUR表示文件当前位置,SEEK_END表示文件的结尾)
fwrite()函数将二进制数据写入文件,其函数原型为size_t fwrite(const void * restrict ptr, size_t size, size_t nmemb, FILE * restrict fp); 举例来说:要写入10个double类型的数据,可以这样做:
double number[10];
fwrite(number, sizeof(double), 10, fp);
与fwrite()函数对应,fread()函数从文件中读取相应的数据到制定目标中,其函数原型为: size_t fread(const void * restrict ptr, size_t size, size_t nmemb, FILE * restrict fp);用法类似与fread(),只不过是从文件中读取相应的数据。
接下来,我以一个模拟压缩的程序来说明文件的I/O读取。压缩是一个很高深的学问,我这里只是提一个最最简单的压缩思路:如果有一串重复性很高的序列,比如0000000001111110000001111,那么我将其表示成09160614,其中9表示0的个数,6表示1的个数,6表示0的个数,4表示1的个数,这样是不是就可以实现压缩了呢。我给出这个模拟压缩程序的代码如下:
/*================================================ # Author: Joker@HIT NolanRobot@163.com # Filetype: C source code # Environment: Linux & Ubuntu 14.04 # Tool: Vim & Gcc # Date: Wed Aug 24 2016 # Descprition: condense the image! ================================================*/ #include<stdio.h> #include<string.h> #include<stdlib.h> #include<time.h> #define COMMAND_SIZE 80 FILE * create_image(char *); void get_command(char [], char *, char *); void condense_image(FILE *, FILE *); int main(int argc, char *argv[]) { if(argc!=3){ printf("Input error! The usage: %s source_file condensed_file ", argv[0]); exit(EXIT_FAILURE); } char command[COMMAND_SIZE] = "ls -l $(pwd) | egrep "; get_command(command, argv[1], argv[2]); FILE *source_file, *condensed_file; source_file = create_image(argv[1]); condensed_file = fopen(argv[2], "w"); condense_image(source_file, condensed_file); fclose(source_file); fclose(condensed_file); system(command); printf("Bye! "); return 0; } FILE * create_image(char *filename) { FILE *fp; int lsize, wsize, i, j; printf("Enter the image size (length * width): "); scanf("%d%d", &lsize, &wsize); char image[lsize][wsize]; srand((unsigned int)time(NULL)); for(i=0;i<lsize;i++) for(j=0;j<wsize;j++) image[i][j] = rand() % 2 + "0"; fp = fopen(filename, "w+"); if(fp==NULL){ printf("Create %s error! ", filename); exit(EXIT_FAILURE); } for(i=0;i<lsize;i++){ fwrite(image[i], sizeof(char), wsize, fp); putc(" ", fp); } return fp; } void get_command(char command[COMMAND_SIZE], char *str1, char *str2) { strcat(command, """); strcat(command, str1); strcat(command, "|"); strcat(command, str2); strcat(command, """); return; } void condense_image(FILE *source, FILE *target) { int ch, prev, count; count = 1; fseek(source, 0L, SEEK_SET); prev = getc(source); while((ch=getc(source))!=EOF){ if(ch==" "){ putc(" ",target); continue; } if(ch!=prev){ putc(prev, target); fprintf(target, "%d", count); count = 1; } else count++; prev = ch; } fseek(target, -1L, SEEK_END); putc(prev, target); fprintf(target, "%d", count); return; }
上述程序的功能是用户通过命令行参数输入两个文件名,执行程序时,先建立模拟数字图像的文件,全为01序列,图像大小由用户输入,然后将该文件通过上述思路压缩,另存为压缩文件,最后程序在屏幕上列出新建两个文件的信息。
注意上述程序中使用到了C语言的命令行参数,常规main(void),若要使用命令行参数,则为main(int argc, char *argv[]),其中argc表示参数的个数,argv为指向字符的指针数组,另外为了确保已经产生了两个新建文件,调用了system()函数,可以执行Linux的系统命令,列出两个新建的文件。
运行结果如下所示:
用Vim同时打开这两个文件,显示如下:
如果读者对代码有什么意见或建议,欢迎邮箱或者留言探讨!
- 上一篇: C语言实现数据输入与输出的函数
- 下一篇: C语言读取文件中的数据作为输入和输出