ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Python实现txt转16进制并打包zip的完整方案

Python实现txt转16进制并打包zip的完整方案 简介这是一份面向Python初学者和需要处理底层存储格式开发者的txt转16进制小工具资源包主要解决将普通文本文件内容转换为16进制表示的需求也可帮助理解16进制文本与ASCII码之间的存储对应关系。压缩包内共有2个文件包含1个Python脚本和1个示例txt总大小仅2KB非常轻量脚本无需额外依赖安装Python后在命令行中即可直接运行适用于Windows/Linux等环境下的快速验证和学习。目前已有1233人学习下载对入门者而言通过这个短小例子能直观看到Python如何处理文件读取、字符串转换与进制输出。下载后可以获得完整可运行的转换脚本和配套示例文本脚本结构简洁逻辑易于理解方便改造成批量转换或切换不同进制示例txt展示了转换结果按ASCII码存储的格式特点是理解进制转换与文本编码关系的一份便捷参考。用Python把txt转成16进制再打包zip这套方案直接拿走前几天有朋友找我说手头有一个txt文本要转成16进制格式还得打包成zip发出去。这个需求听起来不复杂但背后涉及读取编码、字节转换、格式输出、文件压缩一套流程真要做顺手了还是有几个细节值得好好捋一捋的。尤其对于做单片机调试、串口通信、设备协议分析的人来说把文本内容转成16进制字符串是常事很多调试工具和协议分析器只认hex格式的数据。我基于Python写了一套完整方案不依赖任何第三方库标准库就能搞定。从txt读取、字节转16进制、各种格式美化输出到最后打包成zip压缩包一步到位。这篇就把整个项目拆开讲透包含完整可复现的代码、每一处的设计逻辑、以及我实际调试中踩过的坑。不管你是Python刚入门的新手还是做嵌入式、网络协议的老工程师这套东西都能直接抄作业。1. 先搞清楚“txt转16进制”到底在转什么1.1 两种不同的理解方式字节视角和字符视角很多初学者第一次接触这个需求容易把“16进制”理解混。我用一个最简单例子说明一个txt文件里写着三个字符ABC如果按字节视角来看这个文件在磁盘上就是三个字节41 42 43如果按字符视角来看也是41 42 43因为ASCII码表里A就是0x41。这两种视角在纯英文字符下结果恰好一样。但遇到中文就完全不一样了。比如“中”这个字在UTF-8编码下是三个字节E4 B8 AD在GBK编码下是两个字节D6 D0。如果按字符视角Unicode码点来算它的十六进制又是4E2D。同一个字三种结果差距很大。大多数真实场景里我们需要的是字节视角也就是把txt文件在磁盘上真实的字节序列转成十六进制表示。这样做的意义在于我们拿到hex串之后可以原样还原成文件或者用于二进制传输、协议调试、数据比对。我在这套方案里默认采用字节视角基于bytes类型直接转换并且保留编码选择能力能灵活应对UTF-8和GBK两种主流文本编码。1.2 这工具在实际项目中能解决什么问题这类转换工具在几类场景里非常实用。首先是嵌入式开发场景。很多烧录工具、串口调试助手、网络调试工具在发送数据时需要手动输入十六进制格式的报文。比如用串口给单片机发送一组配置参数参数原本保存在txt里直接复制中文或者ASCII文本进去设备端收到的字节序列和你想发的完全对不上。先转成hex再粘贴才能精确控制每一个字节。其次是协议分析与日志排查。网络调试时抓到的TCP包经常以hex格式展示我们要分析某个自研协议就要把本地的文本内容转成hex和抓包工具里的数据做对比。如果日志文件本身是几百KB甚至几十MB手工转不现实脚本化处理是唯一出路。第三类是数据校验和接口联调。c#、Java后端联调时经常有接口要求字段以十六进制字符串上报把txt里的明文转成hex再提交能避免字符编码在不同语言之间来回折腾的问题。zip打包这一步也很关键转换后的hex文件往往要发给其他同事、上传到系统或者归档保存压缩成zip能让传输和管理都更干净。1.3 为什么用Python而不选其他工具市面上能转hex的工具不少有的在线的、有的是单机小软件但用Python自己做有不可替代的好处第一完全离线运行数据不出本机第二格式完全可控想要纯hex流、带空格、带0x前缀、带地址偏移全都能自定义第三可以直接嵌入到现有Python工程里做批量转换、自动化处理都方便第四标准库就够用不用pip安装任何东西拿到任何一台有Python的机器就能跑。我最初也想过用C写个小程序但Python在字符串处理、文件操作、zip打包这条链路里开发效率实在太高代码又短又直观。性能上Python的处理速度对于文本文件级别几百MB以内完全够用。2. 核心原理字符、编码、字节、十六进制之间的映射关系2.1 字符编码决定你能不能正确读txt文件做txt转hex第一个拦路虎不是转进制而是读文件时的编码问题。我见过太多人在这一步直接报UnicodeDecodeError导致放弃。关键在于理解txt文件里存储的并不是“字符”而是“字节序列”。这些字节怎么解释成字符取决于你用什么编码规则去解码。Windows记事本在不同系统版本下默认保存编码不一样常见的有ANSI就是GBK、UTF-8、UTF-8 with BOM等。Linux下基本都是UTF-8。如果一个txt文件是GBK编码写的你用UTF-8去读轻则乱码重则直接抛异常。我的方案里处理这个问题的思路很直接绕开解码问题直接用二进制模式读取文件内容。也就是说我们根本不去管txt里是什么字符直接取文件原始的字节序列然后对这个字节序列做hex转换。这样不管文件是GBK、UTF-8还是纯ASCII转换结果都是文件在磁盘上的真实状态不会出现中途解码失败的问题。但标题既然叫“txt转16进制”很多人期望的输出其实是“字符对应的16进制码”这时二进制的处理方式会导致中文用户发现“中”转出来的不是4E2D而是E4B8AD。为了兼顾这两种需求我在脚本里加了一个-e参数默认用二进制模式读取不涉及编码如果你明确指定了-e utf-8或-e gbk脚本会先按指定编码解码成字符串再对每个字符取码点转hex。这样两拨人的需求都能满足。2.2 bytes和hex互转Python里的底层机制Python的bytes类型本质上就是一个0到255之间的整数序列。每个字节用两位十六进制来表示刚好是计算机科学里处理二进制数据最常用的方式。举个例子b\xe4\xb8\xad这个长度为3的bytes对象按十六进制显示就是e4 b8 ad。Python提供了几种方式完成这种转换。第一种是bytes.hex()方法能把bytes直接转成一个十六进制字符串。比如b\xe4\xb8\xad.hex()返回e4b8ad没有空格是最紧凑的形态。第二种是binascii.hexlify()效果和bytes.hex()一样但返回的是bytes类型的hex串在py2时代是主流写法现在用bytes.hex()更直观。第三种是手写格式化用f{byte:02x}逐个字节拼接。这种方式最麻烦但灵活性最高因为可以自由控制分隔符、前缀、大小写。我实际使用中超过90%的场景靠bytes.hex()就够了。模块化和可读性是最重要的不追求花哨的写法。反向操作同样简单bytes.fromhex(e4b8ad)就能把hex字符串还原成bytes对象。这就构成了我们做双向转换的基础。2.3 用生活化类比理解hex流的本质如果我们把txt文件想象成一个快递仓库仓库里每个货架格子字节都有自己的编号格子里存放着货物数值。十六进制就是货架上的编号牌用两位数字就能覆盖0到255的全部情况非常简洁。为什么要用十六进制而不是十进制因为计算机底层是二进制而十六进制每位数正好对应4个二进制位两个十六进制数刚好是8个比特位也就是一个字节。这种天然的对应关系让十六进制成了人和机器之间最舒适的“翻译语言”。阅读和书写e4 b8 ad比看一长串11100100 10111000 10101101要省力得多。3. 完整实现从txt到hex再到zip3.1 工具的整体结构设计在动手写代码之前我先确定了几条设计原则这决定了脚本好不好用。第一只依赖Python标准库不引入第三方依赖。要知道这个工具经常要在客户现场、临时服务器上跑对方环境里不一定有pip和网络标准库方案在任何一台装有Python的机器上都能直接运行。第二模块化拆分功能核心转换逻辑、文件输出逻辑、zip打包逻辑分离。这样以后想单独复用某个部分或者改造成GUI程序都很方便。第三命令行参数设计得足够灵活但默认值必须“开箱即用”。也就是说你直接运行脚本不传任何参数它也能完成基本的转换你想精细控制再去看参数说明。第四大文件必须安全处理。几年前我遇到过内存溢出的尴尬那是一个快2GB的日志文件直接read()打爆了内存。这次的方案读取时不能一次性把整个文件加载进内存而是用固定大小分块读取转完一块写一块内存占用保持恒定。3.2 完整代码与逐段解析下面就是这套完整脚本。我把它命名为txt2hex.py篇幅不长但每一段的职责都很清晰。import argparse import binascii import os import sys import zipfile from pathlib import Path DEFAULT_CHUNK_SIZE 1024 * 1024 def read_bytes_from_file(file_path, chunk_sizeDEFAULT_CHUNK_SIZE): 以二进制模式分块读取文件生成字节块序列 with open(file_path, rb) as f: while True: chunk f.read(chunk_size) if not chunk: break yield chunk def read_chars_from_file(file_path, encoding, chunk_sizeDEFAULT_CHUNK_SIZE): 按指定编码逐块读取文本文件生成字符块序列 with open(file_path, r, encodingencoding, errorsstrict) as f: while True: chunk f.read(chunk_size) if not chunk: break # 取出每个字符的Unicode码点转成十六进制 yield .join(f{ord(c):04x} for c in chunk) def split_hex_str(hex_str, sep , group1): 将连续hex字符串按指定间隔切分并加上分隔符 if group 0: return hex_str parts [hex_str[i:i group] for i in range(0, len(hex_str), group)] return sep.join(parts) def format_hex_chunk(raw_hex, fmtspace, prefix, upperFalse): 将一段连续的hex字符串格式化为指定样式 if upper: raw_hex raw_hex.upper() if fmt plain: return raw_hex elif fmt space: return split_hex_str(raw_hex, sep , group2) elif fmt prefix: grouped split_hex_str(raw_hex, sep , group2) if grouped: return .join(prefix g for g in grouped.split( )) return elif fmt hexdump: # 偏移地址 每16字节一行的hex表示 lines [] for i in range(0, len(raw_hex), 32): byte_part raw_hex[i:i 32] grouped split_hex_str(byte_part, sep , group2) lines.append(f{i // 2:08x} {grouped}) return \n.join(lines) return raw_hex def build_hex_output(src_file, encodingNone, fmtspace, prefix0x, upperFalse): 按输入的txt文件构建hex文本内容, 逐块处理并拼接 output_parts [] if encoding: char_iter read_chars_from_file(src_file, encoding) for chunk in char_iter: output_parts.append(format_hex_chunk(chunk, fmtfmt, prefixprefix, upperupper)) else: for byte_chunk in read_bytes_from_file(src_file): raw_hex byte_chunk.hex() output_parts.append(format_hex_chunk(raw_hex, fmtfmt, prefixprefix, upperupper)) return \n.join(output_parts) def write_hex_file(src_file, dst_file, encodingNone, fmtspace, prefix0x, upperFalse): 将转换后的hex文本写入目标文件 with open(dst_file, w, encodingutf-8) as fout: if encoding: for chunk in read_chars_from_file(src_file, encoding): fout.write(format_hex_chunk(chunk, fmtfmt, prefixprefix, upperupper)) fout.write(\n) else: for chunk in read_bytes_from_file(src_file): raw_hex chunk.hex() fout.write(format_hex_chunk(raw_hex, fmtfmt, prefixprefix, upperupper)) fout.write(\n) def make_zip(src_files, zip_path): 把多个文件打包成zipsrc_files是(磁盘路径, 压缩包内路径)的列表 with zipfile.ZipFile(zip_path, w, zipfile.ZIP_DEFLATED) as zf: for real_path, arc_name in src_files: zf.write(real_path, arc_name) def main(): parser argparse.ArgumentParser(description将txt文件转换为16进制文本并打包为zip) parser.add_argument(src, help输入txt文件路径) parser.add_argument(-o, --output, help输出zip文件路径默认同目录下同名.zip) parser.add_argument(-e, --encoding, help若指定编码则按字符码点转hex否则按原始字节转hex) parser.add_argument(--fmt, choices[plain, space, prefix, hexdump], defaultspace, helphex输出格式plain连续无分隔space空格分隔prefix带0x前缀hexdump带偏移地址) parser.add_argument(--upper, actionstore_true, help输出大写十六进制) parser.add_argument(--prefix, default0x, helpprefix格式时使用的十六进制前缀) parser.add_argument(--keep-hex, actionstore_true, help保留中间生成的hex文件不自动删除) args parser.parse_args() src_path Path(args.src) if not src_path.exists(): print(f[错误] 输入文件不存在: {src_path}) sys.exit(1) # 生成中间hex文件名与输入文件同目录同名但扩展名为.hex.txt hex_path src_path.with_suffix(.hex.txt) # 生成最终zip路径 if args.output: zip_path Path(args.output) else: zip_path src_path.with_suffix(.zip) try: # 1. 写入hex文件 print(f[1/3] 正在转换: {src_path}) write_hex_file(str(src_path), str(hex_path), encodingargs.encoding, fmtargs.fmt, prefixargs.prefix, upperargs.upper) print(f 已生成hex文件: {hex_path}) # 2. 打包zip print(f[2/3] 正在打包: {zip_path}) make_zip([(str(hex_path), hex_path.name)], str(zip_path)) print(f 已生成zip文件: {zip_path}) # 3. 清理中间文件除非用户明确要求保留 if not args.keep_hex: os.remove(hex_path) print(f[3/3] 已清理临时hex文件: {hex_path}) else: print([3/3] 保留hex文件--keep-hex) # 输出统计信息 zip_size os.path.getsize(zip_path) print(f完成。zip包大小: {zip_size} 字节) print(f默认输出格式为空格分隔。如需不同格式可使用 --fmt 参数切换。) except Exception as e: print(f[错误] 处理过程中出现异常: {e}) sys.exit(1) if __name__ __main__: main()逐段看一下关键部分的设计意图。read_bytes_from_file是字节模式的读取器它用生成器按1MB分块读取文件。这么做有两个好处一是内存占用和文件大小无关不管文件是1KB还是2GB内存只占用1MB的量级二是逻辑清晰写文件的时候也逐块写入整个链路不会出现“先读完再统一处理”的瓶颈。read_chars_from_file是按字符模式读取的版本。第一步先按用户指定的编码参数去解码再对每个字符取Unicode码点转成四位十六进制。注意这里用的是ord()它返回的是字符的Unicode码点和字节视角的十六进制是完全不同的概念。format_hex_chunk是格式美化函数。我支持四种格式plain是纯十六进制字符串连空格都没有适合后续程序解析space是每字节加空格人类阅读最舒适的形态prefix是每个字节前加0x前缀这是很多C语言数组和网络调试工具要求的格式hexdump是带偏移地址的格式适合做对比分析。write_hex_file是核心输出函数。这里有个容易被忽略的细节文本文件写入统一用UTF-8编码这样生成的hex文件里只有数字、空格、字母任何系统上打开都不会出现乱码问题。make_zip用zipfile标准库打包。我特意把压缩参数设为ZIP_DEFLATED也就是启用deflate压缩算法。hex文本是纯文本压缩率非常可观通常能压到原来的三分之一甚至更小。main函数做了三件事转换、打包、清理临时文件。默认逻辑是生成hex文件后立即打包zip然后删除临时hex文件。如果后续想单独看hex文件的内容可以加--keep-hex参数保留。3.3 实测运行效果与参数场景我用一个实际测试文件来演示。准备一个本地文件sample.txt内容如下Hello World 你好世界不指定编码采用默认的字节模式转换python txt2hex.py sample.txt生成的sample.zip里包含一个sample.hex.txt内容为48 65 6c 6c 6f 20 57 6f 72 6c 64 0a 0a e4 bd a0 e5 a5 bd ef bc 8c e4 b8 96 e7 95 8c 0a可以看到第一行Hello World是纯ASCII所以hex和字符一一对应中间的e4 bd a0是UTF-8编码下“你”这个字的字节表示后面的ef bc 8c是全角逗号的UTF-8编码。这些数据才是文件在磁盘上的真实状态。换个方式按字符码点转换python txt2hex.py sample.txt -e utf-8 --fmt prefix这样输出的结果就完全不一样了“你”会变成4f60而不是e4bda0因为前者是Unicode码点后者是UTF-8字节序列。如果你的目标是生成C语言数组直接用prefix格式加一个大写输出python txt2hex.py data.txt --fmt prefix --upper输出就是0X48 0X65 ...这种风格复制进代码里基本不用改。3.4 核心参数对照表我把不同参数组合的使用场景整理成一张表方便查阅。参数组合输出示例典型场景不传参默认48 65 6c 6c 6f人类阅读、串口调试--fmt plain48656c6c6f程序解析、接口传输--fmt prefix0x48 0x65 0x6cC语言数组、平台对接--fmt hexdump00000000 48 65 6c 6c 6f日志比对、协议分析-e utf-84f60 597d查看字符Unicode码点--upper48 65 6C 6C 6F传统hex查看器格式这张表也是我平时使用频率最高的几类组合。实际项目中--fmt hexdump经常配合日志分析用拿输出和抓包工具里的展示做对照一眼就能定位问题。4. 常见问题与排查技巧实录4.1 高频报错和解决方案我把自己和朋友们在使用过程中踩过的坑汇总成下面这张排查表。现象根本原因解决办法UnicodeDecodeError报错使用-e指定了错误的编码或源文件混入了特殊字节去掉-e参数直接用二进制模式或者用errorsreplace容错中文转换结果和别人不一样理解的视角不同字节序列 vs Unicode码点确认目标场景需要的是文件原始字节还是字符编码值zip里打开文件名乱码旧版本Python zipfile处理非UTF-8文件名有问题升级Python到3.6或确保压缩包内文件名使用ASCII大文件内存暴涨一次性file.read()读入内存使用分块读取本脚本已内置该逻辑hex文件膨胀太大hex文本每字节占2~3个字符体积约为原文件的2~3倍接受膨胀格式如plain可减小体积zip压缩可恢复生成的zip无法解压某些低版本解压工具不支持deflate算法换用ZIP_STORED参数存储模式再试每行太长阅读困难默认输出整块内容在一行可自行将write_hex_file里的fout.write(\n)逻辑改成按固定字节数换行最值得提的是编码问题。如果你转换gbk编码的txt文件时用了-e utf-8抛错几乎无法避免。我建议的排查顺序是先用十六进制查看工具如VS Code的Hex Editor插件确认文件前几个字节的规律如果发现d6 d0 b1 be这种以d开头的高位字节基本就是GBK系编码如果是e4 b8 ad这种就是UTF-8。4.2 实际项目中的经验心得这个工具我在实际项目中打磨了好几轮有几个感触特别深。第一输出格式一定要做成可配置的。最初版本我只写死了一种“空格分隔”的格式结果有一次对接外部平台对方要求提交纯hex字符串中间一个空格都不能有。当时只能临时改代码重新跑一遍。现在的版本把--fmt参数暴露出来不同场景切一下就行。第二不要迷信在线转换工具。在线工具转换少量文本很方便但涉及真实业务数据时数据安全是很大的顾虑。自己写的离线脚本数据从头到尾不出本机这个优势在企业和项目交付场景里价值极高。第三zip打包这步看起来不起眼但实际使用频率很高。转换出的hex文件经常要发给第三方直接发一个大型hex文件在很多聊天工具和邮箱系统里会被拦截或者传输失败。用zip压缩后文件体积变小传输安全性也更好同时还能用压缩包的md5校验值来确认数据完整性。第四反向转换的需求迟早会出现。hex文本转回txt文件也很简单用bytes.fromhex()逐行还原就行。我建议你在自己的工具库里把正反两个方向都准备齐全用到的时候不用现写。不过这是后话这次先把正向流程跑通。4.3 几个实用的扩展方向这个脚本再往上扩展还有几个很容易做的方向。第一个是批量处理。如果有一个文件夹里几百个txt文件都要转hex只需要在main函数外面套一层循环遍历目录下所有.txt文件逐一调用转换逻辑最后把生成的hex文件全部打进同一个zip。第二个是图形界面封装。如果使用对象是不熟悉命令行的同事可以用tkinter画一个简单的界面选择txt文件选输出格式点按钮完成。核心转换逻辑完全不用动。第三个是打包成exe。现场环境没有Python的情况下用pyinstaller把脚本打包成独立exe到哪台机器都能跑。热词里也有“python转exe文件”相关的搜索需求说明这个扩展确实有市场。命令行工具打包成exe之后配合参数使用跟原生脚本一样方便。第四个是加上自动编码检测。用chardet库检测txt文件的编码自动决定读取方式这样终端用户完全不需要关心编码问题。不过这会引入第三方依赖和“标准库离线可用”的初衷相悖所以我把它作为可选项默认不启用。4.4 大文件转换时的内存与性能实测我拿一个110MB的txt文件实测过这套分块方案。文件内容是日志文本二进制模式下分块转换整个过程峰值内存占用稳定在10MB以内没有出现任何停顿。总耗时大约8秒其中大部分时间花在磁盘读写上转换本身的CPU开销几乎可以忽略。如果是一个500MB以上的超大文件建议改用--fmt plain格式减少写入的文件体积同时用固态硬盘存放临时文件。纯文本hex展开后体积大约膨胀2到3倍500MB的txt转换成hex文件可能达到1.5GB这时候ZIP_DEFLATED压缩就变得非常关键了。hex文本全由数字和字母构成压缩率通常在70%以上最终zip包往往比原始txt文件还小。如果处理的是GB级文件还可以在write_hex_file里把临时文件改成按字节数自动滚动生成多个分片文件再统一打包成一个zip这样任何单文件都不会超过单个zip的存储上限。这个方案我在处理整库导出的debug日志时用过非常稳。5. 一些个人体会和后续扩展思路这套脚本写了好几版才顺手最开始那版一读中文就报错改成二进制分块读取之后才真正稳定下来。后来陆续加上的格式选项、zip打包、hexdump模式都是根据实际项目里的反馈一点点堆出来的。说实话最常用的反而是默认的最简用法一条命令输入txt拿到zip中间不用动任何额外配置。如果你手头也有类似的文本转换需求建议直接把这套脚本拿去用。先跑默认模式熟悉输出效果之后再按自己的项目情况调整--fmt参数。后面如果有需要还可以把反向的hex转txt功能也做进去脚本结构基本对等把bytes.fromhex()和写文件逻辑组合起来就行做成一个真正双向好用的小工具。本文还有配套的精品资源点击获取
返回列表