今天学习C语言的文件操作,学完写一个文件copy的程序做练习。
文件的打开和关闭: fopen、fclose
文件的读写: fgetc、fputc、fgets、fputs、fprintf、fscanf、fread、fwrite
文件状态检查:feof、ftell、ferror、clearerr
文件定位:rewind、fseek
fopen函数:
FILE* fopen(char const* FileName, char const* Mode );
如:FILE *fp = fopen(“1.txt”, “r”); if (NULL==fp) {printf(“error”); exit(0);}
返回值是NULL说明打开失败。
参数:FileName 文件名(包括路径)
Mode打开方式:
“r” 只读,文件不存在的话,打开失败。成功打开的话只读不能写。
“w” 只写,文件指针指到文件头,不管写不写东西,文件原先的内容都没了。假如文件不存在,则创建该文件。
“a” 追加只写,文件指针指向文件尾,写的内容保存在原文件的内容后面,不会影响原先的文件内容。假如文件不存在,则创建该文件。
“rb”,”wb”,”ab”,和上面类似,上面没有写b的是以文本文件的方式打开,加b的是以二进制的格式打开文件。
“r+” 读写方式打开,文件不存在的话,打开失败。
“w+” 读写方式打开,打开的时候,文件内容先清空,文件不存在则创建文件。
“a+” 读写方式打开,写数据会写在最后面,不会覆盖原内容,文件不存在则创建文件。
“rb+”,”wb+”,”ab+”,和上面类似,上面没有写b的是以文本文件的方式打开,加b的是以二进制的格式打开文件。
一般情况,以”a+”和”ab+”打开比较安全,都可以读写,文件不存在也会创建。当然假如不能改变文件内容的话,还是以 “r”,”rb”打开为好。
打开文件的作用是:
(1)分配给打开文件一个FILE 类型的文件结构体变量,并将有关信息填入文件结构体变量;
(2)开辟一个缓冲区;
(3)调用操作系统提供的打开文件或建立新文件功能,打开或建立指定文件;
FILE *:指出fopen是一个返回文件类型的指针函数;
注意:C语言将计算机的输入输出设备都看作是文件。例如,键盘文件、屏幕文件等。在执行程序时系统先自动打开键盘、屏幕、错误三个文件。这三个文件的文件指针分别是:标准输入stdin、标准输出stdout和标准出错 stderr。
fclose函数:
int fclose(FILE *fp);
关闭由fp指向的文件。返回值:正常返回是0,异常返回是EOF,表示文件在关闭时发生错误。
fgetc函数:从文件中读取一个字符
int fgetc(FILE *fp);
返回读取字符的ASCII码。读取错误返回EOF。
例如,要从”写打开”文件中读取一个字符时,会发生错误而返回一个EOF。
例子:用fgetc读文件全部内容(就是一个一个读,直到读到EOF)
int ch;
while( (ch = fgetc(fp) )!=EOF) {
ch = fgetc(fp);
putchar(ch);
}
fputc函数:向文件中在当前位置写入一个字符
int fputc(int ch,FILE *fp);
参数ch:是一个整型变量,就是要写字符的ASCII值。
成功的话,返回要写入字符的ASCII码。失败返回EOF。
可以用fgetc读1个字符,然后用fputc写1个字符到另外1个文件中,从而实现复制文件。(但是好像只能是文本文件?)
fgets函数:从文件中读取一个字符串
char *fgets(char *str,int n,FILE *fp);
从由fp指出的文件中读取n-1个字符,并把它们存放到由str指出的字符数组中去,最后加上一个字符串结束符’\0’。
str:接收字符串的内存地址,可以是数组名,也可以是指针。
n: 指出要读取字符的个数。
读取正确,返回字符串的内存首地址,即str的值。错误返回NULL。此时可用feof()或ferror()函数来判断是否读取到了文件尾,还是发生了错误。
fputs函数:写一个字符串到文件中去
int fputs(char *str,FILE *fp);
正确返回写入文件的字符个数,即字符串的长度。错误返回NULL。
fprintf函数:往文件中写格式化数据
int fprintf(FILE *fp,char *format,arg_list);
将变量表列(arg_list)中的数据,按照format指出的格式,写入由fp指定的文件。fprintf()函数与printf()函数的功能相同,只是printf()函数是将数据写入屏幕文件(stdout)。所以:printf(…) 其实和 fprintf(stdout,…) 是一样的功能。
使用的时候,要和 fscanf函数的格式一样。
fscanf函数:从文件中读出格式化数据
int fscanf(FILE *fp, char *format, arg_list));
fscanf()函数与scanf()函数的功能相同,只是scanf()函数是将数据从键盘读入(stdin)。所以:scanf(…) 其实和 fscanf(stdin,…) 是一样的功能。
fread函数:以二进制形式读取文件中的数据
int fread(void *buffer,unsigned size,unsigned count,FILE *fp);
从fp指定的文件中,按二进制形式将size * count个数据读到由buffer指出的数据区中。
参数:
buffer:这是一个void型指针,指出要将读入数据存放在其中的存储区首地址。
size:指出一个数据块的字节数,即一个数据块的大小尺寸。
count:指出一次读入多少个数据块。
正确读取,返回实际读取数据块的个数,即count。如果文件中剩下的数据块个数少于参数中count给出的个数,或者发生了错误,返回0值。
fwrite函数: 以二进制形式写数据到文件中去
int fwrite(void *buffer,unsigned size,unsigned count,FILE *fp);
按二进制形式,将由buffer指定的数据缓冲区内的size*count个数据写入由fp指定的文件中去。
参数和fread相同。
正确写入,返回实际输出数据块的个数,即count。错误返回0值。
feof函数:判断文件是否结束
int feof(FILE *fp);
返回0(假),表示文件未结束。返回非0(真),表示文件结束。
文本文件可以用EOF来判断是否到文件尾,二进制文件还是用这个函数。
ferror函数:文件读/写出错
int ferror(FILE *fp);
检查由fp指定的文件在读写时是否出错。
返回0:假,表示无错误。非0:真,表示出错。
clearerr函数:清除文件错误标志
void clearerr(FILE *fp);
ftell函数:文件指针的当前位置
long ftell(FILE *fp);
取得由fp指定文件的当前读/写位置,该位置值用相对于文件开头的位移量来表示。
返回值就是位移量(这是个长整数)。返回-1表示出错。
rewind函数:将文件指针指向文件头部
void rewind(FILE *fp);
文件指针重新指向文件的开头位置。
fseek函数:随机定位文件指针
int fseek(FILE *fp, long offset, int base);
使文件指针fp移到基于base的相对位置offset处。
offset:相对base的字节位移量。这是个长整数,所以一般写 0L(L就是长整型的意思)。
base:文件位置指针移动的基准位置,是计算文件位置指针位移的基点:
SEEK_SET: 文件开头(0)
SEEK_CUR: 当前位置(1)
SEEK_END: 文件结尾(2)
操作成功返回当前指针位置。错误返回-1。
练习:实现文件复制。
问题1:要实现mycopy 文件a 文件b 这样的格式,需要用到命令行参数,所以要在main函数中加上参数:int main(int argc,char* argv[]); argc的内容是有多少个命令参数,假如只写1个mycopy后面什么都不跟的话,这个argc是1,agrv[0]的内容就是”mycopy”,所以后面再有参数的话,就依次是argv[1], argv[2]…
问题2:复制文件,要判断被复制的文件是否存在,还要判断复制到的文件是否存在,已经存在的话,要询问是否覆盖,判断文件是否存在,可以用只读的方式尝试打开该文件,返回NULL说明文件不存在。(但是这个方法,假如没有读权限的话,也会返回NULL)
问题3:我们可以用二进制的方式打开文件,然后fread、fwrite一个字节一个字节的复制,但是文件很大的话,这个效率是不是很低?是不是可以尝试一大段一大段地读写(一大段是多大合适呢?),以达到速度快一些。这个问题,我以一个400M左右的压缩文件为例,看看这几种方式的速度问题。检测是否正确复制,可以把这个压缩文件解压缩,没有问题应该就对了,当然也可以逐个读取比较来判断正确性。
计算程序运行时间的代码:
#include “time.h”
clock_t start, finish;
double used_time;
start = clock();
//这里是执行代码
finish = clock();
used_time = (double)(finish – start) / CLOCKS_PER_SEC;
printf( “%.3lf seconds\n”, used_time );
测试:400M文件,逐个字节方式release模式 55秒左右。一次性读取然后一次性存放6秒左右。但是用 copy 指令,3秒左右就完成了,不知道为啥系统的copy指令速度那么快呢。。。崩溃中。。。
当然,还有一种写法,不是一次性读取所有的内容,而是按照比如64M读取1次,最后不够的部分,重新计算出大小以后再读写一次。(这个没有实现)
源码如下:
/*************************************** Copyright(C) 2017,www.vsppc.com FileName:mycopy.c Author:ppc Version: Date:2017-03-04 Description:实现一个类似dos的copy程序 Others: FunctionList: void mycopy1(FILE* src, FILE* dst); //按字节复制 void mycopy2(FILE* src, FILE* dst); //按段复制 Histroy: ***************************************/ #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> void mycopy1(FILE* src, FILE* dst); //按字节复制 void mycopy2(FILE* src, FILE* dst); //按段复制 int main(int argc,char* argv[]) { if (argc != 3) { //参数数量不对 printf("请使用 mycopy 文件1 文件2 的格式进行文件复制!\n"); return 2; } char src_name[100]; //源文件名 char dst_name[100]; //目标文件名 strcpy(src_name, argv[1]); strcpy(dst_name, argv[2]); FILE *src = fopen(src_name, "rb"); if (NULL == src) { printf("源文件: %s 没有找到,请确认路径正确!\n",src_name); return 1; } FILE *dst = fopen(dst_name, "rb"); if (NULL != dst) { //目标文件已经存在 printf("注意: %s 文件已经存在,是否覆盖(y/n)?\n", dst_name); int ch = getchar(); if (ch != 'Y'&&ch != 'y') { //不是输入的 Y y就退出 fclose(src); fclose(dst); return 1; } } dst = fopen(dst_name, "wb"); //这里开始copy, 2种方法,1种是1个字节1个字节复制,另外个是一段一段复制 clock_t start, finish; double used_time; start = clock(); //mycopy1(src, dst); mycopy2(src, dst); finish = clock(); used_time = (double)(finish - start) / CLOCKS_PER_SEC; printf("%.3lf seconds\n", used_time); fclose(src); fclose(dst); //system("pause"); return 0; } void mycopy1(FILE* src, FILE* dst) { //按字节复制 char buff[1]; while (!feof(src)) { if(1==fread(buff, 1, 1, src)) fwrite(buff, 1, 1, dst); } } void mycopy2(FILE* src, FILE* dst) { //按段复制 fseek(src, 0L, SEEK_END); long length = ftell(src); //获取文件的大小 unsigned char* buff = NULL; buff = (unsigned char*)malloc(sizeof(unsigned char)*length); if (NULL == buff) { printf("内存分配失败!复制失败!\n"); return; } fseek(src, 0L, SEEK_SET); fread(buff, sizeof(unsigned char)*length, 1, src); fwrite(buff, sizeof(unsigned char)*length, 1, dst); free(buff);//释放内存 }
(2017-03-04 www.vsppc.com)
评论前必须登录!
注册