首页 / 资讯中心 / 文章详情

txt转16进制实战:用Python脚本看清文件字节真相

txt转16进制实战:用Python脚本看清文件字节真相 ★ FEATURED ARTICLE
简介这是一份基于Python实现的txt转16进制转换工具包主要面向需要处理数据格式转换的Python初学者与开发者尤其适合在调试通信协议、查看文件二进制内容等场景下快速提取十六进制数据。压缩包体积仅有2KB共收纳2个文件1个py脚本为转换核心可直接在已装Python环境的cmd中运行另1个txt文件为示例文本用户可自行替换内容并验证转换效果。目前该资源已有1237人学习浏览属于轻量实用型脚本。使用者下载后既可获得现成的转换逻辑也可结合py脚本了解文件读取、字符编码与十六进制字符串输出的实现思路通过样例txt快速上手对于不常接触底层表示法的读者直观感受ASCII文本与十六进制编码之间的对应关系也颇有帮助。1. 为什么需要把 txt 转成 16 进制一个调试场景就够说明白一次调试某个跨平台传输模块时两边对报文长度始终对不上日志里看不出任何异常。后来把原始内容存成 txt用 Python 脚本把 txt 逐字节转成 16 进制一看才发现文件里藏着肉眼根本看不见的 0x00 结尾还有几个多余的空格字节把长度字段直接打乱了。txt 转 16 进制本质是把“以字符形态存在的文件”还原成“以字节形态存在的数据”让你看到文本编辑器里看不到的真相。标题里那个 zip 包里的脚本解决的就是协议调试、编码核对、固件抓包这一类“字符层面对不上、字节层面才能对上”的活。适合的人很明确搞嵌入式底层、做串口通信、被文本编码问题坑过的数据工程师。只要手里有一份 txt想看看它真正的字节内容这篇文章就能直接照着用。2. 先想清楚你需要的十六进制是哪一种形态“把 txt 转 16 进制”在真实需求里其实有三种完全不同的意思。第一种把 txt 里的每个字节变成41 42 43这样的可读十六进制字符串方便人眼比对最典型的是协议报文分析。第二种要一个带偏移量的对照视图左边是十六进制字节、右边是 ASCII 可读字符类似命令行里常见的hexdump输出这种是为了看结构、找隐藏字符。第三种txt 文件里本来就已经是别人抄好的十六进制文本你要的不是“再转一次”而是把这份 hex 文本解析回二进制文件。三种需求的代码结构完全不同如果不先确定是哪种就开写脚本大概率要返工。我遇到最多的翻车案例就是把第一种和第三种搞混最后产出的文件完全不是对方想要的形态。2.1 三个常见需求纯 hex 字符串、对照视图、二进制写回纯 hex 字符串输出的核心要求是“连续、无歧义、大小写统一”。它可以不带空格直接拼成一行也可以按字节用空格分开这取决于下游是给人看还是给程序解析。对照视图则多了一个维度偏移量。每一行起始地址从 0x00000000 递增中间是 16 或 32 个字节的 hex右侧把可打印 ASCII 字符原样显示、不可见的替换成点号这样一眼就能看出某个控制字符藏在第几行第几列。二进制写回完全是反向操作把48 65 6c 6c 6f这样的文本还原成Hello的二进制内容常用于把别人从日志里拷出来的报文片段恢复成原始文件。判断需求的方法很简单问一句“你拿这份 hex 要做什么”人眼排查选前面两种要喂给程序或烧进设备选第三种。2.2 编码是前提字节串之前先解决字符集txt 文件在打开之前有一个绕不开的关卡编码。同一个“你好”UTF-8 存的是E4 BD A0 E5 A5 BDGBK 存的是C4 E3 BA C3转出来的十六进制天差地别。所以在写转换脚本之前第一个要回答的问题是这份 txt 是按什么编码写的。我习惯先确认编码再把编码名作为参数传进脚本而不是让 Python 用默认编码去猜。另一个常见坑是 BOM某些文本编辑器保存 UTF-8 文件时会在开头写入EF BB BF直接读进 Python 会把这三个字节一起算进 hex 结果里。如果只关心正文内容就要用utf-8-sig这个编码名让 Python 自动吃掉 BOM。这个决策直接决定后续所有输出对不对所以我一般把它做成命令行参数而不是写死在代码里方便换文件时直接改参数。2.3 三条实现路径对比bytes.hex()、binascii.hexlify、手动循环读出来 bytes 之后转 hex 有三条常见路径效果有细微差别。bytes 对象自带hex()方法返回小写字符串无空格无分隔符最简单直接binascii.hexlify功能和它等价但底层实现更贴近 C 库在循环里反复转换大批量数据时性能更稳定跑批处理我优先用它手动循环f{b:02x}的好处是能自由控制空格、大小写、分组格式适合做对照视图这类自定义输出。选错路径不会报错但可读性和性能会在下一步体现。这里没有什么玄学记住一条就够了要快速出结果用bytes.hex()要稳定跑批用binascii.hexlify要自定义格式再走手动循环。特别提醒一句别用repr()去拼 hexrepr 会同时转义换行和不可见字符产出的根本不是原始字节序列。路径返回形式是否可自定义格式适用场景bytes.hex()连续小写字符串否快速确认内容、传给下游解析binascii.hexlify()连续小写字符串否大文件循环转换、批量跑批手动f{b:02x}完全自定义是hexdump 风格输出、按列分组3. 最小可运行实现一版脚本覆盖三种输出这一章直接给代码。我按上一章的三种需求分别写最小实现三个脚本可以独立运行也可以合并到一个文件里按参数切换。每个脚本都控制在一个文件、一个主函数、一个入口的规模方便你拿去改。先说明一个通用选择读文件的编码我全部做成参数encoding默认utf-8生产环境里遇到 GBK 文件就把参数改成gbk不要把编码判断交给第三方库去猜猜错的代价是整份 hex 全错很难排查。3.1 场景 A纯十六进制字符串输出# txt_to_hex_text.py from pathlib import Path import binascii def txt_to_hex(input_path: str, encoding: str utf-8, block_size: int 8192) - str: 读取 txt 文件返回连续十六进制字符串。 chunks [] with open(input_path, r, encodingencoding) as f: while True: block f.read(block_size) # 按字符块读避免一次性读入大文件 if not block: break raw block.encode(encoding) # 字符串转字节串此时才真正能转 hex chunks.append(binascii.hexlify(raw).decode(ascii)) return .join(chunks) if __name__ __main__: print(txt_to_hex(input.txt, encodingutf-8)[:200])这段代码的逻辑分三步按块读文本、把文本块编码成字节串、用binascii.hexlify转成十六进制字节串后再解码回普通字符串。block_size8192表示每次最多读 8192 个字符对几百 MB 的文本也友好如果你确定文件很小直接f.read()然后data.encode(encoding)一样能跑像我这样写是为了让脚本具备处理大文件的底子。编码这一步必须显式执行因为open(...,r)拿到的是 strPython 不会自动帮你转字节。输出是连续小写 hex 字符串适合丢给下游脚本去解析不适合人眼逐行排查。3.2 场景 B带偏移量的对照视图hexdump 风格# txt_to_hexdump.py def txt_to_hexdump(input_path: str, encoding: str utf-8, cols: int 16) - str: 生成带偏移量和 ASCII 对照的 hexdump 视图。 lines [] with open(input_path, r, encodingencoding) as f: data f.read().encode(encoding) for offset in range(0, len(data), cols): chunk data[offset:offset cols] hex_part .join(f{b:02x} for b in chunk) ascii_part .join(chr(b) if 32 b 126 else . for b in chunk) lines.append(f{offset:08x} {hex_part:{cols * 3}} {ascii_part}) return \n.join(lines) if __name__ __main__: print(txt_to_hexdump(input.txt, encodingutf-8, cols16))这个脚本把整个文件读进来再按cols分组每行输出三部分8 位十六进制偏移量、按字节空格分隔的 hex、ASCII 对照列。f{b:02x}保证每个字节输出两位不足补 032 b 126是可打印 ASCII 范围范围外的控制字符全部显示为点号这是排查隐藏字符的关键。cols16是标准做法改成 8 或 32 也行但下游工具解析时一般认准 16。缺点是一次性read()全文件文件超过一两百 MB 时内存吃紧这一步在第四章专门讲怎么改。3.3 场景 C把十六进制文本写回二进制文件# hex_text_to_bin.py import binascii def hex_text_to_binary(hex_path: str, out_path: str) - None: 读取包含十六进制文本的 txt写回二进制文件。 with open(hex_path, r, encodingutf-8) as f: content f.read() cleaned .join(content.split()) # 去掉所有空白、换行、制表符 raw binascii.unhexlify(cleaned) # 要求输入必须是偶数长度的 hex with open(out_path, wb) as f: f.write(raw) if __name__ __main__: hex_text_to_binary(dump_hex.txt, output.bin)这里最关键的一行是content.split()它能把空格、换行、0x前缀之外的杂散空白全部剔除但剔除不了0x前缀本身。如果源文本里带0x41, 0x42这种格式直接 unhexlify 会报错需要先按逗号分割、去掉前缀再拼接。unhexlify严格要求输入为偶数长度奇数长度会抛binascii.Error这其实是个优点等于在写文件前做了一次合法性校验。写入时必须用wb二进制模式如果用w文本模式Python 会在某些平台上悄悄改掉换行符破坏二进制内容。这种反向转换在生产里最常见的用途是把日志里人工抄录的报文片段还原成固件或数据文件。4. 避坑清单 5 条编码、换行、BOM、内存与格式4.1 中文全部变成乱码 hex编码参数没对齐现象UTF-8 的文本用gbk编码名打开结果不乱码、不报错但转出来的 hex 和预期完全对不上甚至长度都短一截。原因Python 解码用了错误的码表字符被映射成了另一套字节。解决确认源文件编码最简单的方法是打开文件用几种编码试读看哪个不出现UnicodeDecodeError同时肉眼判断内容是否合理更稳妥的是让文件来源方在导出 txt 时固定编码并在脚本里把这个编码写成参数传进去不要依赖默认值。这个坑几乎每个新接手别人 txt 的人都会踩一次因为错误不等于崩溃没有异常提示的错最难查。4.2 换行符被吞或变多文本模式与二进制模式混用现象Windows 生成的 txt 里换行是\r\nLinux 上是\n用r模式读取时 Python 会做通用换行转换把它们都变成\n转出来的 hex 里就少了一个0d。反过来用w写入十六进制解读出的二进制内容时Python 会把\n改写成\r\n二进制文件被污染。原因文本模式自带换行转换逻辑。解决读取时如果你要的是原始字节就不要用文本模式直接用rb读字节再转 hex绕开解码和换行两层处理写入二进制文件一律用wb。想保留哪些字节、想转换哪些字节把这两个意图先在注释里写明能少踩一半这种坑。4.3 BOM 混进数据开头多出来的 EF BB BF现象转换结果开头固定多出efbbbf后面所有字节都正常。原因源 txt 是带 BOM 的 UTF-8 文件BOM 本身就是文件内容的一部分文本模式不会自动去掉它。解决读文件时把编码参数从utf-8改成utf-8-sigPython 会自动跳过开头的 BOM如果你就是要保留 BOM 做严格字节对比那就继续用utf-8并把 BOM 当成正常数据。需要注意的是编码名utf-8-sig只影响解码行为不影响后续encode(utf-8)写回时不会自动重新加 BOM需要自己补。4.4 大文件一次性读完内存直接打满现象一个 2 GB 的 txt 跑转换脚本运行到一半系统卡死最终 OOM 被系统杀掉。原因脚本里用了f.read()不带参数整个文件被一次性载入内存转出的 hex 字符串还要再占一份内存峰值内存是文件大小的好几倍。解决把读取改成循环按块处理第三章场景 A 里的block_size8192就是干这个的如果想要更稳每次读入一行用for line in f逐行处理缺点是跨行的大文本块会被拆散需要自己维护拼接状态。处理大文件的核心原则是读一块、转一块、写一块别让任何环节持有完整文件。4.5 十六进制文本格式五花八门空格、0x 前缀、逗号现象脚本读入 hex 文本后直接抛binascii.Error: Odd-length string或者把0x前缀也当成 hex 字符结果长度对不上。原因源 txt 里有0x41, 0x42、41 42、41\n42各种写法unhexlify只认纯粹的连续 hex 字符。解决在调用 unhexlify 之前做标准化写法如下。import binascii, re def normalize_hex_text(content: str) - bytes: 把带 0x、逗号、空白的 hex 文本变成可解析的字节串。 cleaned content.replace(0x, ).replace(0X, ) cleaned re.sub(r[^0-9a-fA-F], , cleaned) return binascii.unhexlify(cleaned)这段标准化代码先去掉0x和0X前缀再用正则把非十六进制字符全部剔除剩下的连续 hex 才能安全交给 unhexlify。注意replace必须放在正则之前原因很简单删除0x时要避免把0x里面的x留下变成残留字符。格式不统一的问题看似低级但在多人协作的日志流转里特别常见我一般会在脚本里同时保留严格版和宽容版两个函数默认走严格版以暴露格式问题。5. 大文件与提速处理好内存和写盘节奏txt 转十六进制不是只有小文件场景。协议日志、数据库导出的文本、抓包工具保存的可读报文动辄几百 MB 甚至几个 GB。这一章处理的是两个问题内存怎么省、时间怎么省。省内存靠流式读写省时间靠减少不必要的函数调用和选择更底层的转换接口。这两件事做好脚本从“能跑”变成“能在生产机器上长期跑”。5.1 流式读取不要一次性 read 整个文件最常见的错误是data f.read()然后对整个 data 操作。在小文件上没问题文件一大内存占用瞬间飙到文件的 3 到 4 倍原始 bytes、转换后的 hex bytes、再解码后的 str三个对象同时在内存里。流式读写的做法是固定缓冲区大小循环处理。import binascii def stream_txt_to_hex(src: str, dst: str, encoding: str utf-8, chunk_chars: int 65536) - int: 流式读取 txt把连续 hex 写入目标文件返回处理总字节数。 total 0 with open(src, r, encodingencoding) as fin, open(dst, w) as fout: while True: text fin.read(chunk_chars) if not text: break raw text.encode(encoding) fout.write(binascii.hexlify(raw).decode(ascii)) total len(raw) return total if __name__ __main__: print(stream_txt_to_hex(big.txt, big.hex))chunk_chars65536的意思是每轮处理 64K 字符大概 64 万个汉字这个值兼顾了内存占用和循环次数。缓冲区太小会频繁触发读写太大又回到一次性读入的问题。输出端也不用额外缓冲Python 的open(dst, w)自带写缓冲频繁小批量write并不会立刻落盘性能瓶颈主要在编码和 hexlify 上。这个函数返回处理总字节数方便在日志里确认有没有漏读。5.2 输出文件按需选择连续 hex 与可读 hex大文件转出来的连续 hex 字符串没有换行在文本编辑器里打开就是一整行非常卡。如果你生成的文件主要给人浏览我建议按固定列宽手动换行。常见做法是每行 32 字节、用空格分隔。改法不复杂在fout.write之前按列组装字符串只在行与行之间插入换行符。要注意的是加空格和换行会产生额外字符文件体积会比纯 hex 大三分之一左右但如果目标是把 hex 喂给程序解析就不要加任何格式保持纯连续字符串让下游解析逻辑保持简单。这两者在同一套流式框架里只差一行输出代码的差异先想清楚给谁看再决定这一行怎么写。5.3 提速对比什么时候该用 binascii、什么时候放弃循环小文件范围内bytes.hex()和binascii.hexlify的性能差异几乎感知不到真正拉开差距的是手动循环f{b:02x}。每字节一次格式化调用语言层面的解释执行开销极大。实测转一个 100 MB 文件手动循环通常比 hexlify 慢一个数量级所以批量场景里应直接选binascii.hexlify或bytes.hex()。还有一个更快的方向是直接对内存映射文件操作但那需要 mmap 配合代码复杂度和调试成本都上来了除非你每天处理几十 GB 文件否则不值得。我的习惯是先用binascii.hexlify跑一版真到性能瓶颈再考虑下位优化别在一开始就把代码写得晦涩。性能问题用数据说话不要在没跑基准测试之前就用“感觉慢”来决定方案。6. 验证闭环与进阶用法转换完不算完6.1 回读验证hex 转回字节再转回字符转换脚本写完第一件事不是看 hex 内容而是做一次回读验证确保转换可逆。import binascii def verify_roundtrip(origin: str, encoding: str utf-8) - bool: 读取原 txt转为 hex 后再恢复成文本验证一致性。 with open(origin, r, encodingencoding) as f: original_text f.read() raw original_text.encode(encoding) hex_text binascii.hexlify(raw).decode(ascii) restored binascii.unhexlify(hex_text).decode(encoding) return restored original_text print(verify_roundtrip(input.txt, encodingutf-8))这个验证函数不值钱但在交付脚本时是唯一能证明“转换没丢数据”的证据。只要restored original_text成立说明编码、转换、写回整条链路是自洽的。注意验证的是文本内容一致不是字节完全一致所以换行符转换、BOM 处理这些坑在这里不会暴露需要额外的字节级对比才能查出来。6.2 顺手技巧输出附带长度和校验值给脚本加一个--verify开关转换完成后重新打开输出文件计算十六进制文本的字符长度和原文件字节长度是否满足hex_len byte_len * 2再顺手算一个 MD5。这个技巧排查“转换过程是否丢内容”非常快比肉眼比对几千行 hex 可靠得多也是我每次交付脚本时的固定配置。6.3 最后一句经验这个脚本我维护过好几个版本最大的教训是永远不要相信“随便转一下就行”先确认编码、确认需求形态、再加回读验证三个动作加起来不到十分钟却能省掉后面几个小时的排查时间。这方向值不值得做看你的场景是否频繁遇到二进制与文本互转如果每周都要对着报文和日志较劲这份手艺值得沉淀。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站