static stringstream 的问题 static std::stringstream 左操作,只第一次能成功!

本文探讨了在Visual Studio 2017环境下使用静态stringstream时遇到的问题,即只在第一次操作时成功,之后无法正常工作。文章通过示例代码展示了这一现象,并可能涉及对stringstream对象的初始化及重复使用的讨论。
//vs 2017, windows

void MyFun( int a, int b)
{

    static std::string strLog;
    static std::stringstream ss;
	
    // ss的操作,只第一次能成功!
    ss  << "a:"<< a<< ";b:" << b; 
	
    ss >> strLog;
	
    ss.str("");
	
    strLog.clear();
}

 static std::stringstream ss;
 ss的 << 操作,只第一次能成功!

ss  << "a:"<< a<< ";b:" << b; 

<think> 我们讨论的是如何将std::stringstream中的数据以二进制形式读写到文件。 用户已经知道如何使用std::stringstream进行二进制数据的读写(如引用[1]所示),现在想知道如何将std::stringstream中的数据整个写入二进制文件,以及如何从二进制文件中读取数据到std::stringstream。 步骤: 1. 将std::stringstream中的数据写入二进制文件: 我们可以获取stringstream内部的字符串表示(通过str()方法),然后将其写入文件。 注意:stringstream可以同时用于文本和二进制数据。对于二进制数据,我们直接将其底层字符串写入文件即可。 2. 从二进制文件读取数据到std::stringstream: 首先将整个文件读入一个字符串(或缓冲区),然后将这个字符串设置到stringstream中。 但是要注意:如果二进制数据中包含空字符('\0'),那么使用str()方法获取的字符串会被截断(因为C风格字符串以空字符结束)。然而,std::string实际上可以包含空字符,所以我们可以通过以下方式避免问题: - 使用std::string的data()和size()方法,这样不会丢失空字符之后的数据。 因此,在写入文件时,我们使用std::stringstream::str()获取整个字符串(包括空字符),然后使用write方法写入文件(需要指定长度)。 读取时,我们将整个文件内容读入一个std::string(或直接读入stringstream的缓冲区),然后将其放入stringstream。 具体方法: 一、将std::stringstream中的二进制数据写入文件 步骤: 1. 打开文件(二进制模式) 2. 获取stringstream中的字符串(使用str()方法,返回std::string) 3. 将这个字符串的数据(使用data()或c_str())和长度(size())写入文件 二、从二进制文件读取到std::stringstream 步骤: 1. 打开文件(二进制模式) 2. 获取文件大小(如使用seekg和tellg) 3. 分配缓冲区(或使用std::string)并读取整个文件 4. 将读取到的数据设置到stringstream(使用str()方法) 示例代码: 写文件: std::ofstream out_file("file.bin", std::ios::binary); std::string data = stringstream_obj.str(); // 获取整个数据(包括空字符) out_file.write(data.data(), data.size()); 读文件: std::ifstream in_file("file.bin", std::ios::binary | std::ios::ate); if (!in_file) { /* 错误处理 */ } std::streamsize size = in_file.tellg(); in_file.seekg(0, std::ios::beg); // 创建缓冲区(或字符串)来存储数据 std::string buffer; buffer.resize(size); in_file.read(&buffer[0], size); // 将数据设置到stringstream std::stringstream ss; ss.str(buffer); // 设置整个缓冲区的内容到stringstream 注意:使用std::string来存储二进制数据是安全的,因为std::string可以包含空字符('\0')。但是,我们在使用stringstream的str()方法获取字符串时,返回的std::string对象包含了stringstream中的所有字节(包括空字符)。 另外,我们也可以不通过std::string中转,直接使用stringstream的rdbuf()来读写文件流。但考虑到二进制数据,使用上述方法更直观。 另一种写法(使用文件流和字符串流之间的缓冲区复制): 写:out_file << stringstream_obj.rdbuf(); 读:in_file >> stringstream_obj.rdbuf(); 但是这种方法在二进制数据中可能会遇到问题,因为默认情况下流操作会跳过空白字符(对于读取),而且如果二进制数据中包含某些特殊字符(如EOF标记)可能会提前终止。所以不推荐。 因此,我们推荐使用显式读写整个字符串的方式。 示例代码: 写入二进制文件(从stringstream): ```cpp #include <fstream> #include <sstream> #include <iostream> int main() { // 创建一个stringstream并写入一些二进制数据(可能包含空字符) std::stringstream ss; char data[] = {'H', 'e', 'l', 'l', 'o', '\0', 'W', 'o', 'r', 'l', 'd'}; ss.write(data, sizeof(data)); // 将stringstream的内容写入文件 std::ofstream out_file("test.bin", std::ios::binary); if (out_file) { std::string content = ss.str(); // 获取整个内容(包括空字符) out_file.write(content.c_str(), content.size()); out_file.close(); } else { std::cerr << "Failed to open file for writing." << std::endl; } return 0; } ``` 从二进制文件读取到stringstream: ```cpp #include <fstream> #include <sstream> #include <iostream> int main() { // 打开文件并读取全部内容 std::ifstream in_file("test.bin", std::ios::binary | std::ios::ate); if (!in_file) { std::cerr << "Failed to open file for reading." << std::endl; return 1; } // 获取文件大小 auto size = in_file.tellg(); in_file.seekg(0, std::ios::beg); // 创建缓冲区并读取 std::string buffer(size, '\0'); // 初始化一个大小为size的字符串,内容为size个空字符 if (in_file.read(&buffer[0], size)) { // 将读取的内容设置到stringstream std::stringstream ss; ss.str(buffer); // 验证:输出stringstream的内容(按字节) std::cout << "Read " << size << " bytes: "; std::string content = ss.str(); for (char c : content) { // 以十六进制输出每个字节,避免空字符导致输出中断 std::cout << std::hex << static_cast<int>(c) << " "; } std::cout << std::endl; } in_file.close(); return 0; } ``` 注意:在读取文件时,我们使用std::ios::ate打开文件,以便直接跳到文件末尾获取文件大小,然后将文件指针重置到开头。 总结: 写入:将stringstream的str()返回的字符串整个写入文件(指定长度)。 读取:将整个文件读入一个字符串,然后用这个字符串设置stringstream(通过str()方法)。 这样,我们就可以在stringstream和二进制文件之间转换数据。 注意:这种方法适用于较小的文件,因为一次性读取整个文件需要足够的内存。对于大文件,可能需要分块处理,但stringstream本身也是内存中的流,所以通常用于数据量不是特别大的情况。 如果文件很大,可能需要考虑使用文件流直接处理,而不是通过stringstream中转。 相关问题: 1. 如何分块读写大文件以避免内存不足? 2. 使用std::stringstream处理二进制数据时,如何避免空字符导致的截断问题? 3. 如何将二进制文件的一部分内容读入std::stringstream? 4. std::stringstreamstd::fstream在读写二进制数据时的性能差异? 5. 如何在读写二进制文件时处理字节序(大小端)问题? </think> ### 使用 `std::stringstream` 进行二进制文件读写的方法 #### 一、核心思路 `std::stringstream` 可作为内存中的二进制数据容器,通过以下步骤与文件交互: 1. **写入文件**: 获取 `stringstream` 的数据缓冲区 → 以二进制模式写入文件 2. **读取文件**: 将文件二进制数据读入内存 → 注入 `stringstream` 对象 #### 二、完整代码示例 ```cpp #include <fstream> #include <sstream> #include <iostream> // 将 stringstream 二进制数据写入文件 void writeStringStreamToFile( const std::string& filename, std::stringstream& ss ) { // 获取 stringstream 中的数据 std::string buffer = ss.str(); // 以二进制模式写入文件 std::ofstream outfile(filename, std::ios::binary); if (!outfile) { std::cerr << "文件打开失败" << std::endl; return; } // 关键:直接写入原始字节数据 outfile.write(buffer.data(), buffer.size()); outfile.close(); } // 从文件读取二进制数据到 stringstream void readFileToStringStream( const std::string& filename, std::stringstream& ss ) { // 以二进制模式打开文件 std::ifstream infile(filename, std::ios::binary | std::ios::ate); if (!infile) { std::cerr << "文件打开失败" << std::endl; return; } // 获取文件大小 size_t file_size = infile.tellg(); infile.seekg(0, std::ios::beg); // 重置文件指针[^3] // 准备缓冲区 char* buffer = new char[file_size]; // 读取整个文件到缓冲区 infile.read(buffer, file_size); infile.close(); // 将数据注入 stringstream ss.write(buffer, file_size); // 使用 write 而非 << 操作符[^1] delete[] buffer; } // 使用示例 int main() { // 创建包含二进制数据的 stringstream std::stringstream bin_stream; bin_stream.write("\x48\x65\x6C\x6C\x6o\x00\x01\x02", 8); // 包含空字节的二进制数据 // 写入文件 writeStringStreamToFile("data.bin", bin_stream); // 从文件读取 std::stringstream read_stream; readFileToStringStream("data.bin", read_stream); // 验证读取的数据 std::cout << "读取大小: " << read_stream.str().size() << " 字节"; return 0; } ``` #### 三、关键注意事项 1. **二进制模式必须启用** `std::ios::binary` 标记禁用文本转换,确保原始字节完整性(如空字节 `\x00` 不会截断数据)[^1]。 2. **避免使用 `<<` 和 `>>` 操作符** 对二进制数据必须使用 `.write()` 和 `.read()` 方法,文本操作符会忽略空字节或进行格式转换[^2]。 3. **包含空字节的处理** `stringstream::str()` 返回的 `std::string` 可包含空字节,但需注意: ```cpp // 正确:完整获取包含空字节的数据 std::string data = ss.str(); size_t len = data.size(); // 实际包含空字节的字节数 // 错误:C风格字符串会被空字节截断 const char* cstr = ss.str().c_str(); // 遇到 \x00 会终止 ``` 4. **内存高效用法** 处理大文件时使用分块读写: ```cpp // 分块写入示例 const size_t BLOCK_SIZE = 4096; char block[BLOCK_SIZE]; while (infile.read(block, BLOCK_SIZE)) { ss.write(block, infile.gcount()); // gcount() 获取实际读取数 } ``` #### 四、典型应用场景 1. **协议数据处理**:网络包序列化/反序列化 2. **加密操作**:加解密中间数据缓存 3. **文件格式转换**:不同二进制格式间的转换层 4. **内存压缩**:配合压缩库处理压缩数据流 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值