如何在Swift中实现类C的结构体二进制文件读写?
Swift 实现 C 语言 fwrite/fread 结构体读写文件的等效方案
要在Swift中实现和C语言fwrite/fread完全等效的结构体文件读写,核心是保证Swift结构体的内存布局与C完全一致,再通过原始字节操作完成读写。以下是对应你提供的C代码的完整实现:
1. 定义匹配C内存布局的Swift结构体
C语言中结构体默认会有内存对齐填充,Swift默认的结构体布局规则和C一致;如果需要严格紧凑布局(对应C的#pragma pack(1)),可以使用@_packed属性。这里先匹配你未开启pack的C结构体布局:
import Foundation // 与C的Creature结构体内存布局完全匹配 struct Creature { var legs: Int8 var eyes: Int8 var hair: Int32 } // 如果需要对应C的#pragma pack(1)紧凑布局,添加@_packed属性: // @_packed struct Creature { // var legs: Int8 // var eyes: Int8 // var hair: Int32 // }
2. 写入结构体到文件(等效fwrite)
通过withUnsafeBytes获取结构体的原始字节指针,将其转换为Data后写入文件,效果和C的fwrite完全一致:
func writeCreatureToFile(_ creature: Creature, path: String) throws { // 将结构体转换为原始字节Data let data = withUnsafeBytes(of: creature) { Data($0) } // 写入文件(对应C的wb+模式:读写、覆盖创建、二进制模式) try data.write(to: URL(fileURLWithPath: path), options: [.atomic, .withoutOverwriting]) }
3. 从文件读取结构体(等效fread)
读取文件的全部字节后,用withUnsafeMutableBytes将字节复制到结构体实例中,对应C的fread:
func readCreatureFromFile(path: String) throws -> Creature { // 读取文件全部字节 let data = try Data(contentsOf: URL(fileURLWithPath: path)) // 验证字节数是否匹配结构体大小 guard data.count == MemoryLayout<Creature>.size else { throw NSError(domain: "FileError", code: 1, userInfo: [NSLocalizedDescriptionKey: "文件字节数与结构体大小不匹配"]) } // 将字节转换为结构体实例 var creature = Creature() _ = withUnsafeMutableBytes(of: &creature) { data.copyBytes(to: $0) } return creature }
完整测试代码
对应你提供的C主函数逻辑:
func main() { let filePath = "/Users/name/Desktop/code/testing.txt" // 定义测试结构体 let birthday = Creature(legs: 6, eyes: 3, hair: 258) // 写入文件 do { try writeCreatureToFile(birthday, path: filePath) print("结构体写入成功") } catch { print("写入错误:\(error.localizedDescription)") return } // 读取文件 do { let shake = try readCreatureFromFile(path: filePath) print("Legs: \(shake.legs) Eyes: \(shake.eyes) Hair: \(shake.hair)") } catch { print("读取错误:\(error.localizedDescription)") } } // 执行测试 main()
关键注意事项
- 内存布局一致性:如果你的C结构体开启了
#pragma pack(1),必须给Swift结构体添加@_packed属性,否则读写的字节会因为填充不匹配而出错。可以通过MemoryLayout<Creature>.size验证大小是否和C的sizeof(struct Creature)一致。 - 字节序问题:如果文件需要跨平台读写(比如从x86设备写到ARM设备),需要手动处理
Int32的字节序(大端/小端),可以用Int32(bigEndian:)或Int32(littleEndian:)转换,对应你C代码中注释的reverseByteOrder逻辑。 - 文件模式:Swift的
write(to:options:)中,.atomic保证写入的原子性,.withoutOverwriting对应C的wb+中的创建覆盖行为;如果需要追加写入,可以调整选项为.append。
内容的提问来源于stack exchange,提问作者aar
相关产品推荐
相关产品推荐

