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同时打开这两个文件,显示如下:
如果读者对代码有什么意见或建议,欢迎邮箱或者留言探讨!
- 上一篇:没有了
- 下一篇:没有了
