流的概念
流(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++中,常见的文件操作模式有以下几种:
输入模式( std::ios::in):用于读取文件内容。
输出模式( std::ios::out):用于写入文件内容。如果文件已存在,则会先清空原有内容再写入新内容。
附加模式( std::ios::app):用于在文件末尾追加内容。如果文件不存在,则会创建新文件
二进制模式( std::ios::binary):以二进制格式进行读写操作,适用于处理非文本数据。
截断模式( std::ios::trunc):在打开文件时,如果文件已经存在,则清空其内容。
在C++中,二进制文件和文本文件是两种不同的文件类型,它们有以下区别:
数据表示方式:文本文件以可读的文本形式保存数据,使用字符编码(如ASCII或UTF-8)将数据转换为可打印字符。而二进制文件以字节序列的形式保存数据,没有经过字符编码转换。
数据存储方式:文本文件存储数据时会进行格式化处理,通常使用待定的分隔符(如空格、逗号或换行符)来区分不同的数据项。二进制文件则以原始的二进制格式直接存储数据。
可读性:由于文本文件采用可读的文本形式保存数据,因此可以直接用文本编辑器查看和修改内容,而二进制文件由于采用原始的二进制格式存储数据,无法直接以可读形式查看和修改。
大小和效率:相对于文本文件,二进制文件通常更紧凑且占用更少的存储空间。同时,在处理大量结构化数据时,读写二进制文件可能比解析和生成文本格式更高效。
选择使用哪种类型取决于你所需处理的数据类型、操作需求和平台兼容性等因素。如果需要人类可读并且易于编辑的内容,则应选择文本文件;如果需要更高效地存储和操作二进制数据,则应选择二进制文件:
#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++中,将文件流的位置指针移动到文件的任意位置可以通过std::fstream、std::ifstream或std::ofstream中的成员函数seekg()和seekp()来实现。这两个函数分别用于读取和写入操作的位置移动。对于读取操作,使用seekg();对于写入操作,使用seekp()。
这些函数接受两个参数:第一个是偏移量,第二个是参考点。参考点可以是以下预定义的枚举值之一:
std::ios_base::beg:文件的开始位置
std::ios_base::cur:文件的当前位置
std::ios_base::end:文件的结束位置
下面是一些示例代码,演示如何使用seekg()和seekp()函数:
#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;}
#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; }
大多数流类(如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()成员函数,它返回下一个将被读取的字符,或者如果文件已经到了末尾,则返回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()函数来进行更精确的判断。