C++文件读写

c++ fstream类

认识文件

c ++语言把文件看一做个个字符(字节)的排序,即文件就是由一个个字符(字节)按顺序组成,根据这一点,我们把把文件分为acsII文件(文本文件)和二进制文件。
文本文件每一个字节存放着一个ascII码,代表一个字符。二进制文件就是把内存中数据按照在内存的样式一样地保存。这个说它麻烦了,就比如文本文件里存着数字 ‘1’ 那在文件里存着就是对于的ascII码‘0011 0001’。但是存着二进制文件存着的就是 ‘0000 0001’ 二进制中的1。这里可以简单可以看出,二进制文件在存一致的数据时,所占内存就小多了。

文件流类

用途
ifstream从已有的文件中读。
ofstream从已有的文件中写。
fstream打开文件供读写

打开文件

创建类对象,调用open成员函数

函数原型

void open(const char* filename,int mode,int prot=filenuf::openport)
参数用途
filename文件名字
mode打开文件的方式
prot打开文件的属性

mode 的选项项

选项参数
ios::in为输入(读)而打开文件
ios::out为输出(写)而打开文件
ios::ate初始位置:文件尾(文件指针指向文件未)
ios::app所有输出附加在文件末尾
ios::trunc如果文件已存在则先删除该文件
ios::binary二进制方式
ios::nocreate文件不存在就打开失败
ios::noreplace文件存在就打开失败

这些方式是能够进行组合使用的,按位或运算(“|”)的方式,这里按位或运算是有原因的,大家可以查一下他们的定义。不设置为binary,那么就是二进制文件。

ofstream out;  
out.open("Hello.txt", ios::in|ios::out|ios::binary)  //根据自己需要进行适当的选取  

当然prot也有常量值

选项参数
0普通文件
1只读文件
2隐藏文件
4系统文件

文件的属性也可以使用“或”运算和“+”进行组合使用
ps:这些参数都是可以缺省的,都是有默认参数滴。
对于ifstream打开文件,mode的默认值为in,对文本文件读写对于ofsream来说mode的默认值为out,port一般都是默认值 0;

用构造函数打开文件

我们也可以通过在创建对象的时候,我们就打开文件;
构造函数体

//以ofstream为例子
ofstream::ofstream(char* filename,int mode,int port);

和open 差不多,就不赘述了。

文件打开异常

这个三个类重载了 !操作符!返回的是非0那么就是打开失败。
还有其他等价的方法,如下

if(!a)//非零打开失败
cout<<'打开失败\n';非0打开失败
if(a.fail())//返回1打开失败
cout<<'打开失败\n';
if(!a.good())//good返回1 打开成功
cout<<'打开失败\n';

关闭文件 成员函数close;

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

读写文件

步骤

  1. 声明对象
  2. 使用构造函数,或者open方法打开文件
  3. 读写文件
  4. 关闭文件

通过流运算符读写<<写入 >>读入

写入文件

在 C++ 编程中,我们使用流插入运算符( << )向文件写入信息,就像使用该运算符输出信息到屏幕上一样。唯一不同的是,在这里使用的是 ofstream 或 fstream 对象,而不是 cout 对象。

读取文件

在 C++ 编程中,我们使用流提取运算符( >> )从文件读取信息,就像使用该运算符从键盘输入信息一样。唯一不同的是,在这里使用的是 ifstream 或 fstream 对象,而不是 cin 对象。

#include <fstream>
#include<iostream>
using namespace std;
int main () {
     ofstream out("test.txt");//打开文件
     if (out.is_open())
    {
         out << "This is a line.\n";//<< 写入文件
         out.close();//关闭文件
     }
	ifstream in("test.txt");//打开文件
	char buffer[256]; 
       if (! in.is_open())
       { cout << "Error opening file"; exit (1); }
       while(!in.eof())//没有读入到文件结尾
       {
        in>>buffer;
      	cout<< buffer << endl;
       }
       
     return 0;
 }
//结果: 在out.txt中写入:
This is a line.
在屏幕上输出语句 
This
is
a
line.
line.//

这里注意到 >> 读入文件是遇到空格会暂停读取的
当然我们这里<< 写入文件可以是c++提供的类型,也可以以是我们自定义的结构体和类哇,写入顺序就是我们定义的顺序。

成员函数叫做eof ,它是ifstream 从类 ios 中继承过来的,当到达文件末尾时返回true 。

除了eof()以外,还有一些验证流的状态的成员函数(所有都返回bool型返回值):

  • bad()
    如果在读写过程中出错,返回 true 。例如:当我们要对一个不是打开为写状态的文件进行写入时,或者我们要写入的设备没有剩余空间的时候。

  • fail()
    除了与bad() 同样的情况下会返回 true 以外,加上格式错误时也返回true ,例如当想要读入一个整数,而获得了一个字母的时候。

  • eof()
    如果读文件到达文件末尾,返回true。

  • good()
    这是最通用的:如果调用以上任何一个函数返回true 的话,此函数返回 false

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

通过流成员函数读写

函数用途
put(char )文件名字
write(const char* pch,int count)向文件写入count个字符
get(char )获取一个字符
read(const char* pch,int count)文件读取count个字符
getline(const char* pch,int count,char delim='\0')向文件读取conut个字符,结束标志是delim

getline 后面的delim是缺省的,默认的是读取一行。
write 和read getline 参数类型都需要强制转换成char* 类型。

二进制文件和文本文件大同小异

二进制文件和文本文件的区别都在上面说过了,我们经常用read和write对文本文件进行操作,这里cout 也不是字符数,而是字节数啦。例子如下

// reading binary file
    #include <iostream>
    #include <fstream>
using namespace std;
    const char * filename = "test.txt";

    int main () {
        char * buffer;
        long size;
        ifstream in (filename, ios::in|ios::binary|ios::ate);
        size = in.tellg();
        in.seekg (0, ios::beg);
        buffer = new char [size];
        in.read (buffer, size);
        in.close();

        cout << "the complete file is in a buffer";

        delete[] buffer;
        return 0;
    }
    //运行结果:
    //The complete file is in a buffer

文件对随机读写

这里我们就需要文件指针,一句话文件指针就是指向文件内数据的指针。
我们读写文件就有两个指针,读指针和写指针。每当我们操作一次读或者写操作,读指针或者写指针就会移动一次。例如用一次getline();就会移动一次。这就是所谓的按顺序读写。当然我的读写文件不一定要按顺序,所以我们要操作读(写)指针

  • seekg(); 操作读指针
  • seekp();操作写指针
  • tellg();返回读指针所在的位置
  • tellp();返回写指针所在地位置
函数原型

ostream& seekp( streampos pos );

ostream& seekp( streamoff off, ios::seek_dir dir );

istream& seekg( streampos pos );

istream& seekg( streamoff off, ios::seek_dir dir );
streampos tellp();//streampos 其实就是个长整型
streampos tellg();

参数

pos:新的文件流指针位置值,长整型

off:需要偏移的值,长整型

dir:我们移动的时候的参照值

dir被定义成一个枚举值如下

enum seek_dir {beg, cur, end};

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

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

using namespace std;


int main()
{
    ifstream fin("test.txt");
    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;
    cin.get();

    return 0;
}

如果test存着abcdefg,那么输出 c g 7 这里要注意的是我们seek是从0开始计数的。
运行结果

学生管理 文件读写

这个恰好是作业,emm随手写的,不是很完美,明天把c++文件读写的知识再复习一遍。现在下面的代码应该没有bug了,给老师看的时候结果有bug.qwq。

这里我们要强调一点

我们自定的类也是数据类型,也是可以读写的啦,当然我们在使用read&write的时候不要忘了强制转换成<char*>类型哇。如下:

filein.read((char*)&stu[i], sizeof(stu[i]));

Student.h

#pragma once
#include<iostream>
#include<fstream>
#include<iomanip>
#include<string> 
using namespace std;
class Student
{
public:
	void inputId();//输入学号 
	void inputName();// 输入姓名 
	void inputmath();//输入数学成绩 
	void inputeng();//输入英语成绩
	int getId();// 获得学号
	friend ostream& operator<<(ostream&, Student&);
private:
	int id;//学号
	char name[20]; //姓名
	float math;//数学
	float eng;//英语
};

Student.cpp

#include "Student.h"

ostream & operator<<(ostream & out, Student & stu)
{
	out << setw(10) << stu.id << setw(10) << stu.name << setiosflags(ios::fixed) << setprecision(1) << setw(10) << stu.math << setw(10) << stu.eng << endl;
	return out;
}
void Student::inputId()
{
	cout << "请输入学号(1000-2000之间):";
	cin >> id;
	while (id > 2000 || id < 1000)
	{
		cout << "输入学号有误,请重新输入学号(1000-2000之间): " << endl;
		cin >> id;
	}

}
void Student::inputName()
{
	cin.ignore(80, '\n');
	cout << "请输入姓名:";
	cin.getline(name, 50);
}
void Student::inputmath()
{
	cout << "请输入数学成绩100之间):";
	cin >> math;
	while (math < 0 || math>100)
	{
		cout << "输入错误,成绩应该在0到100之间!" << endl;
		cout << "请输数学成绩(0-100之间):";
		cin >> math;
	}
}
void Student::inputeng()
{
	cout << "请输入英语成绩(0-100之间):";
	cin >> eng;
	while (eng < 0 || eng>100)
	{
		cout << "输入错误,成绩应该在0到100之间!" << endl;
		cout << "请输入英语成绩(0-100之间):";
		cin >> eng;
	}
}
int Student::getId()
{
	return id;
}

main.cpp


#include"Student.h"
#include <windows.h>
using namespace std;

const int MAXNUM = 200;


//主菜单 
void Menu()
{
	cout << "          =================================================\n";
	cout << "          =                   主菜单                      =\n";
	cout << "          =           1=录入学生     2=显示信息           =\n";
	cout << "          =           3=保存为文本   0=保存并退出         =\n";
	cout << "          =================================================\n";
	cout << "请选择:";
}
//录入学生信息 
void Input(Student stu[], int& num)//引用 同时改变外面的sN
{
	int i = num; //计数君
	string str;//用来判断是否继续输入的字符串
	while (true)
	{
		if (num > MAXNUM)
		{
			cout << "超出系统上线\n";
			break;
		}

		stu[i].inputId();//查重
		for (int j = 0; j < num-1; j++)
		{
			while (stu[i].getId() == stu[j].getId())
			{
				cout << "学号不允许重复,该学号已经存在!\n";
				stu[i].inputId();
			}
		}
		stu[i].inputName();
		stu[i].inputmath();
		stu[i].inputeng();
		i++;
		num++;//系统录入学生总人数+1
		cout << "继续输入学生数据请按y或Y:";
		cin >> str;
		if (str == "y" || str == "Y")
		{
			continue;
		}
		else
		{
			break;
		}
	}
}
int main()
{
	int sN = 0;//记录系统一共学生的人数
	Student stu[MAXNUM];//分配空间
	/*从二进制文件读数据 ,二进制文件第一个数据为录入学生人数*/
	ifstream filein("students.dat", ios::binary);
	if (filein)//大成功非0
	{
		cout << "系统文件读取成功\n";
		filein.read((char*)&sN, sizeof(sN));
		cout << sN;
		for (int i = 0; i < sN; i++)
		{
			filein.read((char*)&stu[i], sizeof(stu[i]));
		}
	}
	filein.close();
	int a;//用来判断菜单选择
	Menu();
	while (cin >> a)
	{
		switch (a)
		{
		case 1:
		{
			Input(stu, sN);
			cout << "请输入编号\n";
			break;
		}
		case 2:
		{
			if (sN == 0)
				cout << "本系统未录入学生\n";
			cout << setw(10) << "学号" << setw(10) << "姓名" << setw(10) << "数学" << setw(10) << "英语" << endl;
			for (int i = 0; i < sN; i++)
			{
				cout << stu[i];
			}
			cout << "请输入编号\n";
			break;
		}
		case 3:
		{
			ofstream fileout("students.txt", ios::trunc);
			if (!fileout)
			{
				cout << "文件不能被打开!" << endl;
			}
			else
			{
				fileout.write((char*)&sN, sizeof(sN));
				fileout << "学生人数" << sN << endl;
				fileout << setw(10) << "学号" << setw(10) << "姓名" << setw(10) << "数学" << setw(10) << "英语" << endl;
				for (int i = 0; i < sN; i++)
				{
					fileout << stu[i];
				}
				fileout.close();
				cout << "保存成功!\n" << endl;
			}
			cout << "请输入编号\n";
			break;
		}
		case 0:
		{
			ofstream fileout("students.dat", ios::binary | ios::trunc);
			if (!fileout)
			{
				cout << "文件不能被打开!" << endl;
			}
			else
			{
				fileout.write((char*)&sN, sizeof(sN));
				for (int i = 0; i < sN; i++)
				{
					fileout.write((char*)&stu[i], sizeof(stu[i]));
				}
				fileout.close();
				cout << "退出成功!\n" << endl;
				return 0;
			}
			break;
		}
		default:
			cout << "输入非法,请重新输入!" << endl;
			Menu();
			break;
		}
	}
	return 0;
}

努力成长的程序员