从零开始学C++之IO流类库(三):文件的读写、二进制文件的读写、文件随机读写

一、文件的读写

前面所提,流的读写主要有<<, >>, get, put, read, write 等操作,ofstream 继承自ostream, ifstream 继承自 istream,故操作函数都是一致的。

#include <iostream>
#include <fstream>
#include <string>
#include <cassert>
using namespace std;

int main(void)
{
    ofstream fout("test.txt");
    fout << "abc" << " " << 200;
    fout.close();

    ifstream fin("test.txt");
    string s;
    int n;
    //fin>>n>>s;
    fin >> s >> n;
    cout << s << " " << n << endl;

    ofstream fout1("test2.txt");
    assert(fout1);
    char ch;

    for (int i = 0; i < 26; i++)
    {
        ch = 'A' + i;
        fout1.put(ch);
    }
    fout1.close();

    ifstream fin1("test2.txt");
    while (fin1.get(ch))
    {
        cout << ch;
    }
    cout << endl;


    return 0;
}

二、二进制文件的读写

二进制文件不同于文本文件,它可用于任何类型的文件(包括文本文件) 对二进制文件的读写可采用从istream类继承下来的成员函数read()和从ostream类继承下来的成员函数write() 文件打开操作时使用枚举常量ios::binary,例如:ofstream fout(“binary.dat”,ios::out | ios::binary);

(一)、write成员 函数

函数功能:以字节为单位向文件流中写入整块数据,最有价值的应用可以处理结构体变量和类对象 函数原型:

ostream& write( const char* pch, int nCount );

函数参数: pch 写入的数据的指针 nCount 写入数据的字节大小

(二)、read 成员 函数

函数功能:从文件流中读出整块数据 函数原型:

istream& read( char* pch, int nCount ); 

函数参数: pch 用来接收数据的指针 nCount 读取的字节数的大小

#include <cassert>
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

struct Test
{
    int a;
    int b;
};

int main(void)
{
    ofstream fout("test3.txt", ios::out | ios::binary); // 二进制方式打开,'\n'不做转换
    fout << "ABC\n"; // << 是以文本方式写入
    fout.close();

    Test test = { 100, 200 };
    ofstream fout1("test4.txt", ios::out | ios::binary);
    fout1.write(reinterpret_cast<char *>(&test), sizeof(Test));
    // 二进制方式写入后用文本编辑器打开test4.txt 乱码
    fout1.close();

    Test test2;
    ifstream fin("test4.txt", ios::in | ios::binary);
    fin.read(reinterpret_cast<char *>(&test2), sizeof(Test));
    cout << test2.a << " " << test2.b << endl;

    ofstream fout2("test5.txt", ios::out | ios::binary);
    fout2 << "abc" << 200; // << 是以文本方式写入
    fcout2.close();

    return 0;
}

在window下以文本方式打开文件,则以文本方式写入时遇到'\n' , 转换为'\r\n',以二进制方式打开则不做转换,故test3.txt 文件大小为4个字节。

而写入100(write 是以二进制方式写入)就不再是写入'1', '0' ,' 0' 的ascii 码,而是按照内存本来二进制形式写入,故用文本编辑器打开test4.txt 时会出

现乱码。文件大小为8个字节(两个int)。同理,test5.txt 虽然以二进制打开,但是以文本方式(<< 是以文本方式写入)写入的,故写入200后用文本

编辑器打开不会出现乱码,文件大小为6个字节。

有关文本文件与二进制文件的区别,请参考这里

使用read, write 读取string 的时候需要注意,string 实际上内部是一些指针成员,sizeof(string)=32 (跟编译器实现有关),即string 大小是一定的,

而它的指针成员保存的字符串长度不一定是32,故我们应该这些读写:

string str1 = "fdsfafsdsf";
int len = str1.length();
ofstream fout("test6.txt", ios::out | ios::binary);
fout.write(str1.data(), str1.length());

string str2;
str2.resize(len);
ifstream fin("test6.txt", ios::in | ios::binary);
fin.read(&str2[0], len);

如果像这样写入  fout.write((char*)&str1, sizeof(str1)); 一定是错误的,因为写入的是str1 的指针成员,而不是指针成员指向的字符串,而且str1 的大小恒等于32。

三、文件随机读写

(一)、当前文件流活动指针

文件流指针用以跟踪发生 I/O 操作的位置 每当从流中读取或写入一个字符,当前活动指针就会向前移动 当打开方式中不含有ios::ate或ios::app选项时,则文件指针被自动移到文件的开始位置,即字节地址为0的位置。

(二)、文件的随机读写 seekp和seekg

seekp 和 seekg 类似与C库的fseek, linux系统调用的lseek。

函数功能

seekp:设置输出文件流的文件流指针位置 seekg:设置输入文件流的文件流指针位置

函数原型:

ostream& seekp( streampos pos ); ostream& seekp( streamoff off, ios::seek_dir dir ); istream& seekg( streampos pos ); istream& seekg( streamoff off, ios::seek_dir dir );

函数参数

pos:新的文件流指针位置值 off:需要偏移的值 dir:搜索的起始位置

dir参数用于对文件流指针的定位操作上,代表搜索的起始位置 在ios中定义的枚举类型:

enum seek_dir {beg, cur, end};

每个枚举常量的含义: ios::beg:文件流的起始位置 ios::cur:文件流的当前位置 ios::end:文件流的结束位置

tellp 和 tellg 类似C库的ftell,,linux 系统调用的lseek(fd, 0, SEEK_CUR);

函数功能

tellp:获得输出的文件流指针的当前位置,以字节为单位 tellg:获得输入的文件流指针的当前位置,以字节为单位

函数原型:

streampos tellp(); streampos tellg();

函数返回值:实际上是一个long类型

#include <cassert>
#include <iostream>
#include <fstream>
#include <string>

using namespace std;


int main(void)
{
    ifstream fin("test7.txt");
    assert(fin);
    fin.seekg(2);//位置从0开始计数

    char ch;
    fin.get(ch);
    cout << ch << endl;

    fin.seekg(-1, ios::end); //end 实际上是EOF位置
    fin.get(ch);
    cout << ch << endl;

    fin.seekg(0, ios::end);
    streampos pos = fin.tellg();
    cout << pos << endl;

    return 0;
}

假设test7.txt 现在存放abcdefg 7个字符,则输出为c g 7 .

参考:

C++ primer 第四版 Effective C++ 3rd C++编程规范

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏阿凯的Excel

Python读书笔记12(IF语句应用)

今天和大家分享的是所有函数、所有编程的基础,判断! IF语句! IF语句肯定是进行判断,为真怎样,为假如何。 那这个真假就是某个条件是否满足,和Python相...

3439
来自专栏拂晓风起

C++调用C链接库会出现的问题

1063
来自专栏Astropeak

为什么不需要为Python对象添加 getter 和 setter

1132
来自专栏软件开发 -- 分享 互助 成长

C++STL 之排列

固然我们可以自己使用递归编写全排列程序,但是既然STL里面已将有了这个功能为什么不直接用呢,下面就写一下直接使用C++ STL生成全排序的程序 函数名:next...

1907
来自专栏我的博客

php命名空间详解

1、命名空间概述 从广义上来说,命名空间是一种封装事物的方法。在很多地方都可以见到这种抽象概念。例如,在操作系统中目录用来将相关文件分组,对于目录中的文件来说,...

2848
来自专栏Python小屋

详解Python对象属性

在面向对象编程中,公开的数据成员可以在外部随意访问和修改,很难控制用户修改时新数据的合法性。解决这一问题的常用方法是定义私有数据成员,然后设计公开的成员方法来提...

3038
来自专栏夏时

PHP 特色:可变变量

1014
来自专栏王亚昌的专栏

Golang 中的“潜规则”

./example_struct2binary.go:21: head.ver undefined (cannot refer to unexported fi...

772
来自专栏LuckQI

Redis~Hash命令初识

1252
来自专栏Python爬虫与数据挖掘

Python正则表达式初识(一)

首先跟大家简单唠叨两句为什么要学习正则表达式,为什么在网络爬虫的时候离不开正则表达式。正则表达式在处理字符串的时候扮演着非常重要的角色,在网络爬虫的时候...

973

扫码关注云+社区