读写二进制文件

这篇博客展示了如何使用Java进行二进制文件的读写操作,包括将整数、浮点数和双精度浮点数写入文件,以及从文件中读取这些数值。代码示例详细地说明了如何利用ByteBuffer进行数据转换和文件流处理。
import java.io._
import java.nio._

def writeInts(out:OutputStream, a:Array[Int], buf:ByteBuffer, n: Int) {
    var nwritten = 0;
    val ibuff = buf.asIntBuffer
	val bbuff = buf.array
	while (nwritten < n) {
		val todo = if (n-nwritten > ibuff.capacity) ibuff.capacity else (n - nwritten)
		ibuff.put(a, nwritten, todo)
		ibuff.position(0)
		out.write(bbuff,0,todo*4)
		nwritten += todo
	}
}

def writeFloats(out:OutputStream, a:Array[Float], buf:ByteBuffer, n:Int) {
	var nwritten = 0
	val fbuff = buf.asFloatBuffer
	val bbuff = buf.array
	while (nwritten < n) {
		val todo = if (n - nwritten > fbuff.capacity) fbuff.capacity else (n - nwritten)
		fbuff.put(a, nwritten, todo)
		fbuff.position(0)
		out.write(bbuff, 0, todo*4)
		nwritten += todo
	}
}

def writeDoubles(out:OutputStream, a:Array[Double], buf:ByteBuffer, n:Int) {
	var nwritten = 0
	val dbuff = buf.asDoubleBuffer
	val bbuff = buf.array
	while (nwritten < n) {
		val todo = if (n - nwritten > dbuff.capacity) dbuff.capacity else n - nwritten
		dbuff.put(a, nwritten, todo)
		dbuff.position(0)
		out.write(bbuff, 0, 8*todo)
		nwritten += todo
	}
}

//read n ints to a, buf is a buffer
//instream --> buffer --> Array
def readInts(in:InoutStream, a:Array[Int], buf:ByteBuffer, n:Int) {
	val nread = 0//a has read ints from buff
	val ibuff = buf.asIntBuffer
	val bbuff = buf.array
	var readnow = 0// buf has read n bytes from instream
	while (nread < n) {
		val todo = if (n - nread > ibuff.capacity) ibuff.capacity else (n - nread) //for instants, need read 5 ints, 20 bytes, but buff only  has 18 bytes, so first time only read 16 bytes 
		readnow += in.read(bbuff, readnow, todo*4 - readnow) //last time 'readnow' bytes hasnt put in array, but remain in the head of buff
		ibuff.get(a, nread, readnow/4) //put and convert bytes in to ints to array
		ibuff.position(0) //get will get bytes from position
		nread += readnow/4
		if (readnow %4 ! =0) {
			System.arraycopy(bbuff, 4*(readnow/4), bbuff, 0, readnow%4)
		}
		readnow = readnow%4
		
	}
}

def writeBin() {
	val bos = new BufferedOutputStream(new FileOutputStream("tsmat"), 1024)
	val head  = new Array[Int](4)
	val tbuf = ByteBuffer.allocate(16).order(ByteOrder.LITTLE_ENDIAN)
	val rows = 4
	val cols = 5
	val nnz = 8
	head(0) = 231
	head(1) = rows
	head(2) = cols
	head(3) = nnz //nnz
	writeInts(bos, head, tbuf, 4)

	val buff = ByteBuffer.allocate(1024).order(ByteOrder.LITTLE_ENDIAN)
	val colv = Array(0,1,2,4,6,8)
	val rowv = Array(2,1,1,2,2,3,0,2)
	val data = Array(0.3826f,0.5757f,0.2343f,0.4599f,0.5948f,0.6836f,0.49744f,0.92241f)

	writeInts(bos, colv, buff,cols+1)
	writeInts(bos, rowv, buff, nnz)
	writeFloats(bos, data, buff, nnz)
	bos.close
}

def readBin() {
	val bis = new BufferedInputStream(new FileInputStream("s45.1"), 1024)
	val buff = ByteBuffer.allocate(1024).order(ByteOrder.LITTLE_ENDIAN)
	val bbuff = buff.array
	val len = bis.read(bbuff,0,1024)
	println("len = " + len)
	for (i<-1 to 17) print(buff.getInt + " ")
	for (i<-1 to (len/4-17)) print(buff.getFloat + " ")
	bis.close
}



writeBin



<think> 我们被要求提供C++读写二进制文件的方法。根据引用[1]和[2],我们需要注意在Windows和Linux下可能有不同的行为,尤其是文本模式和二进制模式的区别。引用[3]提供了一个读取二进制文件的例子,包括如何获取文件大小和一次读入整个文件。 我们将分步骤讲解: 1. 读写二进制文件的基本步骤。 2. 注意文本模式与二进制模式的区别(重点)。 3. 如何一次读取整个二进制文件(高效方法)。 4. 二进制文件的方法。 同时,我们将生成相关问题。 注意:在Windows和Linux下,二进制模式(`std::ios::binary`)是非常重要的,因为在文本模式下,Windows会将换行符`\n`转换为`\r\n`,而在Linux下则不会。同样,读取时也会进行反向转换。这会导致二进制数据被破坏,如引用[1]中所述。 ### 1. 读取二进制文件 以下是一个读取二进制文件的完整示例: ```cpp #include <fstream> #include <iostream> int main() { // 以二进制方式打开文件 std::ifstream infile("input.bin", std::ios::binary); if (!infile.is_open()) { std::cerr << "无法打开文件" << std::endl; return 1; } // 定位到文件末尾以获取文件大小 infile.seekg(0, std::ios::end); std::streamsize file_size = infile.tellg(); infile.seekg(0, std::ios::beg); // 回到文件开头 // 分配内存 char* buffer = new char[file_size]; // 读取整个文件 if (infile.read(buffer, file_size)) { // 成功读取,现在buffer中包含所有二进制数据 std::cout << "成功读取 " << file_size << " 字节" << std::endl; } else { std::cerr << "读取错误,仅读取了 " << infile.gcount() << " 字节" << std::endl; } // 关闭文件 infile.close(); // 处理buffer中的二进制数据... // 释放内存 delete[] buffer; return 0; } ``` ### 2. 二进制文件 二进制文件的示例: ```cpp #include <fstream> #include <iostream> int main() { // 准备一些二进制数据(示例:一个整数数组) int data[] = {1, 2, 3, 4, 5}; const std::streamsize data_size = sizeof(data); // 以二进制方式入文件 std::ofstream outfile("output.bin", std::ios::binary); if (!outfile.is_open()) { std::cerr << "无法打开文件" << std::endl; return 1; } // 入整个数组 if (outfile.write(reinterpret_cast<const char*>(data), data_size)) { std::cout << "成功入 " << data_size << " 字节" << std::endl; } else { std::cerr << "入错误" << std::endl; } // 关闭文件 outfile.close(); return 0; } ``` ### 重要注意事项 1. **模式选择**:读写二进制文件必须使用二进制模式(`std::ios::binary`),否则在Windows下会因换行符转换导致数据损坏[^1][^2]。 2. **跨平台问题**:二进制文件在不同平台间可能存在字节序(Endianness)差异,如需要跨平台使用,需考虑字节序转换。 3. **文件大小限制**:一次性读取整个文件要求文件大小不超过可用内存。对于大文件,应采用分块读写。 4. **类型安全**:使用`reinterpret_cast`需要确保类型转换的正确性,避免未定义行为。 ### 分块读写大文件 对于大文件,不宜一次性读入内存,可分批读写: ```cpp // 读取大文件(每次读取1MB) constexpr std::streamsize chunk_size = 1024 * 1024; char chunk[chunk_size]; while (infile.read(chunk, chunk_size)) { std::streamsize bytes_read = infile.gcount(); // 处理bytes_read字节的数据... } if (infile.eof()) { // 处理最后一块(小于chunk_size) std::streamsize last_chunk_size = infile.gcount(); // 处理last_chunk_size字节的数据... } ``` ### 总结 - 读二进制文件:打开文件(二进制模式)→读取(或分块读取)→关闭文件。 - 二进制文件:打开文件(二进制模式)→入→关闭文件。 - 关键点:避免文本模式操作二进制文件,防止数据损坏[^2]。 §§相关问题§§ 1. 如何在C++中处理大二进制文件的分块读写? 2. 如何解决跨平台二进制文件的字节序问题? 3. C++中二进制文件操作有哪些常见错误及如何避免? 4. 如何将结构体直接二进制文件?需要注意什么? </think> ### C++ 读写二进制文件实现方法 #### 一、二进制文件读写核心要点 1. **模式选择** 必须使用 `std::ios::binary` 模式打开文件,避免系统自动转换特殊字符(如 Windows 将 `\n` 转 `\r\n`)[^1][^2]: ```cpp // 正确方式 std::ofstream outfile("data.bin", std::ios::binary); std::ifstream infile("data.bin", std::ios::binary); ``` 2. **数据类型处理** 二进制文件直接操作字节流,需使用 `reinterpret_cast` 转换数据类型: ```cpp int data = 12345; // 入 outfile.write(reinterpret_cast<char*>(&data), sizeof(data)); // 读取 infile.read(reinterpret_cast<char*>(&data), sizeof(data)); ``` 3. **文本 vs 二进制模式差异** | 操作类型 | 文本模式 | 二进制模式 | |----------------|--------------------------|------------------------| | 换行符处理 | 自动转换(`\n` ↔ `\r\n`) | 原始字节 | | 数据类型 | 字符/字符串 | 任意数据类型 | | 文件结束符 | 可能添加 `EOF` | 无特殊字符 | | 适用场景 | 文本文件 | 图像/音频/结构体数据 | #### 二、完整读写流程(高效方法) ```cpp #include <fstream> // 二进制文件 void writeBinaryFile(const std::string& filename) { std::ofstream outfile(filename, std::ios::binary); if (!outfile) return; // 错误处理 // 示例数据(结构体+原始字节) struct DataBlock { int id; double value; } block{42, 3.14}; char rawBytes[] = {0xAA, 0xBB, 0xCC}; // 入结构体 outfile.write(reinterpret_cast<char*>(&block), sizeof(block)); // 入原始字节 outfile.write(rawBytes, sizeof(rawBytes)); } // 读取二进制文件(高效单次读取) void readBinaryFile(const std::string& filename) { std::ifstream infile(filename, std::ios::binary | std::ios::ate); if (!infile) return; // 错误处理 // 获取文件大小 const size_t file_size = infile.tellg(); infile.seekg(0, std::ios::beg); // 重置指针[^3] // 一次性读取全部数据 char* buffer = new char[file_size]; infile.read(buffer, file_size); // 解析数据 DataBlock* block = reinterpret_cast<DataBlock*>(buffer); char* rawData = buffer + sizeof(DataBlock); // 使用数据... delete[] buffer; // 释放内存 } ``` #### 三、跨平台注意事项 1. **字节序问题** 不同系统可能有不同字节序(大端/小端),处理网络传输数据时需转换: ```cpp #include <arpa/inet.h> // Linux #include <winsock2.h> // Windows uint32_t normalized = ntohl(original); // 网络字节序转本地 ``` 2. **结构体对齐** 使用 `#pragma pack` 消除平台差异: ```cpp #pragma pack(push, 1) // 1字节对齐 struct CrossPlatformStruct { char flag; int id; }; #pragma pack(pop) ``` 3. **文件路径差异** Windows 用 `\\`,Linux 用 `/`,建议使用 C++17 的 `std::filesystem`: ```cpp #include <filesystem> std::string path = std::filesystem::path("data").append("binary.bin").string(); ``` #### 四、最佳实践 1. **错误处理** 每次操作后检查流状态: ```cpp if (!infile.good()) { // 处理读取错误 } ``` 2. **大文件分块处理** 避免单次读取超大文件(内存映射更高效): ```cpp constexpr size_t CHUNK_SIZE = 4096; char chunk[CHUNK_SIZE]; while (infile.read(chunk, CHUNK_SIZE)) { // 处理当前数据块 } ``` 3. **RAII 管理资源** 使用智能指针避免内存泄漏: ```cpp auto buffer = std::make_unique<char[]>(file_size); infile.read(buffer.get(), file_size); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值