C++ 文件操作篇

C++ 文件操作篇

文章目录

  • C++ 文件操作篇
  • 1 简介
    • 1.1 继承关系
    • 1.2 流
    • 1.3 缓冲区
      • 输入输出流中的缓冲streambuf
  • 2 文件操作步骤
    • 2.1 头文件
    • 2.2 创建流对象
    • 2.3 打开文件
    • 2.4 读取数据
      • 第一种:**按元素直接读**
      • 第二种:**使用getline按行读**
      • 第三种:**使用*get***
    • 2.5 写入数据
    • 2.6 fstream类
    • 2.7 关闭文件
  • 3 数据类型转换
  • 4 状态标志符的验证
  • 5 获得和设置流指针
  • 6 二进制文件读写
    • 6.1 写文件
    • 6.2 读文件
  • 7 缓存和同步
  • 参考文档:

1 简介

1.1 继承关系

请添加图片描述

这里文件操作主要有:

ios->ostream->ofstream
ios->istream->ifstream
iostream->fstream
(还继承了两个缓冲区,一个用于输入,一个用于输出)
(此类用于文件I/O的同步,即协调地移动输入缓冲区的输入指针和输出缓冲区的输出指针)
(输入输出模式时使用fstream类)

1.2 流

在程序设计中,数据输入/输出(I/O)操作是必不可少的,C++语言的数据输入/输出操作是通过I/O流库来实现的。
C++中把数据之间的传输操作称为流,

**输出流:**数据从内存传送到某个载体或设备中
**输入流:**某个载体或设备传送到内存缓冲区变量中

  • 标准I/O流:内存与标准输入输出设备之间信息的传递;
  • 文件I/O流:内存与外部文件之间信息的传递;
  • 字符串I/O流:内存变量与表示字符串流的字符数组之间信息的传递
流类分类流类名称流 类 作 用
流基类ios所有流类的父类,保存流的状态并处理错误
输入流类istream输入流基类,将流缓冲区中的数据作格式化和非格式化之间的转换并输入 ifstream
ifstream文件输入流类
stream_withassigncin输入流类,即操作符>>输入流
istrstream串输入流类, 基于C类型字符串char*编写
istringstream串输入流类, 基于std::string编写
输出流类ostream输出流基类,将流缓冲区中的数据作格式化和非格式化之间的转换。并输出
ofstream文件输出流类
ostream_withassignCout、cerr、clog的输出流类,即操作符<<输出流
ostrstream串输入流类, 基于C类型字符串char*编写
ostringstream串输入流类, 基于std::string编写
输入/输出流类iostream多目的输入/输出流类的基类
fstream文件流输入/输出类
strstream串流输入/输出类, 基于C类型字符串char*编写
stringstream串流输入/输出类, 基于std::string编写

1.3 缓冲区

文件输出输入使用缓冲区,在声明每个ofsream or ifsream对象时程序会为其自动分配该对象自己的缓冲区;分为输入缓冲区输出缓冲区

缓冲区就是一块存储空间,它是为了匹配程序处理速度和外设处理速度;比如程序一次处理1byte,但是磁盘一次读取512bytes;又或者程序一次处理1byte,可以1byte地从磁盘读取,但是由于硬件读取一次数据复杂且操作慢,因此使用缓冲区可以加快程序处理速度。

何时清空缓存: 1.缓存区满时 2.使用文件close()方法时,为了保证关闭文件时文件被更新。

输入输出流中的缓冲streambuf

  • stl(标准库)提供了缓冲区类streambuf,提供给输入输出流使用,每个标准的输入输出流对象均包含一个streambuf指针
  • 可以通过调用rdbuf()获取该指针,从而直接访问底层streambuf对象
  • streambuf最精彩的部分在于它支持<<,>>操作,以及迭代器操作,支持自定义。

2 文件操作步骤

2.1 头文件

#include <fstream>

2.2 创建流对象

ofstream fout("xxx.txt");        //文件写操作 内存写入存储设备 
ifstream fin("xxx.txt");       //文件读操作,存储设备读区到内存中
fstream  foi("xxx.txt");        //读写操作,对打开的文件可进行读写操作 

2.3 打开文件

在从文件读取信息或者向文件写入信息之前,必须先打开文件。ofstreamfstream 对象都可以用来打开文件进行写操作,如果只需要打开文件进行读操作,则使用 ifstream 对象。

void open(const char *filename, ios::openmode mode);  // filename : 文件路径  openmode : 打开方式
模式标志描述
ios::app追加模式。所有写入都追加到文件末尾。
ios::ate文件打开后定位到文件末尾。
ios::in打开文件用于读取。
ios::out打开文件用于写入。
ios::trunc如果该文件已经存在,其内容将在打开文件之前被截断,即把文件长度设为 0。

例如,要以写入模式打开文件,并希望截断文件,以防文件已存在

ofstream fout;
fout.open("file.dat", ios::out | ios::trunc );

如果想要打开一个文件用于读写

ifstream  afile;
afile.open("file.dat", ios::out | ios::in );

*** 可以不显式的调用open()函数 ***

ofstream out("xxx.txt", ios::out);
ifstream in("xxx.txt", ios::in);
fstream foi("xxx.txt", ios::in|ios::out);
/* 直接调用了其默认的打开方式,因为在stream类的构造函数中调用了open()函数,并拥有同样的构造函数,所以在这里可以直接使用流对象进行文件的操作 */

成员函数is_open()可以对文件是否打开进行验证

2.4 读取数据

① 为了保持健壮性,读文件要有验证代码

ifstream fin("xxx.txt", ios::in);

if(!fin.is_open())
{
    std::cerr<<"cannot open the file"
}
//或者
if(!fin)
 {
    std::cerr<<"cannot open the file";
}  

② 读取文件

第一种:按元素直接读

	string tmp;
	ifstream fin("writein.txt");

	if (!fin){
		cerr << "Fail!\n";
	}
	else{
		// 这种方法会从fin中逐个单词读取(也就是遇到空格、换行时会停止)
		while (fin >> tmp)
		{
			cout << tmp << endl;
		}
		fin.close();
	}

第二种:使用getline按行读

输入流成员函数getline()用法

getline是C++标准库函数;它有两种形式,一种是头文件< istream >中输入流成员函数;一种在头文件< string >中普通函数;

语法结构:

istream& getline (char* s, streamsize n );
istream& getline (char* s, streamsize n, char delim );

istream中读取至多n个字符(包含结束标记符)保存在s对应的数组中。即使还没读够n个字符,如果遇到delim 字数达到限制,则读取终止delim都不会被保存进s对应的数组中。

  • 读入了文件结束标志 EOF
  • 读到一个新行
  • 达到字符串的最大长度

–如果getline没有读入字符,将返回false,可用于判断文件是否结束

	char buf[1021] = { 0 };
	string tmp;
	ifstream fin("writein.txt");

	if (!fin){
		cerr << "Fail!\n";
	}
	else{
        // char *
		while (fin.getline(buf, sizeof(buf)))
		{
			cout << buf << endl;
		}
        // string	
        while (getline(fin, tmp))
		{
			cout << tmp << endl;
		}
		fin.close();
	}

第三种:使用get

	// (不推荐,效率)
	char c;
	ifstream fin("writein.txt");

	if (!fin){
		cerr << "Fail!\n";
	}
	else{
		while (c = fin.get() != EOF)
		{
			cout << c;
		}
		fin.close();
	}

2.5 写入数据

	string name;
	ofstream outfile("writein.txt", ios::app); // 以追加模式开启文件,新数据会被加到文件尾端

	cout << "Writing to the file" << endl;
	cout << "Enter your name: ";
	cin >> name;
	outfile << name << endl;

2.6 fstream类

#include<fstream>
using namespace std;
int main() {
	fstream file;
	file.open("1.txt",ios::out); //以只写模式打开文件
	char buf[] = "test";
	file.write(buf,sizeof(buf));//写入文件
	file.close(); //关闭文件

	file.open("1.txt", ios::app); //以追加模式打开文件
	char buf1[] = "test1";
	file.write(buf1, sizeof(buf));//写入文件末尾
	file.close(); //关闭文件

	file.open("1.txt",ios::in); //以只读方式打开文件
	char buf2[0xFF]; //存储读取的内容
	file.read(buf2,0xFF); //读文件
	file.close(); //关闭文件
}

2.7 关闭文件

当文件读写操作完成之后,我们必须将文件关闭以使文件重新变为可访问的。成员函数close()它负责将缓存中的数据排放出来并关闭文件。这个函数一旦被调用,原先的流对象就可以被用来打开其它的文件了,这个文件也就可以重新被其它的进程所访问了。为防止流对象被销毁时还联系着打开的文件,析构函数将会自动调用关闭函数close

3 数据类型转换

一般默认从文件中读取的是字符格式或者字符串格式的数据,如果是数字要转化为float等格式怎么办呢?

直接定义负责接受的变量数据类型,按行分后再按单词分

  • 从文件中提取“行”fin.getline(line,sizeof(line))
  • 从“行”中提取“单词”:`std::stringstream word(line);

4 状态标志符的验证

  • eof()ifstream 从类 ios 中继承过来的,当到达文件末尾时返回true
  • bad():如果在读写过程中出错,返回 true 。例如:当我们要对一个不是打开为写状态的文件进行写入时,或者我们要写入的设备没有剩余空间的时候。
  • fail():除了与bad() 同样的情况下会返回 true 以外,加上格式错误时也返回true ,例如当想要读入一个整数,而获得了一个字母的时候。
  • good():如果调用以上任何一个函数返回true 的话,此函数返回 false
  • clear():重置以上成员函数所检查的状态标志

下面是一个eof()的使用示例

   #include <iostream.h>  
   #include <fstream.h>  
   #include <stdlib.h>  
     
   int main () {  
       char buffer[256];  
       ifstream in("test.txt");  
       if (! in.is_open())  
       { cout << "Error opening file"; exit (1); }  
       while (!in.eof() )  		// 状态标志符的验证
       {  
           in.getline (buffer,100);  
           cout << buffer << endl;  
       }  
       return 0;  
   }  
   //结果 在屏幕上输出  
    This is a line.  
    This is another line  

5 获得和设置流指针

所有输入/输出流对象都有至少一个流指针:

  • ifstream, 类似istream, 有一个被称为get pointer的指针,指向下一个将被读取的元素。
  • ofstream, 类似 ostream, 有一个指针put pointer,指向写入下一个元素的位置。
  • fstream, 类似 iostream, 同时继承了get put

我们可以通过使用以下成员函数来读出或配置这些指向流中读写位置的流指针:

  • tellg() 和 tellp()

    这两个成员函数不用传入参数,返回pos_type 类型的值(根据ANSI-C++ 标准) ,就是一个整数,代表当前get 流指针的位置 (用tellg) 或 put 流指针的位置(用tellp)

  • seekg() 和seekp()

    这对函数分别用来改变流指针get 和put的位置。两个函数都被重载为两种不同的原型:

    seekg ( pos_type position );
    seekp ( pos_type position );

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

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

    使用这个原型可以指定由参数direction决定的一个具体的指针开始计算的一个位移(offset)。它可以是:

    ios::beg从流开始位置计算的位移
    ios::cur从流指针当前位置开始计算的位移
    ios::end从流末尾处开始计算的位移

流指针 get 和 put 的值对文本文件(text file)和二进制文件(binary file)的计算方法都是不同的,因为文本模式的文件中某些特殊字符可能被修改。由于这个原因,建议对以文本文件模式打开的文件总是使用seekg 和 seekp的第一种原型,而且不要对tellg 或 tellp 的返回值进行修改。对二进制文件,你可以任意使用这些函数,应该不会有任何意外的行为产生。

以下例子使用这些函数来获得一个二进制文件的大小:

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

6 二进制文件读写

以二进制形式读写文件有哪些好处?

举个例子,现在要做一个学籍管理程序,其中一个重要的工作就是记录学生的学号、姓名、年龄等信息。这意味着,我们需要用一个类来表示学生,如下所示:

class CStudent
{
    char szName[20];  //假设学生姓名不超过19个字符,以 '\0' 结尾
    char szId[l0];  //假设学号为9位,以 '\0' 结尾
    int age;  //年龄
};

如果以文本形式存储学生的信息,则最终的文件中存储的学生信息可能是这个样子:

Micheal Jackson 110923412 17
Tom Hanks 110923413 18
......

要知道,这种存储学生信息的方式不但浪费空间,而且后期不利于查找指定学生的信息(查找效率低下),因为每个学生的信息所占用的字节数不同。

这种情况下,以二进制形式将学生信息存储到文件中,是非常不错的选择,因为以此形式存储学生信息,可以直接把 CStudent 对象写入文件中,这意味着每个学生的信息都只占用 sizeof(CStudent) 个字节。

  • 二进制方式对文件进行读写操作时,打开方式要指定为ios::binary
  • 如果存储类,只占用 sizeof(class) 个字节。
  • 以二进制形式读写文件,<< 和 >> 将不再适用,需要使用 C++ 标准库专门提供的 read() 和 write() 成员方法。其中
  • 二进制文件后缀:.bat

6.1 写文件

流对象调用成员函数 write:函数原型:ostream & write(const char * buffer, int len); 字符指针buffer指向要写入文件的二进制数据的起始位置,len 是读写的字节数,返回一个调用该方法的对象的引用

下面的程序演示了如何将学生信息以二进制形式写入文件:

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

struct CStudent
{
	char szName[20];
	int age;
};
int main()
{
	CStudent s;
	ofstream fout("students.dat", ios::out | ios::binary);
	while (cin >> s.szName >> s.age)  // ^z 结束键入
	{
		fout.write((char*)&s, sizeof(s));
	}
	fout.close();
    
    return 0;
}

6.2 读文件

流对象调用成员函数 read:函数原型:istream & read(char * buffer, int len); 字符指针buffer指向读取字节的起始位置,len 是读写的字节数,返回一个调用该方法的对象的引用

下面程序演示了如何使用 read() 方法将二进制文件数据读取出来:

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

struct CStudent
{
	char szName[20];
	int age;
};
int main()
{
	CStudent s;
	ifstream fin("students.dat", ios::in | ios::binary); //二进制读方式打开
	if (!fin)
	{
		cout << "error" << endl;
		return;
	}
	while (fin.read((char*)&s,sizeof(s)))//一直读到文件结束
	{
		cout << s.szName << " " << s.age << endl;
	}
	fin.close();
    
    return 0;
}

7 缓存和同步

当我们对文件流进行操作的时候,它们与一个streambuf 类型的缓存(buffer)联系在一起。这个缓存(buffer)实际是一块内存空间,作为流(stream)和物理文件的媒介。例如,对于一个输出流, 每次成员函数put (写一个单个字符)被调用,这个字符不是直接被写入该输出流所对应的物理文件中的,而是首先被插入到该流的缓存(buffer)中。

当缓存被排放出来(flush)时,它里面的所有数据或者被写入物理媒质中(如果是一个输出流的话),或者简单的被抹掉(如果是一个输入流的话)。这个过程称为同步(synchronization),它会在以下任一情况下发生:

  • 当文件被关闭时: 在文件被关闭之前,所有还没有被完全写出或读取的缓存都将被同步。
  • **当缓存buffer 满时:**缓存Buffers 有一定的空间限制。当缓存满时,它会被自动同步。
  • **控制符明确指明:**当遇到流中某些特定的控制符时,同步会发生。这些控制符包括:flush 和endl。
  • 明确调用函数sync(): 调用成员函数sync() (无参数)可以引发立即同步。这个函数返回一个int 值,等于-1 表示流没有联系的缓存或操作失败。

参考文档:

成员函数详解:C++输入文件流ifstream用法详解
流的一些总结:c++输入输出流
一文读懂缓冲区
c++输入文件流ifstream用法详解
c++输出文件流ofstream用法详解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/274506.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构--查找

目录 1. 查找的基本概念 2. 线性表的查找 3. 树表的查找 3.1 二叉排序树 3.1.1 定义: 3.1.2 存储结构&#xff1a; 3.1.3 二叉排序树的查找 3.1.4 二叉排序树的插入 3.1.5 二叉排序树删除 3.2 平衡二叉树&#xff08;AVL 3.2.1 为什么要有平衡二叉树 3.2.2 定义 3.3 B-树 3.3.1…

如何安装T4显卡的驱动

文章目录 一、没有驱动的报错现象二、cuda版本与驱动的版本对应关系三、安装驱动方法1&#xff1a;方法2&#xff1a; 一、没有驱动的报错现象 ERROR: Unable to find the kernel source tree for the currently running kernel. Please make sure you have installed the ker…

uniapp-android原生插件如何打aar包 (避坑指南二)

1.打开android studio项目&#xff0c;找到module项目&#xff0c;打开右侧gradle&#xff0c;找到对应的module, 点击assemble&#xff0c;会打包生成aar&#xff0c;生成的aar在 [module]/build/outputs/aar/目录下 特殊情况&#xff0c;如果右侧的gradle&#xff0c;找到mo…

生存分析序章2——生存分析之Python篇:lifelines库入门

目录 写在开头1. 介绍 lifelines 库1.1 lifelines库简介1.2 安装与环境配置 2. 数据准备2.1 数据格式与结构2.2 处理缺失数据2.3 对异常值的处理 3. Kaplan-Meier 曲线3.1 使用 lifelines 绘制生存曲线3.2 曲线解读3.3 额外补充 4. Cox 比例风险模型4.1 lifelines 中的 Cox 模型…

RabbitMq知识概述

本文来说下RabbitMq相关的知识与概念 文章目录 概述AMQP协议Exchange 消息如何保证100&#xff05;投递什么是生产端的可靠性投递可靠性投递保障方案 消息幂等性高并发的情况下如何避免消息重复消费confirm 确认消息、Return返回消息如何实现confirm确认消息return消息机制 消费…

Flask 与微信小程序对接

Flask 与微信小程序的对接 在 web/controllers/api中增建py文件&#xff0c;主要是给微信小程序使用的。 web/controllers/init.py # -*- coding: utf-8 -*- from flask import Blueprint route_api Blueprint( api_page,__name__ )route_api.route("/") def ind…

数据的价值:隐藏在数字背后的巨大财富

在当今数字化的时代&#xff0c;数据已经成为了一种宝贵的资源&#xff0c;它的价值被越来越多的人所认识。数据不仅可以帮助企业更好地了解市场和消费者&#xff0c;提高决策的准确性&#xff0c;还可以为社会带来更多的便利和创新。企业、组织和个人可以利用数据来更好地了解…

编程笔记 html5cssjs 004 我的第一个页面

编程笔记 html5&css&js 004 我的第一个页面 一、基本结构二、HTML标签三、HTML元素四、HTML属性五、编写第一个网页六、使用VSCODE小结 开始编写网页&#xff0c;并且使用第一个网页成为一个母板&#xff0c;用于完成后续内容的学习。有一个基本要求&#xff0c;显示结…

linux cat命令增加-f显示文件名功能

在使用cat命令配合grep批量搜索文件内容时&#xff0c;我仅仅能知道是否搜索到&#xff0c;不知道是在哪个文件里找到的。比如cat ./src/*.c | grep full_write,在src目录下的所有.c文件里找full_write,能匹配到所有的full_write&#xff0c;但是不知道它们分别在哪些文件里。于…

Spring Boot+RocketMQ 实现多实例分布式环境下的事件驱动

为什么要使用MQ&#xff1f; 在Spring Boot Event这篇文章中已经通过Guava或者SpringBoot自身的Listener实现了事件驱动&#xff0c;已经做到了对业务的解耦。为什么还要用到MQ来进行业务解耦呢&#xff1f; 首先无论是通过Guava还是Spring Boot自身提供的监听注解来实现的事…

【动画视频生成】

转自&#xff1a;机器之心 动画视频生成这几天火了&#xff0c;这次 NUS、字节的新框架不仅效果自然流畅&#xff0c;还在视频保真度方面比其他方法强了一大截。 最近&#xff0c;阿里研究团队构建了一种名为 Animate Anyone 的方法&#xff0c;只需要一张人物照片&#xff0…

观察者模式概述

观察者模式,它用于建立一种对象与对象之间的依赖关系&#xff0c; 一个对象发生改变将自动通知其他对象&#xff0c; 其他对象将相应做出反应。在观察者模式种&#xff0c;发生改变的对象称为观察目标&#xff0c; 而被通知的对象称为观察者&#xff0c;一个观察目标可以对应多…

【SD】IP-Adapter 进阶 - 画风融合【3】

生成图片大小&#xff1a;1024x576 将图片一的画风转到图片2中。 测试一&#xff1a; control-1:IP-Adapter 导入图片1 仅调整大小&#xff08;拉伸&#xff09; control-2:canny 导入图片2 仅调整大小&#xff08;拉伸&#xff09; best qualit…

FPGA高端项目:SDI 视频+音频编解码,提供工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐我这里已有的 GT 高速接口解决方案我目前已有的SDI编解码方案 3、设计思路和框架设计框图GV8601A均衡EQGTX 时钟要求GTX 调用与控制SMPTE SD/HD/3G-SDISMPTE SD/HD/3G-SDI 接收SMPTE SD/HD/3G-SDI 发送 SDI 视频接收数据处理SDI 音频接收-…

Mysql实时数据同步工具Alibaba Canal 使用

目录 Mysql实时数据同步工具Alibaba Canal 使用Canal是什么&#xff1f;工作原理重要版本更新说明 环境准备安装Canalwindow Java : Canal Client 集成依赖编码 工作流程其他学习canal资料 个人主页: 【⭐️个人主页】 需要您的【&#x1f496; 点赞关注】支持 &#x1f4af; M…

2024美赛数学建模思路A题B题C题D题E题F题思路汇总 选题分析

文章目录 1 赛题思路2 美赛比赛日期和时间3 赛题类型4 美赛常见数模问题5 建模资料 1 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 2 美赛比赛日期和时间 比赛开始时间&#xff1a;北京时间2024年2月2日&#xff08;周五&#xff…

excel 函数技巧

1&#xff1a;模糊查询 LOOKUP(1,0/FIND(F1062,Sheet1!C$2:Sheet1!C$9135),Sheet1!B$2:Sheet1!B$9135) 函数含义&#xff1a;寻找F列1062行和sheet1中的C2行到C9135行进行模糊查询&#xff0c;返回该行对应的B2行到B9135行的结果。未查到返回结果0 函数公式&#xff1a; LO…

leetcode贪心算法题总结(一)

此系列分三章来记录leetcode的有关贪心算法题解&#xff0c;题目我都会给出具体实现代码&#xff0c;如果看不懂的可以后台私信我。 本章目录 1.柠檬水找零2.将数组和减半的最少操作次数3.最大数4.摆动序列5.最长递增子序列6.递增的三元子序列7.最长连续递增序列8.买卖股票的最…

设计模式-过滤器模式

设计模式专栏 模式介绍模式特点应用场景Java中的过滤器介绍代码示例Java实现过滤器模式Python实现过滤器模式 过滤器模式在spring中的应用 模式介绍 过滤器模式是一种设计模式&#xff0c;它允许开发人员使用不同的标准来过滤一组对象。这种模式是通过运算逻辑以解耦的方式将它…

XIAO ESP32S3之物体检测加入视频流

一、前言 由于XIAO ESP32S3开发套件没有显示屏配件&#xff0c;因此加入http视频流功能&#xff0c;可通过浏览器请求ESP32S3上的视频流。 二、思路 1、XIAO ESP32S3启动后通过wifi连接到AP&#xff1b; 2、启动http服务器&#xff0c;注册get_mjpeg处理函数&#xff1b; 3…