跳到主要内容

C++ 随机访问

在文件处理中,随机访问是一项非常强大的功能,它允许程序直接跳转到文件中的特定位置进行读取或写入操作,而不必从头到尾顺序处理整个文件。本教程将深入探讨C++中的文件随机访问技术,帮助初学者理解和掌握这一重要概念。

什么是随机访问?

随机访问(Random Access)是指能够直接定位到文件中的任意位置进行读取或写入操作,而不需要按顺序访问前面的所有数据。这与顺序访问形成对比,顺序访问只能从文件开始处按顺序读取数据。

提示

想象一本书:顺序访问就像从第一页开始一页一页地读;而随机访问则像直接翻到某一特定页码开始阅读。

随机访问的基础函数

在C++中,文件随机访问主要通过以下四个函数实现:

  1. seekg() - 用于移动输入流(读取)的文件位置指针
  2. seekp() - 用于移动输出流(写入)的文件位置指针
  3. tellg() - 返回当前输入流位置指针的位置
  4. tellp() - 返回当前输出流位置指针的位置

文件指针位置标志

在使用seekg()seekp()函数时,我们可以指定以下位置标志:

  • ios::beg - 文件开头(默认)
  • ios::cur - 当前位置
  • ios::end - 文件末尾

使用seekg()和tellg()进行读取操作

seekg()函数允许我们将文件读取指针移动到文件中的任意位置。它有两种形式:

  1. seekg(offset) - 从文件开头移动offset个字节
  2. seekg(offset, direction) - 从指定的方向(开头/当前位置/末尾)移动offset个字节

让我们看一个实际的例子:

cpp
#include <iostream>
#include <fstream>
#include <string>

int main() {
std::ifstream file("example.txt");

if (!file) {
std::cerr << "无法打开文件!" << std::endl;
return 1;
}

// 获取文件大小
file.seekg(0, std::ios::end);
std::streampos fileSize = file.tellg();
std::cout << "文件大小: " << fileSize << " 字节" << std::endl;

// 返回到文件开头
file.seekg(0, std::ios::beg);

// 读取第一个字符
char ch;
file.get(ch);
std::cout << "第一个字符: " << ch << std::endl;

// 跳到文件中间
file.seekg(fileSize / 2);
file.get(ch);
std::cout << "文件中间的字符: " << ch << std::endl;

// 读取最后一个字符
file.seekg(-1, std::ios::end);
file.get(ch);
std::cout << "最后一个字符: " << ch << std::endl;

file.close();
return 0;
}

假设example.txt文件内容为:Hello World! This is a test file.

输出将类似于:

文件大小: 34 字节
第一个字符: H
文件中间的字符: i
最后一个字符: .

使用seekp()和tellp()进行写入操作

类似地,seekp()函数用于在文件中定位写入位置指针:

cpp
#include <iostream>
#include <fstream>

int main() {
std::fstream file("random.txt", std::ios::in | std::ios::out | std::ios::trunc);

if (!file) {
std::cerr << "无法打开文件!" << std::endl;
return 1;
}

// 写入一些初始数据
file << "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

// 返回到文件开头
file.seekp(0, std::ios::beg);
std::cout << "当前写入位置: " << file.tellp() << std::endl;

// 修改第5个字符
file.seekp(4);
file << "X";

// 修改第10个字符
file.seekp(9);
file << "Y";

// 修改最后一个字符
file.seekp(-1, std::ios::end);
file << "Z";

// 重置文件指针以便读取
file.seekg(0);
std::string content;
std::getline(file, content);
std::cout << "修改后的内容: " << content << std::endl;

file.close();
return 0;
}

输出将是:

当前写入位置: 0
修改后的内容: ABCDXFGHIYKLMNOPQRSTUVWXYZ

二进制文件的随机访问

随机访问在处理二进制文件时特别有用,特别是当我们需要读取或修改特定记录时:

cpp
#include <iostream>
#include <fstream>
#include <vector>

struct Person {
char name[50];
int age;
double salary;
};

void displayPerson(const Person& p) {
std::cout << "姓名: " << p.name << ", 年龄: " << p.age
<< ", 薪资: " << p.salary << std::endl;
}

int main() {
// 准备数据
std::vector<Person> people = {
{"张三", 25, 8000.0},
{"李四", 30, 9500.0},
{"王五", 22, 7800.0},
{"赵六", 28, 12000.0}
};

// 写入二进制文件
std::fstream file("employees.dat", std::ios::binary | std::ios::out | std::ios::in | std::ios::trunc);

if (!file) {
std::cerr << "无法创建文件!" << std::endl;
return 1;
}

for (const auto& person : people) {
file.write(reinterpret_cast<const char*>(&person), sizeof(Person));
}

// 随机访问记录
Person temp;
// 读取第三条记录(索引从0开始)
file.seekg(2 * sizeof(Person));
file.read(reinterpret_cast<char*>(&temp), sizeof(Person));
std::cout << "第三条记录: ";
displayPerson(temp);

// 修改第二条记录
Person updatedPerson = {"李四", 31, 10000.0}; // 加薪并增加年龄
file.seekp(1 * sizeof(Person));
file.write(reinterpret_cast<const char*>(&updatedPerson), sizeof(Person));

// 检查修改结果
file.seekg(1 * sizeof(Person));
file.read(reinterpret_cast<char*>(&temp), sizeof(Person));
std::cout << "修改后的第二条记录: ";
displayPerson(temp);

file.close();
return 0;
}

输出将类似于:

第三条记录: 姓名: 王五, 年龄: 22, 薪资: 7800
修改后的第二条记录: 姓名: 李四, 年龄: 31, 薪资: 10000

随机访问的实际应用场景

  1. 数据库系统:数据库需要能够快速定位和访问特定记录,而不是每次都从头开始扫描。

  2. 大型文件处理:当处理大型日志文件时,可能只需要分析特定时间段的日志条目。

  3. 多媒体应用:播放器需要能够跳转到音频/视频文件中的特定位置。

  4. 配置文件修改:应用程序可能需要只更新配置文件中的特定条目,而不是重写整个文件。

随机访问注意事项

  1. 二进制模式vs文本模式:在文本模式下,由于换行符的处理不同,精确定位可能会变得复杂。因此,对于需要精确字节定位的操作,推荐使用二进制模式。

  2. 错误检查:随机访问操作后应当检查流的状态,以确保操作成功。

  3. 文件边界:尝试访问超出文件范围的位置将导致错误。

cpp
file.seekg(1000000); // 如果文件小于1000000字节,这将导致错误
if (!file) {
std::cerr << "定位错误!" << std::endl;
}

总结

C++的文件随机访问功能为处理文件提供了极大的灵活性:

  • seekg()seekp()函数允许在文件中自由定位读取和写入位置
  • tellg()tellp()函数返回当前位置信息
  • 随机访问特别适合处理需要选择性读取或更新的文件
  • 二进制文件处理中,随机访问是操作固定大小记录的有力工具

通过掌握文件随机访问,你可以开发出更高效和功能丰富的文件处理应用程序。

练习

  1. 编写一个程序,创建一个包含10个整数的二进制文件,然后随机访问并打印出第3个、第5个和第8个整数。

  2. 创建一个简单的"文本编辑器"程序,允许用户指定行号和新内容来替换文本文件中的特定行。

  3. 实现一个程序,可以在不重写整个文件的情况下,将二进制文件中的特定记录标记为"已删除"(例如,设置一个标志字段)。

警告

在进行文件随机访问操作时,务必确保文件已正确打开,且所有操作后检查流状态以确认操作成功。