基础文件操作
文件打开模式
ios::in:读取模式ios::out:写入模式(覆盖)ios::app:追加模式ios::binary:二进制模式ios::ate:打开后定位到文件尾
#include <fstream>
using namespace std;
// 创建并写入文件
ofstream outFile("example.txt", ios::out);
if(outFile.is_open()){
outFile << "Line 1\nLine 2";
outFile.close();
}
读写文本文件
逐行读取
ifstream inFile("example.txt");
string line;
while(getline(inFile, line)){
cout << line << endl;
}
inFile.close();
格式化写入
ofstream logFile("log.txt", ios::app);
logFile << fixed << setprecision(2);
logFile << "Transaction: " << 19.99 << endl;
二进制文件操作
结构体读写
struct Person {
char name[50];
int age;
};
// 写入二进制
Person p = {"Alice", 30};
ofstream binOut("data.bin", ios::binary);
binOut.write((char*)&p, sizeof(Person));
// 读取二进制
ifstream binIn("data.bin", ios::binary);
Person p2;
binIn.read((char*)&p2, sizeof(Person));
文件定位
随机访问
fstream file("data.dat", ios::in | ios::out | ios::binary);
file.seekg(10, ios::beg); // 移动读取指针
file.seekp(0, ios::end); // 移动写入指针
streampos pos = file.tellg(); // 获取当前位置
错误处理
状态检查
ifstream file("missing.txt");
if(!file){
cerr << "Error: " << strerror(errno) << endl;
}
// 清除状态标志
file.clear();
file.ignore(numeric_limits<streamsize>::max(), '\n');
高级应用
内存映射文件(Windows API)
HANDLE hFile = CreateFile("large.bin", GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
LPVOID pData = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
// 使用指针访问数据...
UnmapViewOfFile(pData);
CloseHandle(hMap);
CloseHandle(hFile);
性能优化
缓冲区设置
char buffer[1024];
ifstream fastFile("bigfile.txt");
fastFile.rdbuf()->pubsetbuf(buffer, 1024);
跨平台注意事项
- Linux/Windows换行符差异(
\nvs\r\n) - 路径分隔符(
/vs\) - 文件权限管理(
chmodvs ACL)
扩展方向
- 加密文件操作:结合OpenSSL实现AES加密
- 多线程读写:使用
std::mutex保护文件操作 - 网络文件传输:通过SFTP协议实现远程访问
- 日志系统设计:异步写入与滚动文件管理
1. 加密文件操作:结合OpenSSL实现AES加密
文件加密是保护敏感数据的关键。我们使用OpenSSL的AES算法(如AES-256-CBC)对文件内容进行加密和解密。核心原理是:读取文件时解密数据,写入时加密数据。OpenSSL提供EVP接口,简化了加密流程。
实现步骤:
- 初始化OpenSSL加密上下文。
- 使用
fstream读取文件,将数据传入EVP加密函数。 - 将加密后数据写入新文件(或覆盖原文件)。
- 解密过程类似,但使用EVP解密函数。
代码示例:
#include <fstream>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <vector>
// 加密函数:输入源文件路径、输出文件路径和密钥
void encrypt_file(const std::string& input_path, const std::string& output_path, const std::vector<unsigned char>& key) {
std::ifstream in_file(input_path, std::ios::binary);
std::ofstream out_file(output_path, std::ios::binary);
if (!in_file || !out_file) {
throw std::runtime_error("文件打开失败");
}
// 初始化OpenSSL上下文
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
throw std::runtime_error("EVP上下文初始化失败");
}
// 设置AES-256-CBC加密模式
if (EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, key.data(), nullptr) != 1) {
EVP_CIPHER_CTX_free(ctx);
throw std::runtime_error("加密初始化失败");
}
// 缓冲区处理
std::vector<unsigned char> in_buf(1024), out_buf(1024 + EVP_MAX_BLOCK_LENGTH);
int out_len;
while (in_file.read(reinterpret_cast<char*>(in_buf.data()), in_buf.size())) {
int in_len = in_file.gcount();
if (EVP_EncryptUpdate(ctx, out_buf.data(), &out_len, in_buf.data(), in_len) != 1) {
EVP_CIPHER_CTX_free(ctx);
throw std::runtime_error("加密更新失败");
}
out_file.write(reinterpret_cast<char*>(out_buf.data()), out_len);
}
// 处理最后块
if (EVP_EncryptFinal_ex(ctx, out_buf.data(), &out_len) != 1) {
EVP_CIPHER_CTX_free(ctx);
throw std::runtime_error("加密结束失败");
}
out_file.write(reinterpret_cast<char*>(out_buf.data()), out_len);
// 清理资源
EVP_CIPHER_CTX_free(ctx);
}
// 解密函数类似,使用EVP_DecryptInit_ex等
int main() {
std::vector<unsigned char> key(32, 0xAA); // 32字节密钥(AES-256)
encrypt_file("plain.txt", "encrypted.bin", key);
// decrypt_file("encrypted.bin", "decrypted.txt", key); // 解密示例
return 0;
}
注意事项:
- 密钥管理:实际应用中应使用安全密钥存储(如密钥派生函数)。
- 错误处理:OpenSSL错误可通过
ERR_print_errors_fp(stderr)输出。 - 编译命令:
g++ -o encrypt encrypt.cpp -lssl -lcrypto。
2. 多线程读写:使用std::mutex保护文件操作
在多线程环境中,文件操作需避免竞争条件。std::mutex提供互斥锁,确保同一时间只有一个线程访问文件。核心原理是:在文件读写前加锁,操作后解锁。
实现步骤:
- 定义一个文件操作类,封装
fstream和std::mutex。 - 在读写方法中,使用
std::lock_guard自动管理锁生命周期。 - 支持并发读取(允许多线程读)和独占写入(只允许一个线程写)。
代码示例:
#include <fstream>
#include <mutex>
#include <thread>
#include <vector>
class ThreadSafeFile {
public:
ThreadSafeFile(const std::string& path) : file_path(path) {}
// 线程安全写入
void write(const std::string& data) {
std::lock_guard<std::mutex> lock(write_mutex); // 写操作互斥
std::ofstream out_file(file_path, std::ios::app); // 追加模式
if (!out_file) {
throw std::runtime_error("文件打开失败");
}
out_file << data << std::endl;
}
// 线程安全读取(允许多线程并发读)
std::string read() {
std::lock_guard<std::mutex> lock(read_mutex); // 读操作互斥(避免写冲突)
std::ifstream in_file(file_path);
if (!in_file) {
throw std::runtime_error("文件打开失败");
}
std::string content((std::istreambuf_iterator<char>(in_file)), std::istreambuf_iterator<char>());
return content;
}
private:
std::string file_path;
std::mutex write_mutex; // 写锁
std::mutex read_mutex; // 读锁(可优化为读写锁,但C++17需用std::shared_mutex)
};
void worker(ThreadSafeFile& file, int id) {
file.write("Thread " + std::to_string(id) + " writes data");
}
int main() {
ThreadSafeFile safe_file("thread_log.txt");
std::vector<std::thread> threads;
for (int i = 0; i < 5; ++i) {
threads.emplace_back(worker, std::ref(safe_file), i);
}
for (auto& t : threads) {
t.join();
}
std::cout << "File content: " << safe_file.read() << std::endl;
return 0;
}
注意事项:
- 性能优化:对于读多写少场景,可使用
std::shared_mutex(C++17)提升并发度。 - 锁粒度:尽量缩小锁范围以减少阻塞。
- 编译命令:
g++ -o thread_safe thread_safe.cpp -lpthread。
3. 网络文件传输:通过SFTP协议实现远程访问
SFTP(SSH File Transfer Protocol)提供安全的远程文件传输。我们使用libssh库实现SFTP客户端,结合fstream处理本地文件。核心原理是:建立SSH连接后,通过SFTP会话上传或下载文件。
实现步骤:
- 初始化libssh会话,认证(如密码或密钥)。
- 创建SFTP会话,打开远程文件句柄。
- 用
fstream读写本地文件,通过SFTP函数传输数据块。
代码示例:
#include <fstream>
#include <libssh/libssh.h>
#include <libssh/sftp.h>
#include <iostream>
void sftp_upload(const std::string& local_path, const std::string& remote_path,
const std::string& host, const std::string& user, const std::string& password) {
// 初始化SSH会话
ssh_session session = ssh_new();
if (!session) {
throw std::runtime_error("SSH会话创建失败");
}
ssh_options_set(session, SSH_OPTIONS_HOST, host.c_str());
ssh_options_set(session, SSH_OPTIONS_USER, user.c_str());
if (ssh_connect(session) != SSH_OK) {
ssh_free(session);
throw std::runtime_error("SSH连接失败");
}
if (ssh_userauth_password(session, nullptr, password.c_str()) != SSH_AUTH_SUCCESS) {
ssh_disconnect(session);
ssh_free(session);
throw std::runtime_error("认证失败");
}
// 初始化SFTP会话
sftp_session sftp = sftp_new(session);
if (!sftp) {
ssh_disconnect(session);
ssh_free(session);
throw std::runtime_error("SFTP会话创建失败");
}
if (sftp_init(sftp) != SSH_OK) {
sftp_free(sftp);
ssh_disconnect(session);
ssh_free(session);
throw std::runtime_error("SFTP初始化失败");
}
// 打开本地文件
std::ifstream local_file(local_path, std::ios::binary);
if (!local_file) {
sftp_free(sftp);
ssh_disconnect(session);
ssh_free(session);
throw std::runtime_error("本地文件打开失败");
}
// 创建远程文件
sftp_file remote_file = sftp_open(sftp, remote_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
if (!remote_file) {
local_file.close();
sftp_free(sftp);
ssh_disconnect(session);
ssh_free(session);
throw std::runtime_error("远程文件打开失败");
}
// 传输数据
char buffer[1024];
while (local_file.read(buffer, sizeof(buffer))) {
int bytes_read = local_file.gcount();
if (sftp_write(remote_file, buffer, bytes_read) != bytes_read) {
sftp_close(remote_file);
local_file.close();
sftp_free(sftp);
ssh_disconnect(session);
ssh_free(session);
throw std::runtime_error("SFTP写入失败");
}
}
// 清理资源
sftp_close(remote_file);
local_file.close();
sftp_free(sftp);
ssh_disconnect(session);
ssh_free(session);
}
int main() {
sftp_upload("local.txt", "/remote/path/file.txt", "example.com", "user", "password");
// 下载类似,使用sftp_read和ofstream
return 0;
}
注意事项:
- 安全性:避免硬编码密码,使用密钥认证更安全。
- 错误处理:libssh错误可通过
ssh_get_error(session)获取。 - 编译命令:
g++ -o sftp_upload sftp_upload.cpp -lssh。
4. 日志系统设计:异步写入与滚动文件管理
日志系统需高性能和可靠性。我们设计一个异步日志器:主线程收集日志消息,后台线程负责写入文件;同时支持滚动文件管理(如按大小分割文件)。核心原理是:使用生产者-消费者模型,通过队列缓冲日志消息。
实现步骤:
- 定义日志队列(使用
std::queue和std::mutex)。 - 启动后台线程,循环检查队列并写入文件。
- 滚动管理:当文件大小超过阈值时,关闭当前文件,创建新文件(如按时间戳命名)。
代码示例:
#include <fstream>
#include <mutex>
#include <queue>
#include <thread>
#include <chrono>
#include <string>
#include <atomic>
class AsyncLogger {
public:
AsyncLogger(const std::string& base_path, size_t max_size = 1024 * 1024)
: base_path(base_path), max_file_size(max_size), running(true), current_size(0) {
worker_thread = std::thread(&AsyncLogger::process_queue, this);
open_new_file(); // 初始文件
}
~AsyncLogger() {
running = false;
worker_thread.join(); // 等待线程结束
if (out_file.is_open()) out_file.close();
}
void log(const std::string& message) {
std::lock_guard<std::mutex> lock(queue_mutex);
log_queue.push(message);
}
private:
std::string base_path;
size_t max_file_size;
std::ofstream out_file;
std::atomic<size_t> current_size;
std::queue<std::string> log_queue;
std::mutex queue_mutex, file_mutex;
std::thread worker_thread;
std::atomic<bool> running;
void open_new_file() {
std::lock_guard<std::mutex> file_lock(file_mutex);
if (out_file.is_open()) out_file.close();
auto now = std::chrono::system_clock::now();
auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
std::string new_path = base_path + "_" + std::to_string(timestamp) + ".log";
out_file.open(new_path, std::ios::app);
if (!out_file) throw std::runtime_error("日志文件打开失败");
current_size = 0;
}
void process_queue() {
while (running || !log_queue.empty()) {
std::queue<std::string> local_queue;
{
std::lock_guard<std::mutex> lock(queue_mutex);
if (!log_queue.empty()) {
local_queue.swap(log_queue); // 批量处理减少锁竞争
}
}
while (!local_queue.empty()) {
std::string msg = local_queue.front();
local_queue.pop();
write_to_file(msg);
}
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 减少CPU占用
}
}
void write_to_file(const std::string& msg) {
std::lock_guard<std::mutex> file_lock(file_mutex);
if (!out_file) return;
out_file << msg << std::endl;
current_size += msg.size() + 1; // 估算大小
if (current_size >= max_file_size) {
open_new_file(); // 滚动新文件
}
}
};
int main() {
AsyncLogger logger("app_log");
logger.log("系统启动");
// 多线程调用logger.log安全
std::this_thread::sleep_for(std::chrono::seconds(1));
return 0;
}
注意事项:
- 异步性能:队列大小可限制,防止内存溢出。
- 滚动策略:可扩展为按时间滚动(如每天新文件)。
- 编译命令:
g++ -o logger logger.cpp -lpthread。
总结
C++fstrem文件操作非常重要,要使文件信息保存恰当,安全性高,性能强等等,希望这篇文章能帮助到你。
108

被折叠的 条评论
为什么被折叠?



