C++ - 文件读写
5 文件操作
C++文件操作介绍
文件是指存储在计算机文件系统中的数据集合。文件可以包含各种类型的信息,例如文本、图像、音频视频等。在 C++中,文件是一种数据流,可以用于读取或写入数据。C++提供了一系列的文件操作函数,用于实现对文件的读取、写入、打开、关闭等操作。
在 C++中,文件可以分为文本文件和二进制文件。这两种文件的区别在于它们存储数据的方式:
文本文件:文本文件以字符形式存储数据,例如 ASCII 或 Unicode 字符。每个字符占用一个字节的空间。文本文件通常用于存储文本信息,如代码、配置文件、文档等。
二进制文件:二进制文件以字节形式存储数据,每个数据项可以是任意长度。二进制文件通常用于存储非文本信息,如图像、音频、视频等。
文本文件是包含可读字符的文件,例如.txt和.csv等文件通常是文本文件。图像、音频和视频等文件是二进制文件,因为它们包含嵌入在其中的二进制数据。图像文件通常包括.bmp、.jpg、.png等格式,音频文件通常包括.wav、.mp3等格式,视频文件通常包括.mp4、.mov等格式。
文件的路径可以使用字符串表示。文件路径可以是相对路径或绝对路径。
相对路径是相对于当前工作目录的路径。当前工作目录是执行程序时操作系统指定的默认路径。例如,如果当前工作目录是 D:\Test\John,而文件位于该目录下的子目录 Files 中,可以使用相对路径来表示文件路径:"Files/myfile.txt"。
绝对路径是文件的完整路径,包括驱动器名(在Windows系统中)或根目录符号(在类Unix系统中)。绝对路径从文件系统的根目录开始。例如,在Windows系统中,文件 myfile.txt 位于 D:\Test\John\Files,可以使用绝对路径来表示文件路径:" D:\Test\John\Files/myfile.txt"。
文件类型分为两种:
-
文本文件 - 文件以文本的ASCII码形式存储在计算机中
-
二进制文件 - 文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们
在C++中,文件的读写主要通过<fstream>
库中的ifstream
类来进行文件的读操作,通过ofstream
类来进行文件的写操作。
操作文件的三大类:
-
ofstream:写操作
-
ifstream: 读操作
-
fstream : 读写操作
文件打开的方式:
打开方式 | 解释 |
---|---|
ios::in | 为读文件而打开文件 |
ios::out | 为写文件而打开文件 |
ios::ate | 初始位置:文件尾 |
ios::app | 追加方式写文件 |
ios::trunc | 如果文件存在先删除,再创建 |
ios::binary | 二进制方式 |
注意: 文件打开方式可以配合使用,利用 | 操作符
例如:以追加的方式写文件 ofs.open("test.txt", ios::app | ios:: out)
存储单位转换:
5.1文本文件
5.1.1写文件
写文件步骤如下:
-
包含头文件
#include <fstream>
-
创建流对象
ofstream ofs;
-
打开文件
ofs.open("文件路径",打开方式);
-
写数据
ofs << "写入的数据";
-
关闭文件
ofs.close();
示例:
#include <fstream>
void test01()
{
ofstream ofs;
ofs.open("test.txt", ios::out);
if (!ofs.is_open())
{
cout << "文件打开失败" << endl;
return;
}
ofs << "姓名:张三" << endl;
ofs << "性别:男" << endl;
ofs << "年龄:18" << endl;
ofs.close();
}
int main() {
test01();
system("pause");
return 0;
}
总结:
-
文件操作必须包含头文件 fstream
-
读文件可以利用 ofstream ,或者fstream类
-
打开文件时候需要指定操作文件的路径,以及打开方式
-
利用<<可以向文件中写数据
-
操作完毕,要关闭文件
5.1.2读文件
读文件与写文件步骤相似,但是读取方式相对于比较多
读文件步骤如下:
-
包含头文件
#include <fstream>
-
创建流对象
ifstream ifs;
-
打开文件并判断文件是否打开成功
ifs.open("文件路径",打开方式);
-
读数据
四种方式读取
-
关闭文件
ifs.close();
示例:
#include <fstream>
#include <string>
void test01()
{
ifstream ifs;
ifs.open("test.txt", ios::in);
if (!ifs.is_open())
{
cout << "文件打开失败" << endl;
return;
}
//第一种方式:采用“>>”运算符
//char buf[1024] = { 0 };
//while (ifs >> buf)
//{
// cout << buf << endl;
//}
//第二种方式:利用ifs流对象内部的getline方法,这个名字虽然和第三种中的一样,但是传入的参数不同,并不是同一个函数
//char buf[1024] = { 0 };
//while (ifs.getline(buf,sizeof(buf)))
//{
// cout << buf << endl;
//}
//第三种方式:利用<string>中的getline函数,按行读取,这样每一行内的所有空格也可以顺利读取
//string buf;
//while (getline(ifs, buf))
//{
// cout << buf << endl;
//}
//第四种方式:利用ifs内部的get()函数一个个的将字符读出来,遇到EOF时返回False退出while循环,EOF 是 end of file的标志,不太建议使用这个,因为这个很慢,只能一个一个字符读取
char c;
while ((c = ifs.get()) != EOF)
{
cout << c;
}
ifs.close();
}
int main() {
test01();
system("pause");
return 0;
}
总结:
-
读文件可以利用 ifstream ,或者fstream类
-
利用is_open函数可以判断文件是否打开成功
-
close 关闭文件
5.2 二进制文件
以二进制的方式对文件进行读写操作
打开方式要指定为 ==ios::binary==
5.2.1 写文件
值得一提的是,要实现以二进制形式读写文件,<< 和 >> 将不再适用,需要使用 C++ 标准库专门提供的 read() 和 write() 成员方法。
二进制方式写文件主要利用流对象调用成员函数write
函数原型 :ostream& write(const char * buffer,int len);
参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数
示例:
#include <fstream>
#include <string>
class Person
{
public:
char m_Name[64];
int m_Age;
};
//二进制文件 写文件
void test01()
{
//1、包含头文件
//2、创建输出流对象
ofstream ofs("person.txt", ios::out | ios::binary);
//3、打开文件
//ofs.open("person.txt", ios::out | ios::binary);
Person p = {"张三" , 18};
//4、写文件
ofs.write((const char *)&p, sizeof(p));
//5、关闭文件
ofs.close();
}
int main() {
test01();
system("pause");
return 0;
}
总结:
-
文件输出流对象 可以通过write函数,以二进制方式写数据
5.2.2 读文件
二进制方式读文件主要利用流对象调用成员函数read
函数原型:istream& read(char *buffer,int len);
参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数
示例:
#include <fstream>
#include <string>
class Person
{
public:
char m_Name[64];
int m_Age;
};
void test01()
{
ifstream ifs("person.txt", ios::in | ios::binary);
if (!ifs.is_open())
{
cout << "文件打开失败" << endl;
}
Person p;
ifs.read((char *)&p, sizeof(p));
cout << "姓名: " << p.m_Name << " 年龄: " << p.m_Age << endl;
}
int main() {
test01();
system("pause");
return 0;
}
-
文件输入流对象 可以通过read函数,以二进制方式读数据
5.3 获取文件大小
方式一:推荐使用此方式获得文件大小
C/C++、Window、Linux 环境都可以使用!
通过结构体struct stat
获得。
需要包含头文件:#include <sys/stat.h> // struct stat
#include <iostream>
using namespace std;
#include <sys/stat.h> // struct stat
//通过stat结构体获得文件大小,单位字节
size_t getFileSize1(const char* fileName)
{
if (fileName == NULL)
{
return 0;//这是一个存储文件(夹)信息的结构体,其中有文件大小和创建时间、访问时间、修改时间等
}
struct stat statbuf;//提供文件名字符串,获得文件属性结构体
stat(fileName, &statbuf);// 获取文件大小
size_t filesize = statbuf.st_size;
return filesize;
}
int main()
{
size_t filesize = getFileSize1("test.txt");
cout << "文件大小:" << filesize << endl;
return 0;
}
首先也是通过stat函数获得文件的基本属性,然后再从结构体中直接进行获取文件大小就行,很简单,也很好用。
方式二:C++ 方式获得文件大小,使用std::ifstream
和std::ios::binary
打开文件,然后调用tellg()
和seekg()
函数获取文件大小。
不推荐使用此方式
当文件太大时,移动文件光标指针会非常耗时!
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <fstream>
int getFileSize(const std::string& filename)
{
std::ifstream file(filename, std::ios::binary | std::ios::ate);//ate 初始位置:文件尾
if (!file)
{
std::cerr << "Failed to open file: " << filename << std::endl;
return -1; // or throw an exception
}
std::streampos size = file.tellg();
return static_cast<int>(size);
}
int main()
{
size_t filesize = getFileSize("test.txt");
cout << "文件大小:" << filesize << endl;
return 0;
}
或者:
#include <iostream>
using namespace std;
#include <fstream>
#include <string>
std::streampos getFileSize(std::string fileName)
{
//打开文件
std::ifstream file;
file.open(fileName, std::ios::in);
if (file.is_open() == false)
{
// 文件打开失败
std::cout << "Open file " << fileName << " fail!" << std::endl;
return -1;
}
//文件指针移动到末尾位置
file.seekg(0, std::ios_base::end);
//获取当前文件指针位置,也就是获得文件大小
std::streampos fileSize = file.tellg();
//文件指针移动到开头文件
file.seekg(0, std::ios_base::beg);
//关闭文件
file.close();
return fileSize;
}
int main()
{
size_t filesize = getFileSize("test.txt");
cout << "文件大小:" << filesize << endl;
return 0;
}
5.4 判断文件读取结束
在C++中,可以使用以下方法来判断文件读取是否结束:
1、使用eof()
函数:eof()函数检查文件流对象是否已经达到文件末尾。它返回一个bool值,如果已经到达文件末尾,则返回true,否则返回false。可以在循环中使用该函数来判断文件是否读取结束。
例如:
ifstream file("example.txt");
string line;
while (!file.eof())
{
getline(file, line);
// 处理读取的行数据
}
2、使用good()
函数:good()函数用于检查文件流对象的状态是否正常。如果流对象正常且未发生任何错误,则该函数返回true,否则返回false。因此,可以在循环中使用该函数来判断文件是否读取结束。
例如:
ifstream file("example.txt");
string line;
while (file.good())
{
getline(file, line);
// 处理读取的行数据
}
3、使用peek()
函数:peek()函数用于查看下一个字符,但并不从输入流中提取该字符。如果文件已经到达末尾,peek()函数将返回EOF(或者-1)。可以在循环中使用该函数来判断文件是否读取结束。
例如:
ifstream file("example.txt");
string line;
while (file.peek() != EOF)
{
getline(file, line);
// 处理读取的行数据
}