torch7 DiskFile 完全指南:磁盘文件读写、字节序控制与序列化实战
发布时间:2026/9/25 5:49:43来源:尧图网络
深度学习【免费下载链接】torch7http://torch.ch项目地址https://gitcode.com/gh_mirrors/to/torch7点击查看免费下载导读DiskFile是 torch7 中负责把数据读写到磁盘文件的File实现它继承了 File 的全部能力ASCII/二进制模式、类型化读写、对象序列化并额外提供字节序大小端控制、长整型宽度定制与缓冲控制等专属接口。本文以 doc/diskfile.md 为核心结合 lib/TH/THDiskFile.c、DiskFile.c 与 test/longSize.lua 等源码与测试讲清构造参数、模式选择、字节序 API、longSize 与 noBuffer 的底层原理并给出可复制的实战示例让读者能写出跨平台、可回读、高性能的磁盘数据存取代码。DiskFile 是什么DiskFile是 File 这个抽象类的一个具体子类专门用于对磁盘上的文件执行基本的读写操作。它实现了 File 中定义的所有方法并额外增加了一批与字节序endian编码相关的接口。在 torch7 的 C 层这一关系通过“虚表vtable”机制体现基础读写能力定义在struct THFileVTable见 lib/TH/THFilePrivate.h中而 lib/TH/THDiskFile.c 中THDiskFile_new用一套完整的THDiskFile_read* / THDiskFile_write*函数填充这张表构成磁盘文件的具体实现。一个THDiskFile对象在内存中的结构非常简单见 lib/TH/THDiskFile.ctypedef struct THDiskFile__ { THFile file; // 基类字段读写标志、quiet、binary、autoSpacing 等 FILE *handle; // C 标准库文件句柄 char *name; // 磁盘文件名 int isNativeEncoding; // 当前是否使用本机字节序编码 int longSize; // long 类型的字节宽度0/4/8 } THDiskFile;在 Lua 侧torch7 通过 DiskFile.c 中torch_DiskFile_init注册torch.DiskFile元表并声明其父类为torch.File见 DiskFile.c。因此用print(file)打印一个 DiskFile 时会得到形如torch.DiskFile on foo.dat [status: open -- mode r ]这样的描述字符串其中status为open/closedmode显示当前是只读r、只写w还是读写rw——具体格式化逻辑见 DiskFile.c。默认行为默认情况下DiskFile处于 ASCII 模式。若切换到 binary 模式默认的字节序编码是本机native字节序。这两个默认值在THDiskFile_new中直接初始化self-file.isBinary 0; self-isNativeEncoding 1;见 lib/TH/THDiskFile.c。构造函数与打开模式torch.DiskFile(fileName, [mode], [quiet])打开磁盘上的fileName使用给定的mode打开文件。参数说明参数类型默认值说明fileNamestring必填磁盘文件的路径名modestringrr只读、w只写、rw读写quietbooleanfalse仅当为true时打开失败不抛 Lua 错误而是返回nilLua 绑定侧的实现DiskFile.c验证了参数默认值static int torch_DiskFile_new(lua_State *L) { const char *name luaL_checkstring(L, 1); // 必填 const char *mode luaL_optstring(L, 2, r); // 默认 r int isQuiet luaT_optboolean(L, 3, 0); // 默认 false THFile *self THDiskFile_new(name, mode, isQuiet); luaT_pushudata(L, self, torch.DiskFile); return 1; }三种模式的行为细节r只读打开已存在的文件用于读取。底层调用fopen(name, rb)。w只写创建/覆盖文件用于写入。底层调用fopen(name, wb)。rw读写若文件不存在会自动创建若文件已存在打开后指针定位在文件开头。底层逻辑lib/TH/THDiskFile.c先尝试fopen(name, rb)失败则先fopen(name, wb)创建、关闭再以rb重新打开。模式字符串的合法性由THDiskFile_mode校验只接受长度 1 的r/w或长度 2 的rw其他取值会在THDiskFile_new中抛出参数错误file mode should be r,w or rw见 lib/TH/THDiskFile.c、lib/TH/THDiskFile.c。quiet 参数优雅处理打开失败若quiet为true文件打开失败例如只读模式下文件不存在时不会抛出 Lua 错误而是返回nil方便调用方自行判断f torch.DiskFile(maybe_not_exist.dat, r, true) if not f then print(无法打开文件继续降级处理) end对应 C 实现if(!handle) { if(isQuiet) return 0; else THError(...); }见 lib/TH/THDiskFile.c。注意quiet只影响“打开”这一步的错误处理打开之后的读写错误行为由 File 基类的 quiet()/pedantic() 方法控制。读写模式ASCII 与二进制DiskFile继承自 File 的类型化读写方法包括readByte/readChar/readShort/readInt/readLong/readFloat/readDouble/readBool与对应的write*系列。这些方法受两种编码模式影响ascii()默认数字以人类可读的字符形式存储布尔值写作0/1。结合默认的 autoSpacing()每个数字/布尔值后自动追加空格每次 write 调用后追加换行可通过 noAutoSpacing() 关闭。binary()数字直接以内存中的寄存器表示写入不可读但明显更快字节序由 DiskFile 的专属接口控制见下节。在 C 层这两种模式的分支集中在THDiskFile_read*/THDiskFile_write*的isBinary判断中。以读取为例lib/TH/THDiskFile.cif(dfself-file.isBinary) { nread fread__(data, sizeof(TYPE), n, dfself-handle); if(!dfself-isNativeEncoding (sizeof(TYPE) 1) (nread 0)) THDiskFile_reverseMemory(data, data, sizeof(TYPE), nread); } else { // ASCII 模式逐元素 fscanf如 %hd/%d/%g/%lg ... }各类型在 ASCII 模式下使用的格式化串lib/TH/THDiskFile.c类型读取写入Short%hd%hdInt%d%dFloat%g%.9gHalf%g经TH_float2half转换%.9g经TH_half2float转换Double%lg%.17gLong%ld%ld可见 ASCII 模式特意保留了浮点的高精度表示%.9g/%.17g保证读写往返不损失有效数字而Byte/Char在两种模式下都直接按单字节fread/fwrite处理见 lib/TH/THDiskFile.c。完整示例ASCII 模式读写-- 写 f torch.DiskFile(data.asc, w) -- 默认 ASCII 模式 f:writeInt(42) f:writeDouble(3.14159265358979) f:writeString(hello torch) f:close() -- 确保数据落盘 -- 读假设文件从开头读起 f torch.DiskFile(data.asc, r) local i f:readInt() -- 42 local d f:readDouble() -- 3.14159265358979 local s f:readString(*l) -- hello torch不含行尾符 f:close()readString/writeString的格式语义同样继承自 File*l读取下一行跳过行尾符*a读取文件剩余全部内容由于 torch 对数字类型有更精确的区分Lua 的*n格式不被支持请改用数字读取方法。字节序控制跨平台数据交换的关键二进制模式下多字节数值在磁盘上的排列顺序由字节序决定。torch7 为DiskFile提供了 4 个专属接口nativeEndianEncoding()在 binary 模式下强制使用本机字节序编码即不进行任何字节交换。这也是打开文件后的默认状态。C 实现只是把标志位置 1lib/TH/THDiskFile.cvoid THDiskFile_nativeEndianEncoding(THFile *self) { ... dfself-isNativeEncoding 1; }littleEndianEncoding()在二进制模式下强制使用小端序little end编码数值从最低有效字节到最高有效字节随内存地址递增存储。void THDiskFile_littleEndianEncoding(THFile *self) { ... dfself-isNativeEncoding THDiskFile_isLittleEndianCPU(); }注意其巧妙之处isNativeEncoding的语义是“写入时是否做字节反转”。若本机恰为小端机小端编码即为本机编码isNativeEncoding 1若本机为大端机则置为 0读写时对每个多字节元素执行反转。bigEndianEncoding()在二进制模式下强制使用大端序big end编码数值从最高有效字节到最低有效字节随内存地址递增存储数值重要性随地址增大而递减。实现与上面对称dfself-isNativeEncoding !THDiskFile_isLittleEndianCPU();isBigEndianCPU() / isLittleEndianCPU()返回true当且仅当当前 CPU 采用对应字节序。底层检测非常直观在栈上放一个int x 7检查其首字节是否为 0见 lib/TH/THDiskFile.cint THDiskFile_isLittleEndianCPU(void) { int x 7; char *ptr (char *)x; if(ptr[0] 0) // 首字节是高字节 → 大端 return 0; else // 首字节是低字节 → 小端 return 1; }字节反转的底层实现当isNativeEncoding 0且元素宽度大于 1 字节时读写都会调用THDiskFile_reverseMemory对每个元素按字节块做头尾交换lib/TH/THDiskFile.cstatic void THDiskFile_reverseMemory(void *dst, const void *src, size_t blockSize, size_t numBlocks) { if(blockSize 1) { size_t halfBlockSize blockSize/2; ... for(b 0; b numBlocks; b) for(i 0; i halfBlockSize; i) { char z charSrc[i]; charDst[i] charSrc[blockSize-1-i]; charDst[blockSize-1-i] z; } } }实战场景-- 在大端机上写一个“标准小端”的二进制文件供小端机读取 f torch.DiskFile(tensor_le.bin, w) f:binary() f:littleEndianEncoding() f:writeObject(torch.randn(3, 3)) f:close() -- 读取前先确认机器字节序 print(torch.DiskFile.isLittleEndianCPU()) -- true/false print(torch.DiskFile.isBigEndianCPU()) -- true/false跨平台共享数据时约定好一种固定字节序如小端并在读写两端显式调用littleEndianEncoding()即可。注意这三个接口只对 binary() 模式生效ASCII 模式本身是平台无关的。longSize定制 long 的磁盘宽度longSize([size])long类型在写入/读取文件时按size字节处理size可以是0、4或80表示使用系统默认宽度sizeof(long)。C 层对取值有严格校验lib/TH/THDiskFile.cvoid THDiskFile_longSize(THFile *self, int size) { THArgCheck(dfself-handle ! NULL, 1, attempt to use a closed file); THArgCheck(size 0 || size 4 || size 8, 1, Invalid long size specified); dfself-longSize size; }底层原理三种宽度分支long之所以要单独重写读写逻辑见 lib/TH/THDiskFile.c是因为它需要处理“内存宽度”与“磁盘宽度”不一致的转换longSize 0或longSize sizeof(long)按本机sizeof(long)直接fread/fwrite非本机字节序时做整块反转。longSize 4按 4 字节读写元素经int32_t缓冲转换后落地写入时buffer[i] data[i]完成截断读取时data[i-1] ((int *)data)[i-1]完成扩展。longSize 8分配8*n字节缓冲把 4 字节的long值放入 8 字节槽位的“大端半区”或“小端半区”由big_endian变量决定实现真正的 64 位对齐布局。这正好解释了官方文档中 “0, 4 or 8” 三个取值0是系统默认4用于在 32 位语义下精简存储8用于强制 64 位宽布局。实测用例仓库自带的回归测试 test/longSize.lua 完整展示了这一接口的读写闭环f torch.DiskFile(tensor8.bin,w) f:binary() f:longSize(8) -- 以 8 字节宽度写 long f:writeObject(tensor) f:close() f torch.DiskFile(tensor8.bin,r) f:binary() f:longSize(8) -- 以 8 字节宽度读 tensor2 f:readObject() f:close() tester:assert(tensor:norm()tensor2:norm())同文件中的longSize(4)用例结构完全相同验证了两种宽度的往返一致性。另外 doc/serialization.md 提到torch.load支持b32/b64格式加载 32/64 位 OS 上保存的文件正是与longSize相关的跨平台场景。noBuffer关闭读写缓冲noBuffer()禁用DiskFile的读写缓冲。C 实现调用setvbuf将文件流设为无缓冲模式lib/TH/THDiskFile.cvoid THDiskFile_noBuffer(THFile *self) { ... if (setvbuf(dfself-handle, NULL, _IONBF, 0)) { THError(error: cannot disable buffer); } }适用场景需要确保每次写入立即落盘例如边写边由另一进程/线程读取、或需要实时持久化的日志场景。正常情况下无需调用若调用后想恢复默认缓冲行为可通过 synchronize()对应fflush见 lib/TH/THDiskFile.c或 close() 强制刷盘。序列化writeObject / readObject 与 torch.save / torch.loadDiskFile最常用的场景之一是与 File 的序列化方法配合把任意可序列化对象Torch 对象、table、number、string、纯 Lua 函数等写入磁盘并完整恢复。官方文档 doc/file.md 中给出了引用保持的经典示例这里用DiskFile完整跑一遍-- 构造一个包含“两次同一张张量”的数组 array {} x torch.Tensor(1) table.insert(array, x) table.insert(array, x) -- array[1] 与 array[2] 指向同一地址 array[1][1] 3.14 -- 写入磁盘并关闭确保数据落盘 file torch.DiskFile(foo.asc, w) file:writeObject(array) file:close() -- 重新加载 file torch.DiskFile(foo.asc, r) arrayNew file:readObject() file:close() -- arrayNew[1] 与 arrayNew[2] 依然指向同一地址 -- arrayNew[1][1] arrayNew[2][1] 3.14 arrayNew[1][1] 2.72 -- 此时 arrayNew[1][1] arrayNew[2][1] 2.72这正是 File:writeObject 的“引用去重”机制同一对象只保存一次之后只写引用既省空间又保留对象之间的依赖关系相关实现见 File.lua 的writeObjects/writeObjectsRef记录表。注意若在同一个文件中重复写入同一对象修改后的内容不会再次记录因为只写入指向原对象的引用。对于更上层的需求可直接使用 doc/serialization.md 描述的便捷接口-- torch.save / torch.load 内部就是基于 File 的封装 obj { mat torch.randn(10,10), name 10, test { entry 1 } } torch.save(test.dat, obj) -- 默认二进制格式 obj2 torch.load(test.dat) -- 完整还原 -- 需要跨平台分享时改用 ASCII torch.save(test.asc, obj, ascii) obj3 torch.load(test.asc, ascii)若需频繁改变同一对象内容并反复写入可通过 File 的 referenced(false) 关闭引用追踪避免对象被 File 长期持有见 File.lua 的env.force机制。与 MemoryFile / PipeFile 的对比同为 File 的子类三者定位不同DiskFile读写磁盘文件支持r/w/rw三种模式、字节序控制与 seek 定位。MemoryFile在内存缓冲区上读写适合快速构建/克隆对象见 doc/file.md 的writeObject → seek(1) → readObject克隆技巧。PipeFile通过管道popen读写外部进程输出。值得注意的实现细节在 lib/TH/THDiskFile.c 中THPipeFile_new的虚表与 DiskFile 几乎完全共用仅free换成pclose可见三者共享同一套类型化读写内核差异只在数据源。错误处理与最佳实践打开文件torch.DiskFile(name, mode)默认在出错时抛 Lua 错误pedantic 行为需要容错时传quiettrue并检查返回值是否为nil。读写过程中的错误由 File 的quiet()/pedantic()控制——默认 pedantic 抛错quiet 模式下用 hasError() 检查、clearError() 清除错误标志。定位与遍历position() 返回当前字节位置首位置为1遵循 Lua 索引约定seek(position) 与 seekEnd() 实现随机跳转底层在 64 位平台使用fseeko/ftello并校验位置不超过LLONG_MAX见 lib/TH/THDiskFile.c。落盘写完数据后务必调用close()或至少调用synchronize()触发fflush防止缓冲数据滞留。关闭后的访问对已关闭的 DiskFile 做任何操作都会抛错——C 层每个入口都有THArgCheck(dfself-handle ! NULL, 1, attempt to use a closed file)守卫如 lib/TH/THDiskFile.c。跨平台共享二进制文件建议显式指定字节序如littleEndianEncoding()并视需要以longSize(4)/longSize(8)固定 long 宽度ASCII 文件天然跨平台。小结DiskFile把 C 标准库的文件 I/O 包装为 torch7 统一的File接口默认 ASCII 可读格式、可切换的高性能二进制模式、可显式控制的大小端字节序、可定制的 long 宽度以及基于引用追踪的完整对象序列化。结合 lib/TH/THDiskFile.c 的源码与 test/longSize.lua 的测试用例读者可以放心地在自己的项目中使用torch.DiskFile实现可靠的模型/张量持久化与跨平台数据交换。赞分享深度学习【免费下载链接】torch7http://torch.ch项目地址https://gitcode.com/gh_mirrors/to/torch7点击查看免费下载相关推荐LifeOS CMUX Monitor 工作流基于轮询的 Agent 状态监控与语音通知实战指南LifeOS CMUX Monitor 工作流基于轮询的 Agent 状态监控与语音通知实战指南 导读 monitor 是 LifeOS 的 CMUX 技能中深度学习Nuxt useCookie 完全指南SSR 友好的 Cookie 读写、过期控制与序列化实现Nuxt useCookie 完全指南SSR 友好的 Cookie 读写、过期控制与序列化实现 useCookie 是 Nuxt 内置的 SSR 友好 Coo前端后端Web框架SSRApache Arrow C IPC 读写 API 完全指南流格式与文件格式的序列化实战Apache Arrow C IPC 读写 API 完全指南流格式与文件格式的序列化实战 Apache Arrow 的 C 实现通过 arrow::i数据工程大数据序列化数据分析上一篇serve-handler 安装和配置指南下一篇解决Blender导入三维模型难题Photogrammetry-Importer常见问题与解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网