C/C++文件操作——输入输出流

一、输入输出流相关类

如下图所示:

ios_base是ios类的基类,分别派生出 istream 和 ostream 两个输入输出类,

iostream 类又继承了 istream 和 ostream 的输入输出,成为包括 istream 和 ostream 的输入输出类,

ifstream 类继承了istream ,ofstream 继承了 ostream类,

fstream 类又继承了 ifstream 和 ofstream两个输入输出文件类,成为包括 ifstream 和 ofstream 的输入输出文件类,

关于输入输出文件类的介绍如下:

ofstream //文件写操作,内存写入存储设备(由ostream引申而来)

ifstream  //文件读操作,存储设备读区到内存中(由istream引申而来)

fstream  //读写操作,对打开的文件可进行读写操作(由iostream引申而来)

二、基于C++的文件操作

1. 通过类对象构造函数打开文件

类ofstream, ifstream 和 fstream的对象都可以调用open函数打开文件,这些类都有一个构造函数可以直接调用open 函数。

open函数原型:void open (const char * filename, openmode mode);

头文件:#include <fstream>

参数:

filename 是一个字符串,代表要打开的文件名;

mode 是以下标志符的一个组合(这些标识符可以被组合使用,中间以”或”操作符“|”间隔):

  • ios::out 文件以输出(写)方式打开 
  • ios::in    文件以输入(读)方式打开
  • ios::ate 文件打开后定位到文件尾(ios:app就包含有此属性)
  • ios::app 以追加方式打开文件
  • ios::trunc 如果文件已存在则先删除该文件
  • ios::nocreate 不建立文件,所以文件不存在时打开失败
  • ios::binary 二进制方式

注:文件的输入输出是从内存的角度看的:数据载入内存(读数据)叫做输入,数据从内存到其他地方(写数据)叫做输出。

范例:

#include<fstream>
using namespace std;
int main()
{
	//通过ofstream类打开文件
	ofstream file;
	file.open("example.bin", ios::in | ios::binary);
	file.close();

	//通过fstream类打开文件
	fstream file1;
	file1.open("example.bin", ios::binary | ios::in);
	file1.close();

	//open函数只有文件名一个参数时,默认以读/写普通文件打开
	file1.open("example.bin");
	file1.close();

	//fstream有两个子类:ifstream(input file stream)和ofstream(output file stream)
	fstream file2("example.bin");
	file2.close();

	//ifstream默认以输入方式打开文件
	ifstream file3("example.bin");
	file3.close();

	//ofstream默认以输出方式打开文件
	ofstream file4("example.bin");
	file4.close();
	system("PAUSE");
}

二、读写文件

读写文件基本可分为对文本文件和二进制文件的读取。

1 文本文件的读写

1.1 文本文件的写入:用插入器(<<)向文件输入

#include<fstream>
using namespace std;
int main()
{
	//通过ofstream类打开文件
	ofstream file;
	file.open("example.bin");//文件默认在工程目录debug文件夹下
	file << "你好!";
	//以16进制输入值
	file << hex<<123;
	file.close();
	system("PAUSE");
}

操纵符

功能

输入/输出

Dec

格式化为十进制数值数据

输入和输出

endl

输出一个换行符并刷新此流

输出

ends

输出一个空字符

输出

hex

格式化为十六进制数值数据

输入和输出

oct

格式化为八进制数值数据

输入和输出

setpxecision(int p)

设置浮点数的精度位数

输出

 范例,新建一个文件并写入:

#include<fstream>
using namespace std;
int main()
{
	//通过ofstream类打开文件
	ofstream file("example.bin");//文件不存在则新建
	if (file.is_open())
	{
		file << "First Line!" << endl;
		file << "Second Line!";
		file.close();
	}
	system("PAUSE");
}

1.2 文本文件的读入:用cout(<<)打印 

#include <iostream>
#include <fstream> 
#include <stdlib.h> 
using namespace std;
int main() 
{
	char buffer[256];
	ifstream file("example.txt");
	if (file.is_open())
	{
		cout << "Error opening file"; exit(1);
	}
	while (!file.eof())//当到达文本末返回true
	{
		file.getline(buffer, 100);
		cout << buffer << endl;
	}
	system("PAUSE");
	return 0;
}

其中,file.getline函数的说明如下:

函数原型:istream& getline (char* s, streamsize n );

作用是从istream中读取至多n个字符保存在s对应的数组中。即使还没读够n个字符,如果遇到换行符'\n'(第一种形式)或delim(第二种形式),则读取终止,'\n'或delim都不会被保存进s对应的数组中,即按行读入。

一些常见的状态标识符(返回值均为bool类型):

bad():如果在读写过程中出错,返回 true

例如:当对一个不可写文件进行写入操作时,或者内存分配不足时。

fail():除了与bad() 同样的情况下会返回 true 以外,格式错误时也返回true。例如:当想要读入一个整数,却获得了一个字母的时候。
eof():如果读文件到达文件末尾,返回true。原型是int eof(); 如:if(in.eof()) ShowMessage("已经到达文件尾!");
good():常用,如果调用以上任何一个函数返回true 的话,此函数返回 false

注:要想重置以上成员函数所检查的状态标志,可使用clear()函数,无参数。

2.二进制文件的读写

2.1二进制文件单个字符的读写

       首先,在二进制文件中,使用<< 和>>,以及函数(如getline)来操作符输入和输出数据,没有什么实际意义,虽然它们是符合语法的。文件流包括两个为顺序读写数据特殊设计的成员函数:write 和 read,可以说是为二进制文件的读写量身定做。write函数是ostream 的一个成员函数,被ofstream所继承。而read 是istream 的一个成员函数,被ifstream 所继承。类 fstream 的对象同时拥有这两个函数。它们的原型是:

write ( char * buffer, streamsize size );
read ( char * buffer, streamsize size );

参数buffer 是一块内存的地址,用来存储或读出数据。参数size 是一个整数值,表示要从缓存(buffer)中读出或写入的字符数。

// reading binary file
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
	const char * filename = "example.txt";
	char * buffer;
	long size;
	ifstream file(filename, ios::in | ios::binary | ios::ate);
	size = file.tellg();//用于输入流,返回流中‘get’指针当前的位置
	file.seekg(0, ios::beg);
	buffer = new char[size];
	file.read(buffer, size);
	file.close();
	delete[] buffer;
	return 0;
}
//The complete file is in a buffer

对于输入流/输出流的调用:

对输入流操作:seekg()与tellg()用来改变流指针get的位置
对输出流操作:seekp()与tellp()用来改变流指针put的位置

其中,
下面以输入流函数为例介绍用法:
seekg()和seekp()被重载为两种原型:

(1)seekg ( pos_type position );

         seekp ( pos_type position );

         流指针被改变为指向从文件开始计算的一个绝对位置。要求传入的参数类型与函数 tellg 和tellp 的返回值类型相同。

         建议以文本格式读写内容使用此原型,避免对tellg 或 tellp 的返回值进行修改。

(2)seekg ( off_type offset, seekdir direction );

          seekp ( off_type offset, seekdir direction );

          可以指定由参数direction决定的一个具体的指针开始计算的一个位移(offset),即对输入文件定位。建议以二进制格式读写内容使用此原型。它有两个参数:第一个参数是偏移量,第二个参数是基地址。对于第一个参数,可以是正负数值,正的表示向后偏移,负的表示向前偏移。而第二个参数可以是:
ios::beg:表示输入流的开始位置
ios::cur: 表示输入流的当前位置
ios::end:表示输入流的结束位置
tellg()函数不需要带参数,它返回当前定位指针的位置,也代表着输入流的大小。

如:file1.seekg(1234,ios::cur);      //把文件的读指针从当前位置向后移1234个字节

// obtaining file size
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
	const char * filename = "example.txt";
	long l, m;
	ifstream file(filename, ios::in | ios::binary);
	l = file.tellg();
	file.seekg(0, ios::end);
	m = file.tellg();
	file.close();
	cout << "size of " << filename;
	cout << " is " << (m - l) << " bytes.\n";
	system("PAUSE");
	return 0;
}

 当然,也可以通过以下方式快速遍历文件获取大小:

// get file size using buffer's members
std::filebuf* pbuf = ifs.rdbuf();
std::size_t size = pbuf->pubseekoff(0, ifs.end, ifs.in);
pbuf->pubseekpos(0, ifs.in);
std::cout << "file size : " << size << " bytes" << std::endl;

 常用读写函数:

(1)put函数:向流写入一个字符,其原型是ofstream &put(char ch),使用也比较简单,如file1.put(‘c’);就是向流写一个字符’c’。

(2)get函数有三种重载形式:

 

         ifstream &get(char &ch);  

        形式和put()对应,功能是从流中读取一个字符,结果保存在引用ch中,如果到文件尾,返回空字符。

        如file2.get(x);    表示从文件中读取一个字符,并把读取的字符保存在x中。

       int get();

       功能是从流中返回一个字符,如果到达文件尾,返回EOF,如x=file2.get(); 和上例功能是一样的。

      ifstream &get(char *buf,int num,char delim=’n’);

     功能是把字符读入由 buf 指向的数组,直到读入了 num 个字符或遇到了由 delim 指定的字符。缺省值为换行符’n’。

     例如: file2.get(str1,127,’A’); 表示从文件中读取字符到字符串str1,当遇到字符’A’或读取了127个字符时终止。

 2.2 二进制文件数据块的读写

要读写二进制数据块,依然使用成员函数read()和write()成员函数,但与上例不同,它们原型如下:

read(unsigned char *buf,int num);
write(const unsigned char *buf,int num);
read() 从文件中读取 num 个字符到 buf 指向的缓存中,如果在还未读入 num 个字符时就到了文件尾,可以用成员函数 int gcount();来取得实际读取的字符数; write() 从buf 指向的缓存写 num 个字符到文件中,值得注意的是缓存的类型是 unsigned char *,有时可能需要类型转换。

#include <fstream>
using namespace std;
int main()
{
    char str1[] = "This is an example";
	int n[5];
	ifstream filein("xxx.xxx");
	ofstream fileout("yyy.yyy");
	fileout.write(str1, strlen(str1));//把字符串str1全部写到yyy.yyy中
	filein.read((char*)n, sizeof(n));//从xxx.xxx中读取指定个整数,注意类型转换
	filein.close(); 
	fileout.close();
}

三、基于C语言的文件操作

1. ANSI C定义

在ANSI C中,对文件的操作分为两种方式,即流式文件操作和I/O文件操作,下面就分别介绍之。

1.1 流式文件操作:这种方式的文件操作有一个重要的结构FILE,FILE在stdio.h中定义如下:

typedef struct
{
    short           level;          /* fill/empty level of buffer */
    unsigned        flags;          /* File status flags    */
    char            fd;             /* File descriptor      */
    unsigned char   hold;           /* Ungetc char if no buffer */
    short           bsize;          /* Buffer size          */
    unsigned char   *buffer;        /* Data transfer buffer */
    unsigned char   *curp;          /* Current active pointer */
    unsigned        istemp;         /* Temporary file indicator */
    short           token;          /* Used for validity checking */
}FILE;    /* This is the FILE object */
FILE这个结构包含了文件操作的基本属性,对文件的操作都要通过这个结构的指针来进行。

 C语言中没有输入输出语句,所有的输入输出功能都用 ANSI C提供的一组标准库函数来实现:     

         文件的打开操作 fopen 打开一个文件

      文件的关闭操作 fclose 关闭一个文件

      文件的读写操作 fgetc 从文件中读取一个字符

              fputc 写一个字符到文件中去

              fgets 从文件中读取一个字符串

              fputs 写一个字符串到文件中去

              fprintf 往文件中写格式化数据

              fscanf 格式化读取文件中数据

              fread 以二进制形式读取文件中的数据

              fwrite 以二进制形式写数据到文件中去

              getw 以二进制形式读取一个整数

              putw 以二进制形式存贮一个整数

    文件状态检查函数  feof 文件结束

              ferror 文件读/写出错

              clearerr 清除文件错误标志

              ftell 了解文件指针的当前位置

           文件定位函数 rewind 反绕

              fseek 随机定位

 

2.C语言通过调用fopen函数打开文件

2.1 fopen函数

fopen函数原型:FILE * fopen(const char * path,const char * mode);

头文件:#include <stdlib> (标准C语言函数库)

参数:

path:文件路径,mode:打开方式 

     r        以只读方式打开文件,该文件必须存在。  

     r+     以可读写方式打开文件,该文件必须存在。  

     rb+   读写打开一个二进制文件,允许读数据。  

     rw+  读写打开一个文本文件,允许读和写。  

     w      打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。  

     w+    打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。  

     a       以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容被保留。(EOF符保留)  

     a+     以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容被保留。 (原来的EOF符不保留)  

    wb     只写打开或新建一个二进制文件;只允许写数据。  

    wb+   读写打开或建立一个二进制文件,允许读和写。  

    wt+    读写打开或着建立一个文本文件;允许读写。  

   at+     读写打开一个文本文件,允许读或在文本末追加数据。     

  ab+    读写打开一个二进制文件,允许读或在文件末追加数据。  

上述的形态字符串都可以再加一个b字符,如rb、w+b或ab+等组合,加入b 字符用来告诉函数库打开的文件为二进制文件,而非纯文字文件。    

返回值:文件顺利打开后,指向该流的文件指针会被返回。如果文件打开失败则返回NULL,并把错误代码存在errno中。

范例:

#include<stdio.h>
int main() 
{
	FILE *fp;
	fp = fopen("C://Users//61417//Desktop//Document//Document01//example.bin", "r");
	if (NULL == fp)
	{
		return 0;//返回错误代码
	}
	fclose(fp);
	return 0;
}

但是,本人的编译器是VS2015,由于编译器对文件操作安全性有了更高的要求,所以出现了下面的编译错误:

更正为fopen_s函数如下:

#include<stdio.h>
#include <stdlib.h>
int main() 
{
	FILE *fp;
	fopen_s(&fp,"C:\\Users\\61417\\Desktop\\Document\\Document01\\example.bin", "r");
	if (NULL == fp)
	{
		return -1;//返回错误代码
	}
	fclose(fp);
	system("PAUSE");
	return 0;
}

2.2  freopen函数

相关函数  fopen,fclose

头文件     #include<stdio.h>

函数原型:  FILE * freopen(const char * path,const char * mode,FILE * stream);

参数:

参数1 path:字符串包含欲打开的文件路径及文件名,

参数2 mode请参考fopen()说明。

参数3 stream为已打开的文件指针。

freopen()会将原stream所打开的文件流关闭,然后打开参数path的文件。

返回值 :文件顺利打开后,指向该流的文件指针就会被返回,打开失败则返回NULL,并把错误代码存在errno 中。

#include<stdio.h>
main()
{
	FILE * fp;
	fp = fopen("./example.txt", "r");
	fp = freopen(" ./example.txt", "r", fp);
	fclose(fp);
}

由于本人使用的编译环境是VS2015,fopen函数不能正常编译,所以关于C语言部分的文件操作就不赘述了。

  • 12
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值