17.4 输入输出流--文件操作与文件流

17.4 输入输出流–文件操作与文件流


4.1 文件的分类

  1. 按数据的组织形式可以分为ASCII码文件二进制文件:

例如:
存储int型整数100000,前者为6byte,后者4byte;
double型双精度数123.45,前者分别存储6个字符(‘1’,‘2’,‘3’,’.’,‘4’,‘5’)6byte,后者为double 8byte.

  1. C++对文件的访问可以分为:低级I/0(字符流方式I/O),高级I/O(转换为数据指定形式I/O)

4.2 文件流类与文件流对象

  1. 与磁盘文件有关的流类:

输入:ifstream类,派生自istream类
输出:ofstream类,派生自ostream类
输入/输出:fstream类,派生自iostream类

4.3 文件的打开与关闭

#include <fstream>
ofstream out1("../aa.dat",ios::out|ios::app);
/*另一种方式*/
ofstream out2;
out2.open("../aa.dat",ios::out|ios::app);

打开方式(用位或’|'组合):

方式作用
ios::in输入方式
ios::out输出方式(默认方式),如果已存在该名称文件:清空原有内容
ios::app输出方式,写入数据添加于文件末尾
ios::ate打开一个已有文件,文件指针指向文件末尾
ios::trunc打开一个文件,若已存在,则删除其中全部数据;若不存在,则建立新文件.如果已经指定ios::out方式,而未指定ios::app,ios::ate,ios::in,则同时默认此方式
ios::binary二进制方式打开,否则默认ASCII方式
ios::nocreate打开已有的文件,若不存在,则不建立新文件打开失败
ios::noreplace若文件不存在则建立新文件,若存在则操作失败,不更新替代原有文件
  1. 注意:
    1. DevC++和Linux下不支持nocreate和noreplace,VS下写为_Nocreate,_Noreplace
    2. 目录分隔符建议使用’/’,Linux/Windows均适用.
    3. 打开方式需要兼容,例如:ifstream对象以ios::out形式打开无意义,认为ios::in.
  2. 判断打开成功:
    if(!out1)
    
    if(out1.is_open()==0)
    
    out1.close();
    

4.4 对ASCII文件的操作

基本与cin/cout相同,且可以使用get/getline/put/eof/peek/puchar/ignore等成员函数.同时对空格/回车等等处理方式也相同.事实上流插入流提取已经对二进制数据做了十进制处理.

  1. 一个关于eof的辨析:
    文件共5字节大小:内容为12345
#include <iostream>
#include <fstream>
using namespace std;

int main()
{
	ifstream in;
	char ch;
	in.open("D:/f1.dat", ios::in);
	/*省略is_open()*/
	int cnt = 0;

	while (!in.eof()) {
		in.get(ch);
		putchar(ch);
		cnt++;
	}

	//while ((ch = in.get()) != EOF)
	//{
	//	putchar(ch);
	//	cnt++;
	//}

	cout << '\n' << cnt;

	in.close();

	return 0;
}

执行结果1:
在这里插入图片描述

执行结果2:
在这里插入图片描述

可以简单理解文件中内容为:1 2 3 4 5 EOF,按1方式,in只有读到(get)EOF之后才会置eof为1.方法二最终ch为-1,但方法一为’5’.

  1. 接上:
	char ch;
	ifstream in("D:/f1.dat", ios::in);
	ofstream out("D:/f2.dat", ios::out);

	/*省略打开成功判断*/

	/*方法一会比源文件多输出FF(-1)*/
	while (!in.eof()) {
		ch = in.get();
		out.put(ch);
	}

	//while (in.get(ch))
	//	out.put(ch);

综上,还是推荐 (ch = in.get()) != EOF 这种判断写法.

4.5 对二进制文件的操作

ASCII读写存在两个问题:

  1. 仅能按照字节读写
  2. 若文件中有字符’0x1A’则无法继续读取(文本文件不可能有)
  1. 一般用read/write进行二进制读写的操作:
    在这里插入图片描述
  2. 与指针相关的流成员函数:
输入
gcount()返回最后一次读入的字节数.read参数中的长度为最大读取长度,并非实际读取长度,用gcount()可知真实读取字节数.
tellg()当前指针位置,按get记忆
seekf(位移量,位移方式)移动指针
输出
tellp()当前指针位置,按put记忆
seekp(…)
位移方式
ios::beg从头位移量为正
ios::cur从当前
ios::end从尾,位移量为负

fstream的tellg和tellp是同步的.

4.6 差异比较

1. 不同方式下输出endl

十进制方式写(ios::out):endl在Windows下为’\r’ ‘\n’ 在Linux下为’\n’;
二进制方式写(ios::out|ios::binary)均为’\n’

2. 不同方式下读取"\r\n"

十进制读(ios::in),Windows下"合二为一",视为’\n’,即一个字符处理;
二进制读(ios::in|ios::binary),Windows仍为"\r\n"

3. >>和getline()

差别就是>>识别’\r’或’\n’,遇之结束读取不丢弃,getline识别’\n’为止,读完’\n’丢弃之.

全部以十进制方式读写:
读取输出的fout<<“hello”<<endl,实际上读取的内容为"hello\n",

  1. char buf[80];
    fin>>buf;
    cout<<in.peek()<<endl;
    这种方式读到"hello"结束,’\n’留在缓冲区(同cin),peek()输出’\n’;
  2. fin.getline(buf,80);
    这种方式尽管strlen(buf)也为5,但是’\n’被读掉,未入buf,但也不在缓冲区了,此时in.peek()读到EOF返回-1.

十进制写,二进制读,即测试"\r\n",仍然按上述方式来:

  1. fin.peek()读到’\r’,缓冲区此时为’\r’ ‘\n’;
  2. fin.peek()读到EOF,缓冲区’\n’被读走并丢弃,此时buf中的字符串长度strlen()首次变为6,即"hello\r"

4. 十进制方式下写入特殊字符的测试

均在 ios::out 方式下

  1. 写入含’\0’的文件

out<<“ABC\0\x61\x62\x63”<<endl;
最终结果Windows下为"ABC\r\n",Linux少一个’\r’,说明out遇\0视为字符串的结束.

  1. 写入非图形字符

out<<“ABC\x1\x2\x1A\t\v\b\xff\175()-=def”<<endl;
最终结果Windows下文件大小20字节,即一字不拉的输出了,而Linux下19字节,理由同前.说明非图形字符不影响输出,但不应包含\0.

  1. 写入’\x1A’ (Ctrl+Z)

实际上写入不存在任何问题,和写入非图形字符是一样的;
读取时,注意只有Windows下的ios::in(十进制方式)才会将’\x1A’特殊处理为EOF,Windows二进制读和Linux下均不存在该问题!

  1. 写入’\xFF’ (-1)
    这是一个很有意思的区别:
    1. in加上binary也一样:
      在这里插入图片描述
    2. in加上binary也一样:
      在这里插入图片描述
      为啥后者会被\xff截断而前者不会?其实道理也很简单,因为in.get()实际上返回的是int型值,而非char型值,因此读入\xff实际上返回的是255而非-1.这个地方需要多加注意,尤其是字符方式读取非文本文件时,常常会出现超出ASCII范围的值,需要考虑是不是应该使用int代替char.

5. 对read()的一些辨析

  1. 对比格式化读>>和read()
  1. in>>读入字符串和cin并无区别,读到’\n’或’\r’停止,并自动添加’\0’;read()方式不会管你这些’\n’’\0’的规则,因此若不人工管一管尾0,再使用<<输出时会越界;
  2. 此外,tellg两种方式都有效,gcount仅对read有效;
  3. read()读取的大小超出文件长度时,以gcount为准可知实际读取的长度,tellg()置为-1,good()置为0.这点和sstream是一样的.此时若不重新置位(in.clear()),之后的tellg()/seekg()/gcount()均不可信.
if(!in.good())
	in.clear();
  1. 若读取不超过文件长度,则good()为1,tellg()为实际位置.
  2. 若同时使用ios::in|ios::out,这两个指针将完全同步,使用其一即可.

6. 对ios::app的一些辨析

加入ios::app后,尽管seekg()/seekp()可以移动文件指针,但是写入的位置始终位于文件的末尾.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值