目录
键盘、显示器、打印机、磁盘驱动器等逻辑设备, 其输入输出都可以通过文件操作管理的方法来完成。文件输入输出函数有两类关于文件的函数。一类称做标准文件函数也称缓冲型文件函数, 这是 ANSI 标准定义的函数; 另一类叫非标准文件函数, 也称非缓冲型文件函数。
在了解文件操作函数之前,需知道一个基本概念:
流(stream)和文件(file) ,标准库为编程者和被访问的设备之间提供了一层抽象的东西, 称之为"流", 而将具体的实际设备叫做文件 。 流看作是一个逻辑设备, 具有相同的行为。因此, 其可用来进行文件写的函数也同样可以用来进行打印机的写入。一般有两种性质的流: 文字流( text stream)和二进制(binary stream)。对磁盘访问来说就是文本文件和二进制文件。
一、标准文件函数
主要包括文件的打开、关闭、读和写等函数,提供顺序读写函数和随机读写函数两组函数。
【1】文件的打开和关闭
1.fopen()函数用于打开文件,通常包含在<stdio.h> 中,其调用格式为: FILE *fopen(char *filename, const char *mode);
文件指针FILE 实际上FILE 是一个新的数据类型。它是基本数据类型的集合的结构指针。就是一个包括了文件管理有关信息的数据结构, 即在打开文件时必须先定义一个文件指针。
fopen打开文件的函数,返回一个文件指针,其中形式参数有两个,均为字符型变量(字符串数组或字符串指针)。 fopen()函数中第一个形式参数表示文件名, 可以包含路径和文件名两部分。 如: "1.txt" "C:\\myfile\\1.txt" 。 第二个形式参数表示打开文件的类型。关于文件类型的规定参见下表
- "r" 打开文字文件只读
- "w" 创建文字文件只写
- "a" 增补, 如果文件不存在则创建一个
- "r+" 打开一个文字文件读/写
- "w+" 创建一个文字文件读/写
- "a+" 打开或创建一个文件增补
- "b" 二进制文件(可以和上面每一项合用)
- "t" 文这文件(默认项)
如果要打开一个 C:\\myfile子目录中, 文件名为 1.dat的二进制文件,可写成: fopen("c:\\myflie\\1.dat", "rb")。如果成功的打开一个文件, fopen()函数返回文件指针, 否则返回空指针 (NULL)。由此可判断文件打开是否成功。
2. fclose()函数
fclose()函数用来关闭一个由fopen()函数打开的文件 , 其调用格式为: int fclose(FILE *stream); 该函数返回一个整型数。当文件关闭成功时, 返回 0, 否则返回一个非零值。 可以根据函数的返回值判断文件是否关闭成功 。
【2】文件操作的函数
1. 文件的顺序写函数 fprintf()、fputs()和 fputc()函数
函数 fprintf()、fputs()和 fputc()均为文件的顺序写操作函数, 其调用格 式如下:
- int fprintf(FILE *stream, char *format,... );
- int fputs(char *string, FILE *steam);
- int fputc(int ch, FILE *steam);
上述三个函数的返回值均为整型量。fprintf() 函数的返回值为实际写入文件中的字节个数(字节数)。如果写错误,则返回一个负数; fputs()函数返回 0 时表明将 string 指针所指的字符串写入文件中的操作成功,返回非 0 时,表明写操 作失败;fputc()函数返回一个向文件所写字符的值,此时写操作成功,否则返 回 EOF(文件结束结束其值为-1, 在 stdio.h 中定义)表示写操作错误。 fprintf( ) 函数中格式化的规定与 printf( ) 函数相同,所不同的只是 fprintf()函数是向文件中写入。而 printf()是向屏幕输出。
2. 文件的顺序读操作函数 fscanf()、fgets()和 fgetc()函数
函数 fscanf()、fgets()和 fgetc()均为文件的顺序读操作函数, 其调用格式 如下:
- int fscanf(FILE *stream, char *format, ...);
- char fgets(char *string, int n, FILE *steam);
- int fgetc(FILE *steam);
fscanf()函数的用法与scanf()函数相似,只是它是从文件中读到信息。 fscanf()函数的返回值为 EOF(即-1),表明读错误,否则读数据成功;fgets()函数从文件中读取至多n-1个字符(n 用来指定字符数),并把它们放入string 指向的字符串中, 在读入之后自动向字符串未尾加一个空字符,读成功返回 string 指针,失败返回一个空指针;fgetc()函数返回文件当前位置的一个字符,读错误时返回EOF。
【3】文件的随机读写
1.有时需要直接读取文件中间某处的信息,若用文件的顺序读写必须从文件 头开始直到要求的文件位置再读,这显然不方便。标准库提供了一组文件的随机读写函数,即可以将文件位置指针定位在所要求读写的地方直接读写。
文件的随机读写函数如下:
- int fseek (FILE *stream, long offset, int fromwhere);
- int fread(void *buf, int size, int count, FILE *stream);
- int fwrite(void *buf, int size, int count, FILE *stream);
- long ftell(FILE *stream);
fseek()函数的作用是将文件的位置指针设置到从fromwhere 开始的第 offset 字节的位置上,其中 fromwhere(fromwhere,文件位置指针起始计算位置 ) 是下列几个宏定义之一:
- SEEK_SET 0 从文件开头
- SEEK_CUR 1 从文件指针的现行位置
- SEEK_END 2 从文件末尾
offset 是指文件位置指针从指定开始位置(fromwhere到指出的位置)跳过的字节数。它是一个长整型量, 以支持大于64K字节的文件。fseek()函数一般用于对二进制文件进行操作。当 fseek()函数返回 0 时表明操作成功,返回非0 表示失败。
fread()函数是从文件中读 count 个字段,每个字段长度为size个字节,并把它们存放到 buf 指针所指的缓冲器中。
fwrite()函数是把 buf 指针所指的缓冲器中,长度为size 个字节的 count 个字段写到 stream 指向的文件中去。 随着读和写字节数的增大,文件位置指示器也增大, 读多少个字节,文件位置指示器相应也跳过多少个字节。读写完毕函数返回所读和所写的字段个数。
ftell()函数返回文件位置指示器的当前值,这个值是指示器从文件头开始 算起的字节数,返回的数为长整型数,当返回-1 时,表明出现错误。
2.缓存区刷新函数fflush():
当用标准文件函数对文件进行读写操作时, 首先将所读写的内容放进缓冲区, 即写函数只对输出缓冲区进行操作,读函数只对输入缓冲区进行操作。例如向一个文件写入内容,所写的内容将首先放在输出缓冲区中, 直到输出缓冲区存满或使用 fclose()函数关闭文件时,缓冲区的内容才会写入文件中。若无 fclose() 函数,则不会向文件中存入所写的内容或写入的文件内容不全。有一个对缓冲区进行刷新的函数,即 fflush(),其调用格式为:
int fflush(FILE *stream);
该函数将输出缓冲区的内容实际写入文件中,而将输入缓冲区的内容清除掉。
【4】读写指针位置操作
feof()和rewind()函数 , 这两个函数的调用格式为:
int feof(FILE *stream);
int rewind(FILE *stream);
feof()函数检测文件位置指示器是否到达了文件结尾,若是则返回一个非 0 值,否则返回 0。这个函数对二进制文件操作特别有用,因为二进制文件中,文 件结尾标志EOF也是一个合法的二进制数,只简单的检查读入字符的值来判断文件是否结束是不行的。如果那样的话,可能会造成文件未结尾而被认为结尾,所以就必须有 feof()函数。 例如下面的这条语句是常用的判断文件是否结束的方法。
while(!feof(fp)) fgetc(fp);
rewind()函数用于把文件位置指示器移到文件的起点处,成功时返回 0,否则返回非 0 值。
二、非标准文件函数
这类函数最早用于UNIX 操作系统,ANSI 标准未定义,但有时也经常用到,因此大多编译器也支持这些函数。它们的头文件为io.h。 一、文件的打开和关闭
【1】 文件的打开与关闭
1.open()函数
open()函数的作用是打开文件,其调用格式为:
int open(char *filename, int access);
该函数表示按 access 的要求打开名为 filename 的文件, 返回值为文件描述字,其中 access 有两部分内容:基本模式和修饰符,两者用" |"("或")方式连接。 修饰符可以有多个, 但基本模式只能有一个。access的规定如下表:
- 基本模式 含义 修饰符 含 义
- O_RDONLY 只读 O_APPEND 文件指针指向末尾
- O_WRONLY 只写 O_CREAT 文件不存在时创建文件, 属性按基本模式属性
- O_RDWR 读写 O_TRUNC 若文件存在, 将其长度 缩为 0, 属性不变 O_BINARY 打开一个二进制文件
- O_TEXT 打开一个文字文件
open()函数打开成功, 返回值就是文件描述字的值(非负值), 否则返回-1。
2.close()函数
close()函数的作用是关闭由open()函数打开的文件,其调用格式为:
int close(int handle);
该函数关闭文件描述字 handle 相连的文件。
【2】读写函数
1. read()函数
read()函数的调用格式为:
int read(int handle, void *buf, int count);
read()函数从handle(文件描述字)相连的文件中,读取 count 个字节放到 buf 所指的缓冲区中,返回值为实际所读字节数,返回-1表示出错。返回 0 表示文件结束。
2. write()函数
write()函数的调用格式为:
int write(int handle, void *buf, int count);
write()函数把 count 个字节从 buf 指向的缓冲区写入与 handle 相连的文件中,返回值为实际写入的字节数。
【3】随机定位函数
1. lseek()函数
lseek()函数的调用格式为:
int lseek(int handle, long offset, int fromwhere);
该函数对与 handle 相连的文件位置指针进行定位,功能和用法与fseek() 函数相同。
2. tell()函数
tell()函数的调用格式为:
long tell(int handle);
该函数返回与 handle 相连的文件现生位置指针,功能和用法与 ftell()相同。
三、以“流”的名义
在 C++ 中,有一个 stream 这个类,所有的 I/O 都以这个“流”类为基础的,包括我们要认识的文件 I/O,也包括从stream类派生出来的istream、ostream、fstream、iostream、ifstream等等, stream 这个类有两个重要的运算符:
1、插入器(<<)
向流输出数据。系统有一个默认的标准输出流(cout),一般情况下就是指的显示器,所以,cout<<"hello world";就表示把字符串"hello world"输出到标准输出流。
2、析取器(>>)
从流中输入数据。系统有一个默认的标准输入流(cin),一般情况下就是指的键盘,所以,cin>>x;就表示从标准输入流中读取一个指定类型(即变量 x 的类型)的数据。
在 C++ 中,对文件的操作是通过 stream 的子类 fstream(file stream)来实现的。
【1】打开文件
在 fstream 类中,有一个成员函数 open(),就是用来打开文件的,其原型是:
void open(const char* filename,int mode,int access);
参数:
- filename : 要打开的文件名
- mode : 要打开文件的方式
- access: 打开文件的属性
打开文件的方式在类 ios(是所有流式 I/O 类的基类)中定义,常用的值如下:
- ios::app: 以追加的方式打开文件
- ios::ate: 文件打开后定位到文件尾, ios:app 就包含有此属性
- ios::binary: 以二进制方式打开文件,缺省的方式是文本方式。
- ios::in: 文件以输入方式打开
- ios::out: 文件以输出方式打开
- ios::nocreate: 不建立文件,所以文件不存在时打开失败
- ios::noreplace:不覆盖文件,所以打开文件时如果文件存在失败
- ios::trunc : 如果文件存在,把文件长度设为 0
可以用"|"(“或”)把以上属性连接起来,如 ios::out|ios::binar。
打开文件的属性取值是:
- 0:普通文件,打开访问
- 1:只读文件
- 2:隐含文件
- 4:系统文件
可以用“或”(“|”)或者“+”把以上属性连接起来 ,如 3 或 1|2 就是以只读和隐含属性打开文件。
如果你已经创建一个流文件对象,但你没有进行打开文件操作,像这样:
ifstream File; //也可以是一个 ofstream
这样,我们就拥有一个文件句柄。 如果在你的程序的某处,你可能需要知道当前的句柄是否关联了一个已经打开的文件,那么你可以用 is_open()来进行检测。如果文件没有打开,它将返回 0 (false);如果文件已经打开,它将返回 1 (true)。
【2】关闭文件
打开的文件使用完成后一定要关闭,fstream 提供了成员函数 close()来完成此操作,如: fstream::open(file1);file1.close();就把 file1 相连的文件关闭。
【3】读写文件
读写文件分为文本文件和二进制文件的读取,对于文本文件的读取比较简单,用插入器和析 取器就可以了;而对于二进制的读取就要复杂些,下要就详细的介绍这两种方式 :
1、文本文件的读写 (ASCII)
文本文件的读写很简单:用插入器(<<)向文件输出,用析取器(>>)从文件输入。假设 file1 是以输入方式打开,file2 以输出打开。示如下:
- file2<<“hello world”;//向文件写入字符串
- int i;file1>>i;//从文件输入一个整数值。
这种方式还有一种简单的格式化能力,比如可以指定输出为 16 进制等等,具体的格式有以 下一些 :
- 操纵符 功能 输入/输出
- dec 格式化为十进制数值数据 输入和输出
- endl 输出一个换行符并刷新此流 输出
- ends 输出一个空字符 输出
- hex 格式化为十六进制数值数据 输入和输出
- oct 格式化为八进制数值数据 输入和输出
- setpxecision(int p) 设置浮点数的精度位数 输出
比如要把 256当作十六进制输出:file1<<hex<<256;
2、二进制文件的读写(binary)
1)put()
put()函数向流写入一个字符,其原型是 ofstream &put(char ch),使用也比较简单,如 file1.put('c');就是向流写一个字符'c'。
2)get()
get()函数比较灵活,有 3 种常用的重载形式:
一种就是和 put()对应的形式:
ifstream &get(char &ch);
功能是从流中读取一个字符, 结果保存在引用 ch中,如果到文件尾,返回空字符。如 file2.get(x);表示从文件中读取一个字 符,并把读取的字符保存在 x 中。
另一种重载形式的原型是:
int get();
这种形式是从流中返回一个字符,如果到达文件尾, 返回 EOF,如 x=f ile2.get();和上例功能是一样的。
还有一种形式的原型是:
ifstream &get(char *buf,int num,char delim='n') ;
这种形式把字符读入由 buf 指向的数组,直到读入了 num 个字符或遇到了由 delim 指定的字符, 如果没使用 delim 这个参数,将使用缺省值换行符'n'。例如:
file2.get(str1,127,'A');//从文件中读取字符到字符串 str1,当遇到字符'A'或读取了 127 个字符时终止。
3)读写数据块 要读写二进制数据块,使用成员函数 read()和 write()成员函数,它们如下:
- istream &read(unsigned char *buf, streamsize num);
- ostream &write(const unsigned char *buf, streamsize num);
对于read()函数,buf 应当是一个字符数组,由文件读出的数据将被保存在这儿。对于write()函数,buf 是一个字符数组,它用以存放你要写入文件的数据。read()从文件中读取 num 个字符到 buf 指向的缓存中,如果在还未读入 num 个字符时就到了文件尾,可以用成员函数 int gcount();来取得实际读取的字符数;而 write() 从 buf 指向的缓存写 num 个字符到文件中,值得注意的是缓存的类型是 unsigned char *,有时可能需要类型转换。
【4】检测 EOF
成员函数 eof()用来检测是否到达文件尾,如果到达文件尾返回非 0 值,否则返回 0。原型是 int eof(); 例:if(in.eof())printf("已经到达文件尾\n");
【5】文件定位
C++ I/O 系统管理两个与一个文件相联系的指针。一个是读指针,它说明输入操作在文件中的位置;另一个是写指针,它下次写操作的位置。每次执行输入或输出时,相应的指针自动变化。所以,C++ 的文件定位分为读位置和写位置的定位,对应的成员函数是seekg()和 seekp(),seekg()是设置读位置,seekp 是设置写位置。它们最通用的形式如下:
- istream &seekg(streamoff offset,seek_dir origin);
- ostream &seekp(streamoff offset,seek_dir origin);
streamoff 定义于 iostream.h 中,定义有偏移量 offset 所能取得的最大值,seek_dir 表示移动的基准位置,是一个有以下值的枚举:
- ios::beg: 文件开头
- ios::cur: 文件当前位置
- ios::end: 文件结尾
这两个函数一般用于二进制文件,因为文本文件会因为系统对字符的解释而可能与预想的值 不同。