C++文件读写
认识文件
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。
我们要养成良好的习惯,打开文件就要关闭。
读写文件
步骤
- 声明对象
- 使用构造函数,或者open方法打开文件
- 读写文件
- 关闭文件
通过流运算符读写<<写入 >>读入
写入文件
在 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;
}