PPC的C/C++和人工智能学习笔记
每一篇学习笔记,都只是为了更好地掌握和理解

C语言基础(18)_文件操作

今天学习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)

学习笔记未经允许不得转载:PPC的C/C++和人工智能学习笔记 » C语言基础(18)_文件操作

分享到:更多 ()

评论 抢沙发

评论前必须登录!