c++笔记(10) 文件输入输出
c++ 定义了ifstream, ofstream, fstream类用于文件处理和操作文件,这些类定义在头文件<fstream>中。
c++使用“流”来描述数据流动,数据流向程序,则为input stream(输入流),反之为output stream输出流。
1.文本文件的读写操作。
写入文件
#include <iostream> #include <fstream> using namespace std; int main() { ofstream output; output.open("score.txt"); // open a file output << "zhangjun" << " " << 'S' << " " << 90 << endl; output << "hehe" << " " << 'L' << " " << 88 << endl; output.close(); // close the file cout << "Done" << endl; return 0; }
如果文件已经存在,再次打开的话,文件的内容会被清除。
读取文件:
#include <iostream> #include <fstream> using namespace std; int main() { ifstream input; input.open("score.txt"); string name; char med; int score; input >> name >> med >> score; cout << "name is " << name << " char is " << med << " score is " << score << endl; input >> name >> med >> score; cout << "name is " << name << " char is " << med << " score is " << score << endl; input.close(); return 0; }
关于空格:
读取的数据中是以空格作为分割。如果写入的也有空格,则会导致独处的数据有问题。
2. 检测文件是否存在
fail()函数用于检测文件是否存在!打开文件后可以用fail()函数判断
对上面的代码添加文件检测是否存在:
#include <iostream> #include <fstream> using namespace std; int main() { ifstream input; input.open("score.txt"); // if the file exist if(input.fail()) { cout << "the file not exist"; return 0; } string name; char med; int score; input >> name >> med >> score; cout << "name is " << name << " char is " << med << " score is " << score << endl; input >> name >> med >> score; cout << "name is " << name << " char is " << med << " score is " << score << endl; input.close(); return 0; }
3.检测文件结束
在不知道文件有多少行又想读取读取全部数据的话,需要检测文件的结束位置
(1).eof()函数
ifstream input; input.open("filename"); while(!input.eof()) { // read data from file; input >> number; if(input.eof()) break; // 每读完一次数据,立即检测一次 // other operation ..... }
(2)通过input>>返回的值判断
while(input>>number) { // read data from file; }
input>>number读入数据返回的是一个对象,否则返回的是NULL,据此可以判断文件末尾
注: c++中输入输出流的构造函数参数为c字符串,所以如果文件名问string,要转换c_str()
string filename; cin >> filename; input.open(filename.c_str());
4.函数getline, get, put
流提取运算符读取数据,只能以空格作为分隔符,如果读取的数据中含有空格,则应该怎么读取
getline() : <iostream> , 函数参数getline(ifstream, int/string data, delimitChar)
get() // 只能读写单个字符
put() // 只能读写单个字符
例如读取: New york#New Mexico# India
string city while(!input.eof()) { getline(input, city, '#'); // 注意delimitChar! 是Char! cout << city << endl; }
5.fstream和文件打开模式
fstream创建既能写入又能读出的文件
打开文件的模式:
ios::in 读取模式打开文件
ios::out 写入文件模式
ios::app 追加模式
ios::trunc 如果文件已经存在,丢弃文件内容
ios::binary 二进制输入输出
实例:创建一个文件,写入数据并关闭,再次打开追加数据
#include <iostream> #include <fstream> using namespace std; int main() { fstream inout; // creat a file inout.open("city.txt", ios::out); //打开文件的模式 // write cities inout << "Dalloa" << " " << "Hoston" << " " << "Anlantas" << " "; inout.close(); // open file inout.open("city.txt", ios::out | ios::app); // write a line inout << "Swadas" << " " << "Austin" << " " << "Chicago" ; inout.close(); // read the file inout.open("city.txt", ios::in); string city; while(!inout.eof()) { inout >> city; cout << city << endl; } inout.close(); return 0; }
检测流状态:
c++以提供的检测流状态的函数:
eof()
fail() 文件打开是否成功
bad()
good()
clear()
#include <iostream> #include <fstream> using namespace std; void showState(fstream&); int main() { fstream inout; //creat an output file inout.open("temp.txt", ios::out); inout << "Dallas"; cout << "Normal operation." << endl; inout.close(); showState(inout); inout.open("temp.txt", ios::in); string city; inout >> city; cout << "End of file" << endl; showState(inout); inout.close(); return 0; } void showState(fstream& a) { cout << "stream state: " << endl; cout << "eof(): " << a.eof() << endl; cout << "fail(): " << a.fail() << endl; cout << "bad(): " << a.bad() << endl; cout << "good: " << a.good() << endl; }
6.二进制输入输出
二进制文件输入输出打开方式
文件可以分为文本文件和二进制文件两类
文本文件:能被文本编辑器处理的文件称为文本文件。 在c++中,扩展名为.txt
二进制文件:非文本文件都称为二进制文件。只能由计算机程序读取处理,在c++中扩展名为.dat, 处理二进制文件效率更高
为了读写二进制文件,必须对流对象使用read(), write()函数
1.write()函数
streamObject.write(const char*, int size): char*: 写入的字符数组 size:写入的字节数
#include <iostream> #include <fstream> #include <string> using namespace std; int main() { fstream binaryio; binaryio.open("city.dat", ios::out | ios::binary); // 二进制写入, 二进制文件 .dat string s("Atlant"); binaryio.write(s.c_str(), s.size()); // 转化为c字符串 binaryio.close(); cout << "Done" << endl; return 0; }
非字符数据的写入,使用reinterpret_cast运算符实现将一个指针类型转化为与其不相关的指针类型,它只是进行指针值的二进制复制,并不改变指针指向的数据。
reinterpret_cast<datatype*>(address) 允许将任何指针转换为任何其他指针类型
#include <iostream> #include <fstream> #include <string> using namespace std; int main() { fstream binaryio; binaryio.open("temp.dat", ios::out | ios::binary); // 二进制写入, 二进制文件 .dat int value = 199; binaryio.write(reinterpret_cast<char*>(&value), sizeof(value)); binaryio.close(); cout << "Done" << endl; return 0; }
2 read()函数
streamObject.read(char* address, int size) // size指定可以读取的最大字节数, gcount() 获取实际读取字节数
#include <iostream> #include <fstream> #include <string> using namespace std; int main() { fstream binaryio; binaryio.open("city.dat", ios::in | ios::binary); // 二进制读取, 二进制文件 .dat char s[10]; binaryio.read(s, 10); s[binaryio.gcount()] = '\0'; // gcount()获取实际读取的字节数 // 设置字符串的末尾'\0' cout << s << endl; binaryio.close(); return 0; }
读取整数;
文件temp.dat中存储的整数转变为二进制字节,现在读取这些字节,转化为整数
#include <iostream> #include <fstream> #include <string> using namespace std; int main() { fstream binaryio; binaryio.open("temp.dat", ios::in | ios::binary); // 二进制读取, 二进制文件 .dat int value; binaryio.read(reinterpret_cast<char*>(&value), sizeof(value)); cout << value << endl; binaryio.close(); return 0; }
3. 二进制数组IO
可以使用reinterpret_cast将任意类型的数据转变为二进制字节,也可以将二进制字节转化为任意类型的数据
如:将double数组写入二进制文件,然后再读取出来
#include <iostream> #include <fstream> #include <string> using namespace std; int main() { const int SIZE = 5; fstream binaryio; // 想数组中写入数据 binaryio.open("array.dat", ios::out | ios::binary); double array[SIZE] = {2.3, 4, 3.8, 3.0, 11.11}; binaryio.write(reinterpret_cast<char*>(&array), sizeof(array)); binaryio.close(); // 读取文件中的数据 binaryio.open("array.dat", ios::in | ios::binary); double result[SIZE]; binaryio.read(reinterpret_cast<char*>(&result), sizeof(result)); binaryio.close(); // display array for(int i=0; i<SIZE; i++) { cout << result[i] << " "; } cout << endl; return 0; }
二进制对象IO
如何向二进制文件写入对象,以及从二进制文件读出
定义一个Student类, 将类写入文件和从文件中读出
代码:
student.h
#include <iostream> #include <string> using namespace std; #ifndef STUDENT_H #define STUDENT_H class Student { private: char firstName[25]; char mid; // 中间名 char lastName[25]; int score; public: Student(); // 无参构造函数 Student(const string& firstName, const char mid, const string& lastName, int score); // 有参构造函数 // 定义访问器和更改器 void setFirstName(const string& firstName); void setMid(const char mid); void setLastName(const string& lastName); void setScore(int score); string getFirstName() const; char getMid() const; string getLastName() const; int getScore() const; }; #endif
student.cpp
#include <iostream> #include <string> #include <cstring> #include "E:\back_up\code\c_plus_code\chapter5\external_file\student..h" using namespace std; Student::Student() { // all the data remain default value; } Student::Student(const string& firstName, const char mid, const string& lastName, int score) { setFirstName(firstName); setMid(mid); setLastName(lastName); setScore(score); } // mutator void Student::setFirstName(const string& firstName) { strcpy(this->firstName, firstName.c_str()); // string 类型的值复制到char[] } void Student::setMid(const char mid) { this->mid = mid; } void Student::setLastName(const string& lastName) { strcpy(this->lastName, lastName.c_str()); // strcpy()函数的参数:,char[], string.c_str()才可以 } void Student::setScore(int score) { this->score = score; } string Student::getFirstName() const { return string(firstName); // 将char[]转换为string } char Student::getMid() const { return mid; } string Student::getLastName() const { return string(lastName); // } int Student::getScore() const { return score; } // accessor
main.cpp
#include <iostream> #include <fstream> #include <string> #include "E:\back_up\code\c_plus_code\chapter5\external_file\student..h" using namespace std; void displayStudent(const Student&); int main() { Student student1("Jhoon", 'K', "jerry", 90); Student student2("Mary", 'L', "Charlotte", 100); Student student3("Koeras", 'B', "Long", 78); Student student4("Maksa", 'C', "short", 90); fstream binaryio; binaryio.open("student.dat", ios::out | ios::binary); // 创建文件,写入数据 binaryio.write(reinterpret_cast<char*>(&student1), sizeof(Student)); binaryio.write(reinterpret_cast<char*>(&student2), sizeof(Student)); binaryio.write(reinterpret_cast<char*>(&student3), sizeof(Student)); binaryio.write(reinterpret_cast<char*>(&student4), sizeof(Student)); binaryio.close(); binaryio.open("student.dat", ios::in | ios::binary); // 这种读取数据的方法,文件指针自动下移 Student student; binaryio.read(reinterpret_cast<char*>(&student), sizeof(Student)); displayStudent(student); binaryio.read(reinterpret_cast<char*>(&student), sizeof(Student)); displayStudent(student); return 0; } void displayStudent(const Student& student) { cout << student.getFirstName() << " " << student.getMid() << " " << student.getLastName() << " " << student.getScore() << endl; }
注:
student类中lastName和firstName的数据类型为固定字符长度的数组,char[25], 而不用string, 是因为在reinterpret(char*, size)时。size = sizeof(Student)保证每个学生记录大小相同,保证正确读取学生数据,如果采用string则不能保证。
char[] 与string类型之间的转化:
string(char[]) 类型转化
strcpy(string, char[].c_str()) // 值复制
4. 随机访问文件
使用seekg()函数和seekp()函数移动文件指针到任意位置。
文件由字节序列组成,操作系统中会维护一个文件指针的特殊标记,指向序列中的某个位置,读写操作都是从文件指针指向的位置处进行。
文件的访问分为:
1. 顺序访问文件 文件指针位于文件开始的地方。向后移动
2.随机访问文件 读取文件的数据时,如果想跳过前面的数据项,访问某一项数据,可以采用随机访问文件。‘
c++允许对流对象使用seekp()和seekg()函数,移动文件的指针到任意的位置
seekp() : seek put 用于输出流
seekg() : seek get 用于输入流
seekg(pos): 将指针移动到绝对位置pos
seekg(long a, pos): pos是相对位置, a是偏移量, 注意偏移量的单位是字节 pos: :ios:beg, ios::end, ios::cur
tellp:
tellg: 返回文件指针的当前位置
修改上面的student代码
只对main.cpp作如下修改:
#include <iostream> #include <fstream> #include <string> #include "E:\back_up\code\c_plus_code\chapter5\external_file\student..h" using namespace std; void displayStudent(const Student&); int main() { Student student1("Jhoon", 'K', "jerry", 90); Student student2("Mary", 'L', "Charlotte", 100); Student student3("Koeras", 'B', "Long", 78); Student student4("zhuwen", 'F', "short", 90); Student student5("xiang", 'F', "short", 90); Student student6("gouhao", 'S', "short", 90); Student student7("crappple", 'C', "short", 90); fstream binaryio; binaryio.open("student.dat", ios::out | ios::binary); // 创建文件,写入数据 binaryio.write(reinterpret_cast<char*>(&student1), sizeof(Student)); binaryio.write(reinterpret_cast<char*>(&student2), sizeof(Student)); binaryio.write(reinterpret_cast<char*>(&student3), sizeof(Student)); binaryio.write(reinterpret_cast<char*>(&student4), sizeof(Student)); binaryio.write(reinterpret_cast<char*>(&student5), sizeof(Student)); binaryio.write(reinterpret_cast<char*>(&student6), sizeof(Student)); binaryio.write(reinterpret_cast<char*>(&student7), sizeof(Student)); binaryio.close(); binaryio.open("student.dat", ios::in | ios::binary); // 打开文件,指针这时位于文件起始的位置 cout << "current position is " << binaryio.tellg() << endl; // 当前文件中指针的位置 Student student; // 读取第三个数据,跳过前两个 binaryio.seekg(2*sizeof(Student)); // 将指针向后移动两个数据项 cout << "current position is " << binaryio.tellg() << endl; // 当前文件中指针的位置 binaryio.read(reinterpret_cast<char*>(&student), sizeof(Student)); // 读取的是第三项数据 displayStudent(student); cout << "current position is " << binaryio.tellg() << endl; // 当前文件中指针的位置 // 指针的当前位置 binaryio.seekg(2*sizeof(Student), ios::cur); // 读取第七条数据 // 偏移量单位是字节,所以向后移动两项数据,偏移量应该是n*sizeof(Student) cout << "Current position is " << binaryio.tellg() << endl; binaryio.read(reinterpret_cast<char*>(&student), sizeof(Student)); displayStudent(student); cout << "Current position is " << binaryio.tellg() << endl; return 0; } void displayStudent(const Student& student) { cout << student.getFirstName() << " " << student.getMid() << " " << student.getLastName() << " " << student.getScore() << endl; }
运行结果:
分析:
1. 首先将七个student对象依次写进二进制文件
2. 打开二进制文件,此时文件指针位于文件的开头,指向的是第一个数据, position = 0
3. 将文件指针向后移动两个数据项 binaryio.seekg(2*sizeof(Student)); position = 112
4.读取数据项,读到的是第三个数据项,可以看到读取完毕后文件指针后移到下一个数据项, position = 168
5.再将文件指针从当前位置向后移动两个位置,读取数据项
6. 更新文件:
可以使用组合模式打开文件。如:按照读写模式打开一个二进制文件,对文件进行更新
对上面的程序做出修改:
#include <iostream> #include <fstream> #include <string> #include "E:\back_up\code\c_plus_code\chapter5\external_file\student..h" using namespace std; void displayStudent(const Student&); int main() { fstream binaryio; binaryio.open("student.dat", ios::out | ios::in | ios::binary); // 以读写模式打开二进制文件 Student student_new; binaryio.seekg(sizeof(Student)); // 文件指针后移一个位置 binaryio.read(reinterpret_cast<char*>(&student_new), sizeof(Student)); displayStudent(student_new); student_new.setFirstName("SHI"); student_new.setLastName("gououou"); student_new.setScore(100); binaryio.seekg(sizeof(Student)); // 因为读完数据后文件指针下移,所以需要将文件指针拉回到原处再写入修改后的数据,达到文件更新的目的 binaryio.write(reinterpret_cast<char*>(&student_new), sizeof(student_new)); binaryio.close(); binaryio.open("student.dat", ios::in | ios::binary); // 打开文件,指针这时位于文件起始的位置 Student student; binaryio.seekg(sizeof(Student)); // 读取修改后的数据 定位 //cout << "current position is " << binaryio.tellg() << endl; // 当前文件中指针的位置 binaryio.read(reinterpret_cast<char*>(&student), sizeof(Student)); // 读取的是第三项数据 displayStudent(student); //cout << "current position is " << binaryio.tellg() << endl; // 当前文件中指针的位置 binaryio.close(); return 0; } void displayStudent(const Student& student) { cout << student.getFirstName() << " " << student.getMid() << " " << student.getLastName() << " " << student.getScore() << endl; }
需要注意的是对文件修改时,必须保证指针指向数据的正确性,否则数据没有修改,却把别的数据给覆盖了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)