流文件操作(打开、读、写、关闭)
概述
1、流式文件:在C语言中对文件的记录是以字符(字节)为单位的。输入输出的数据流的开始和结束仅受程序控制而不受物理符号(如回车换行符)控制。也就是说,在输出时不以回车换行符作为记录的间隔(事实上C文件并不由记录构成)。我们把这种文件称为流式文件。
2、磁盘文件:在程序运行时,常常需要将一些数据(运行的最终结果或中间数据)输出到磁盘上存放起来,以后需要时再从磁盘中输入到计算机内存。
3、C文件本质:C语言将文件看作是一个字符(字节)的序列,即一个一个字符(字节)的数据顺序组成。
- 根据数据的组成形式,可分为ASCII文件和二进制文件。ASCII文件又称文本(text)文件,它的每一个字节可放一个ASCII码,代表一个字符。二进制文件是把内存中的数据按其在内存中的存储形式按原样输出到磁盘上存放。
- 用ASCII码形式输出的数据是与字符一一对应的,一个字节代表一个字符,因而便于对字符进行逐个处理,也便于输出字符。但一般占存储空间较多,而且要花费转换时间(二进制形式与ASCII码间的转换)。用二进制形式输出数值,可以节约外存空间和转换时间,但一个字节并不对应一个字符,不能直接输出字符形式。
- 把一个文本文件读入内存时,要将ASCII码转换成二进制码, 而把文件以文本方式写入磁盘时,也要把二进制码转换成ASCII码,因此文本文件的读写要花费较多的转换时间。对二进制文件的读写不存在这种转换。
- 例如:有一个整数12345(十进制),在内存中占2个字节(无符号:0~65535)。如果按ASCII码形式输出,则占5个字节(由高到低:第一个字符1的ASCII值49,二进制表示为0011_0001;第二个字符0的ASCII值50,二进制表示为0011_0010;同理后三个字符)。而按二进制形式输出,在磁盘上只占2个字节(12345的二进制0011_0000_0011_1001)。
范例
1、打开和关闭一个文件常用方法:fopen函数和fclose函数
FILE* fp;
if((fp = fopen("file.txt", "w+")) == NULL)
{
printf("can not open the file\n");
exit(0);
}
fclose(fp);
表示打开一个file.txt的文件;使用文件方式为w+(读/写/可新建,可按需要选择文件使用方式);若不能实现打开/新建的任务,此时fopen函数将会带回一个空指针NULL,NULL在头文件中已被定义为0;对于输出流,fclose函数会在文件关闭前刷新缓冲区,如果它执行成功,fclose返回值为0,否则返回EOF(-1)。
2、文件的读写
C++提供了低级的I/O功能和高级的l/O功能:
- 高级的I/O功能是把若干个字节组合为一个有意义的单位(如整数、单精度数、双精度数、字符串或用户自定义的类型的数据),然后以ASCII字符形式输人和输出。例如将数据从内存送到显示器输出,就属于高级I/O功能,先将内存中的数据转换为ASCII字符,然后分别按整数、单精度数、双精度数等形式输出。这种面向类型的输入输出在程序中用得很普遍,用户感到方便。但在传输大容量的文件时由于数据格式转换,速度较慢,效率不高。
- 所谓低级的I/O功能是以字节为单位输入和输出的,在输人和输出时不进行数据格式的转换。这种输入输出是以二进制形式进行的。通常用来在内存和设备之间传输一批字节。这种输入输出速度快、效率高,一般大容量的文件传输用无格式转换的I/0。但使用时会感到不大方便。
例:将一个文本文件中的内容写入到一个新的文本文件中。
#include "stdio.h"
#include "stdlib.h"
void main()
{
FILE* fp_r;
if((fp_r = fopen("file_r.txt", "r")) == NULL)
{
printf("can not open the file_r\n");
exit(0);
}
FILE* fp_w;
if((fp_w = fopen("file_w.txt", "w")) == NULL)
{
printf("can not open the file_w\n");
exit(0);
}
int file_size = 0;
unsigned char ch[1] = {0};
fseek(fp_r,0L,SEEK_END);
file_size = ftell(fp_r);
rewind(fp_r);
while(ftell(fp_r) != file_size) //判断是否读到文末
{
ch[0] = fgetc(fp_r);// int fgetc(FILE *stream) 读一个字节
fputc(ch[0],fp_w);// int fputc(int char, FILE *stream) 写一个字节
// fread(ch,1,1,fp_r); // fread(buffer,size,count,fp)
// fwrite(ch,1,1,fp_w); // fwrite(buffer,size,count,fp)
// fscanf(fp_r,"%c",&ch[0]); // fscanf(文件指针,格式字符串,输入列表)
// fprintf(fp_w,"%c",ch[0]); // fprintf(文件指针,格式字符串,输出列表)
}
fclose(fp_r);
fclose(fp_w);
}
特殊符号输出,以putc为例:
putc('a',fp_w); 输出字符a
putc(' ',fp_w); 输出空格
putc('\n',fp_w); 输出换行符
putc(10,fp_w); 输出换行符
注:
- fseek() 一般用于二进制文件,因为文本文件要发生字符转换,计算的位置时往往会发生混乱。
- C/C++中对文件的操作分以二进制文件方式读写和以文本文件方式读写两种,二者唯一的区别就在于对换行符的处理上 。
详解连接
判断是否读到文末的三种方法
1、ch = feof(fp):返回值ch为所读取的一个字节,如果读到文件末尾或者读取出错时返回EOF
ch = fgetc(fp); // 先读在判断
while(!feof(fp)) // or while(EOF != ch)
{
//TODO;
printf("%x",ch);
c=fgetc(fp);
}
2、buf = fread(ptr, 1, 2, fp):返回值buf为所读取到的字节个数
buf = fread(ptr, 1, 2, fp); // 先读在判断
while(buf != 0)
{
//TODO;
printf("%d",buf);
buf = fread(ptr, 1, 2, fp);
}
3、fseek、ftell和rewind:判断读取文件大小
fseek(fp,0L,SEEK_END);
file_size = ftell(fp);
rewind(fp);
while(ftell(fp) != file_size)
{
//TODO;
}