C++中的文件流操作

528 阅读8分钟

一、C++输入流输出流如何工作

流的概念

流(Stream)是计算机科学中一个重要的概念,用于描述数据的连续传输 ,流可以被视为一个数据通道,数据在这个通道中从源头流向目的地。

流的基本特性

方向性:流通常具有方向,分为输入流(从数据源读取数据)和输出流(向数据目的地写入数据)。

抽象性:流提供了一个高级抽象,隐藏了底层数据传输的具体细节,使得程序员可以更关注于数据处理逻辑,而不是传输机制。

可连接性:多个流可以串联起来,形成数据的管道,数据从源头流经一系列流到达目的地。

流的分类

字符流:处理字符数据,如std::cin、std::cout、std::ifstream、std::ofstream等

字节流:处理原始字节数据,通常用于二进制文件或网络数据传输。

缓冲流:使用缓冲区来优化数据传输,减少磁盘或网络访问次数。

过滤流:在数据传输过程中对数据进行某种转换,如压缩、加密等。

C++中数据的输入和输出包括以下几种情况

对标准输入设备--键盘或标准输出设备--显示器进行输入/输出操作,简称为标准I/O流

对外存(如磁盘)上的文件进行输入/输出操作,简称为文件I/O。

对内存中指定的字符用存储空间进行输入/输出操作,简称为串I/O,

流操作

读写操作:使用>>(提取运算符)和<<(插入运算符)来进行数据的读取和写入。

状态检查:流提供了成员函数如good()、fail()、eof()等,用于检查流的状态

格式控制:流支持格式化输出,如设置宽度、精度、填充字符等。

在C++语言中,有四个已预先定义好的流对象

cin:用于处理标准输入。

cout:用于处理标准输出。

cerr:用于处理标准出错信息,并提供不带缓中区的输出。

clog:也用于处理标准出错信息,但提供带有缓冲区的输出。

二、C++中有哪些常见的文件操作模式

在C++中,常见的文件操作模式有以下几种:

输入模式( std::ios::in):用于读取文件内容。

输出模式( std::ios::out):用于写入文件内容。如果文件已存在,则会先清空原有内容再写入新内容。

附加模式( std::ios::app):用于在文件末尾追加内容。如果文件不存在,则会创建新文件

二进制模式( std::ios::binary):以二进制格式进行读写操作,适用于处理非文本数据。

截断模式( std::ios::trunc):在打开文件时,如果文件已经存在,则清空其内容。

三、 C++中的二进制文件和文本文件之间的区别

在C++中,二进制文件和文本文件是两种不同的文件类型,它们有以下区别:

数据表示方式:文本文件以可读的文本形式保存数据,使用字符编码(如ASCII或UTF-8)将数据转换为可打印字符。而二进制文件以字节序列的形式保存数据,没有经过字符编码转换。

数据存储方式:文本文件存储数据时会进行格式化处理,通常使用待定的分隔符(如空格、逗号或换行符)来区分不同的数据项。二进制文件则以原始的二进制格式直接存储数据。

可读性:由于文本文件采用可读的文本形式保存数据,因此可以直接用文本编辑器查看和修改内容,而二进制文件由于采用原始的二进制格式存储数据,无法直接以可读形式查看和修改。

大小和效率:相对于文本文件,二进制文件通常更紧凑且占用更少的存储空间。同时,在处理大量结构化数据时,读写二进制文件可能比解析和生成文本格式更高效。

选择使用哪种类型取决于你所需处理的数据类型、操作需求和平台兼容性等因素。如果需要人类可读并且易于编辑的内容,则应选择文本文件;如果需要更高效地存储和操作二进制数据,则应选择二进制文件:

四、 C++如何打开一个文件

在C++中打开文件通常使用库中的std::ifstream(用于读取)或std::ofstream(用于写入)。

打开文件进行读取

 #include <iostream>
 #include <fstream>
 #include <string>
 ​
 int main() {
     std::ifstream inputFile("example.txt");
 ​
     if (!inputFile.is_open()) {
         std::cerr << "Error opening file." << std::endl;
         return 1;
     }
 ​
     std::string line;
     while (getline(inputFile, line)) {
         std::cout << line << std::endl;
     }
 ​
     inputFile.close();
     return 0;
 }

打开文件进行写入

 #include <iostream>
 #include <fstream>
 #include <string>
 ​
 int main() {
     std::ofstream outputFile("example.txt");
 ​
     if (!outputFile.is_open()) {
         std::cerr << "Error opening file for writing." << std::endl;
         return 1;
     }
 ​
     outputFile << "Hello, World!" << std::endl;
     outputFile << "This is a test line." << std::endl;
 ​
     outputFile.close();
     return 0;
 }

打开文件进行追加:

如果你想要在文件末尾追加内容,可以使用std::ios:app模式:

std::ofstream outputFile("example.txt", std::ios::app);

打开文件进行读写:

如果需要同时读写文件,可以使用 std::fstream:

 #include <fstream>
 std::fstream ioFile("example.txt", std::ios::in | std::ios::out);
 ​
 if (!ioFile.is_open()) {
     std::cerr << "Error opening file for reading and writing." << std::endl;
     return 1;
 }
 ​
 // Read or write operations...
 ​
 ioFile.close();

五、 C++如何将当前位置移动到文件任意位置

在C++中,将文件流的位置指针移动到文件的任意位置可以通过std::fstream、std::ifstream或std::ofstream中的成员函数seekg()和seekp()来实现。这两个函数分别用于读取和写入操作的位置移动。对于读取操作,使用seekg();对于写入操作,使用seekp()。

这些函数接受两个参数:第一个是偏移量,第二个是参考点。参考点可以是以下预定义的枚举值之一:

  • std::ios_base::beg:文件的开始位置
  • std::ios_base::cur:文件的当前位置
  • std::ios_base::end:文件的结束位置

下面是一些示例代码,演示如何使用seekg()和seekp()函数:

使用seekg()移动读取位置

 #include <fstream>
 #include <iostream>
 ​
 int main() {
     std::ifstream file("example.txt", std::ios::binary);
     
     if (file.is_open()) {
         // 移动读取位置到文件的第10个字节
         file.seekg(10, std::ios::beg);
         
         // 或者从当前位置向前移动5个字节
         file.seekg(-5, std::ios::cur);
         
         // 或者移动到文件末尾前10个字节
         file.seekg(-10, std::ios::end);
         
         // 检查移动是否成功
         if (!file) {
             std::cerr << "Error moving the file position." << std::endl;
             return 1;
         }
         
         // 现在可以从新的位置读取数据
         char buffer[256];
         file.read(buffer, sizeof(buffer));
         
         // 关闭文件
         file.close();
     } else {
         std::cerr << "Unable to open file." << std::endl;
         return 1;
     }
     
     return 0;
 }

使用seekp()移动写入位置

 #include <fstream>
 #include <iostream>
 ​
 int main() {
     std::ofstream file("example.txt", std::ios::binary | std::ios::trunc);
     
     if (file.is_open()) {
         // 移动写入位置到文件的第10个字节
         file.seekp(10, std::ios::beg);
         
         // 写入一些数据
         file.write("Hello, World!", 13);
         
         // 关闭文件
         file.close();
     } else {
         std::cerr << "Unable to open file." << std::endl;
         return 1;
     }
     
     return 0;
 }

当使用seekg()或seekp()时,应该检查操作是否成功,因为如果文件的大小不足以容纳偏移量,或者文件已损坏,这些函数可能会失败 。

 if (file.fail()) {
     std::cerr << "Failed to seek to position 1000 for writing. Error occurred." << std::endl;   
     // 可选:清除流的错误标志
     file.clear();
 } else {
     std::cout << "Seek operation for writing succeeded." << std::endl;
 }

六、 C++如何判断是否已经到达文件末尾

使用eof()成员函数

大多数流类(如std::ifstream, std::ofstream, 和std::fstream)都提供了eof()成员函数,它在流达到文件末尾时返回true。但是需要注意的是,eof()通常在尝试读取操作之后才会返回true,也就是说,只有在读取操作尝试从文件末尾读取数据时,eof()才会变为true。

 std::ifstream file("example.txt");
 ​
 if (file.is_open()) {
     // 尝试读取数据
     char ch;
     while (file >> ch) {
         // 处理数据...
     }
     if (file.eof()) {
         std::cout << "End of file reached." << std::endl;
     }
     file.close();
 }

使用peek()成员函数

另一种方法是使用peek()成员函数,它返回下一个将被读取的字符,或者如果文件已经到了末尾,则返回traits_type::eof()。你可以通过比较返回值与traits_type::eof()来判断是否到达文件末尾。

 std::ifstream file("example.txt");
 ​
 if (file.is_open()) {
     while (file.peek() != std::ifstream::traits_type::eof()) {
         // 进行读取操作...
     }
     file.close();
 }

在C++中,当以二进制模式打开文件时,使用std::ifstream或std::fstream的eof()成员函数来判断是否到达文件末尾并不总是可靠的。这是因为eof()的特性是在读取操作之后返回true,仅当读取操作尝试从文件的末尾读取数据时才如此。然而,对于二进制文件,由于文件中可能包含值恰好等于EOF(通常定义为-1)的字节,这可能会导致误判。

在这种情况下,可以使用peek()函数来进行更精确的判断。