十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Python实现txt转16进制:字节流与hexdump的实用指南

Python实现txt转16进制:字节流与hexdump的实用指南 简介这是一份面向Python初学者的txt转16进制转换工具包旨在解决纯文本与十六进制存储格式之间的快速转换问题。包内共2个文件包括1个可直接运行的Python脚本与1个用于验证输出效果的txt示例文件压缩包整体仅2KB轻量无依赖已安装Python即可在cmd中直接使用。脚本可将txt内容转换为16进制并按ASCII码格式存储配套示例文件便于对照执行结果帮助理解文件读取、字符编码及进制转换的实现思路。目前已有1233人学习下载结合作者博客中的源码解析可进一步掌握文件操作与编码处理的细节适用于刚接触Python文件处理或需要批量转换文本格式的开发者。 做串口调试那阵子我经常要对着协议文档把配置文本拼成十六进制报文。最烦的不是写代码而是明明手里的数据就在txt里对方却要你按16进制发过去。在线转换工具一次只能处理一小段内容一长就卡死还得手动清空格、删换行。后来干脆用Python写了个txt转16进制的小工具压缩成zip放在本地命令行一条指令就能完成转换输入路径、输出格式、大小写、分隔符都能自己控制顺手还解决了一堆编码排查问题。这篇文章把整个项目的思路、核心代码和踩过的坑摊开讲适合想快速处理文本与字节的Python使用者也适合需要在调试场景下看懂文件原始内容的人。1. 先厘清一个概念txt文件在磁盘上本来就是字节流1.1 你看到的文本其实是编码后的字节计算机不认识你好或者Hello它只认识字节。txt文件在磁盘上存着的本来就是一堆字节一个纯ASCII的Hello实际是48 65 6C 6C 6F这5个字节一个UTF-8编码的你好实际是E4 BD A0 E5 A5 BD这6个字节。所以把txt转成16进制本质上不是转换而是把文件里本来就存在的字节用人类能直接阅读的形式显示出来。16进制只是字节的一种表示方式跟十进制、二进制、八进制并列。之所以大家都用16进制是因为一个字节正好对应两位16进制字符0xFF就是255看一眼就知道这个字节的位模式字节和hex之间不存在尴尬的进位换算。想通这一点后面所有问题都好解释文件内容没有变变的只是看它的方式。1.2 什么情况下非要用十六进制视角不可我整理了自己实际工作中用到hex视角的几个场景你会发现它们大多和文本表象欺骗了你有关排查编码乱码文件打开是乱码你看到的是被错误解码后的字符根本不知道源文件字节到底是什么。转成hex一看立刻能判断是GBK被当成UTF-8读还是BOM缺失导致的误判。协议调试串口、TCP、UDP报文对接时协议文档直接用hex描述帧结构比如帧头AA 55、帧尾0D 0A手头文本不转hex根本没法和文档对照。隐藏字符检查文件里是不是有不可见字符BOM头的EF BB BF长什么样行尾到底是0D 0A还是只有0A这些在普通编辑器里很难100%确认hex视图一眼过。文件内容比对两个txt肉眼看起来一模一样但hex不一样说明存在隐藏差异。这类问题用文本diff工具经常漏掉用hex diff才能暴露。二进制文件格式研究txt只是最简单的入口你真正想分析的可能是某个自定义格式文件先转hex看结构再决定怎么解析。2. 用Python把txt转16进制两行核心代码和一个完整脚本2.1 读文件的正确姿势rb和r之间的坑Python里把文件字节读出来再转hex核心代码极简with open(input.txt, rb) as f: data f.read() hex_str data.hex() print(hex_str)bytes.hex()是Python 3.5开始内置的方法3.8之后还支持分隔符参数。如果你追求更经典的写法可以用标准库里的binascii.hexlify(data)效果一样只是返回的是bytes对象需要用.decode(ascii)转成字符串。关键在rb这个模式。为什么必须加b因为如果你用r文本模式打开Python会默认按系统编码去解码文件内容Windows上通常是GBKLinux和macOS上是UTF-8。遇到解码不了的字节直接抛UnicodeDecodeError就算解码成功文本模式还会把行尾\r\n悄悄转成\n你以为读的是原文件其实已经被动过手脚了。做底层字节处理这一层自动转换必须绕开用rb拿到原始字节后续怎么解析完全由你自己控制。我见过不少初学者报读取txt文件编码错误基本都是忘了这个b。2.2 输出格式化大小写、分隔符、0x前缀拿到hex字符串之后实际用的时候很少直接输出一长串更多的是按需格式化。几个最常见的参数# 全部大写 data.hex().upper() # 每字节之间加空格 data.hex( ) # 每隔一个字节加分隔符第二个参数是字节间隔 data.hex( , 1) # 每字节前加0x前缀适合生成C语言数组 hex_str data.hex( ) hex_str 0x hex_str.replace( , 0x)这些格式化选项看着简单但在实际使用中区别很大。协议调试时我喜欢带空格的大写形式方便和文档逐字节对照写脚本生成测试报文时习惯纯小写无分隔因为后面还要传给bytes.fromhex()做逆运算生成固件数组时就必须用0x前缀。既然没法互相替代做成命令行参数就是最灵活的方案。2.3 一个可以直接用的命令行脚本我最终项目的核心脚本长这样支持普通hex字符串和hexdump两种输出模式也可以处理大文件#!/usr/bin/env python3 # -*- coding: utf-8 -*- txt2hex.py - 将txt文件转换为十六进制表示支持hex字符串和hexdump视图。 import argparse import sys from pathlib import Path BLOCK_SIZE 65536 def read_blocks(file_obj, sizeBLOCK_SIZE): while True: block file_obj.read(size) if not block: break yield block def format_hex_block(block: bytes, upper: bool, sep: str) - str: text block.hex() if upper: text text.upper() if sep: text sep.join(text[i:i2] for i in range(0, len(text), 2)) return text def print_hexdump(block: bytes, offset: int, cols: int): for i in range(0, len(block), cols): line block[i:i cols] hex_col .join(f{b:02X} for b in line) hex_col hex_col.ljust(cols * 3 - 1) ascii_col .join(chr(b) if 32 b 126 else . for b in line) print(f{offset i:08X} {hex_col} |{ascii_col}|) def main(): parser argparse.ArgumentParser(descriptiontxt转16进制小工具) parser.add_argument(input, typePath, help输入txt文件路径) parser.add_argument(-o, --output, typePath, help输出文件路径缺省打印到终端) parser.add_argument(-u, --upper, actionstore_true, help十六进制字母大写) parser.add_argument(-s, --sep, default, help字节之间的分隔符例如空格) parser.add_argument(-d, --hexdump, actionstore_true, helphexdump风格输出带偏移和ASCII) parser.add_argument(-c, --cols, typeint, default16, helphexdump风格时每行字节数默认16) args parser.parse_args() out open(args.output, w, encodingutf-8) if args.output else sys.stdout offset 0 first True try: with open(args.input, rb) as f: for block in read_blocks(f): if args.hexdump: print_hexdump(block, offset, args.cols) else: text format_hex_block(block, args.upper, args.sep) if not first and args.sep: out.write(args.sep) out.write(text) first False offset len(block) if not args.hexdump: out.write(\n) finally: if args.output: out.close() if __name__ __main__: main()用起来很简单# 输出纯hex字符串默认小写 python txt2hex.py input.txt # 大写、每字节空格分隔 python txt2hex.py input.txt -u -s # 输出hexdump风格 python txt2hex.py input.txt -d # 输出到文件 python txt2hex.py input.txt -u -s -o output.hex脚本里我特意用分块读取read_blocks而不是一次性read()原因后面讲大文件时会细说。另外注意普通模式下块与块之间补分隔符的逻辑如果不处理这个边界文件中间会漏一个空格虽然不影响整体可读性但强迫症看着难受而且做逆向转换时容易踩坑。3. 实操中的三个细节编码、换行、字节序3.1 一个中字为什么是e4 b8 ad刚用hex视角看txt的人经常会问为什么一个汉字变成三四个字节这不是bug是编码决定的。以UTF-8为例英文字符兼容ASCII一个英文字母占1个字节而中文字符通常占3个字节比如中就是E4 B8 AD国是E5 9B BD。如果你把文件转成hex后看到一长串E4 BD A0 E5 A5 BD那就是你好的UTF-8字节序列。这个认知在排查乱码时特别有用。你遇到一个乱码txt转成hex后如果看到大量E4、E5、E6开头的三字节序列基本可以断定源文件是UTF-8问题出在用别的编码去解码了它。反之如果看到大量由两个字节组成的序列而第二个字节经常落在80~BF区间那很可能是GBK/GB2312。hex视图不会直接告诉你答案但它给了你判断的依据而不是对着乱码瞎猜。3.2 换行符和BOMhex视角下无处遁形换行符是另一个容易被文本编辑器掩盖的细节。Windows文本文件的行尾是0D 0ACRLFLinux和macOS是0ALF。如果你用文本模式读取文件Python会做换行转换hex出来就全是0A你会以为原文件本来就只有LF这可能会误导判断。用rb模式读出来的是原始字节Windows记事本存的txt转成hex你就能看到行尾是标准的0D 0A。BOM头也一样。UTF-8 with BOM的文件开头会有EF BB BF三个字节不带BOM的没有。这个信息在普通编辑器里通常看不到但在协议对接时非常重要——有些老系统遇到BOM会直接把它当内容发出去导致对端解析第一帧就出错。有了hex工具这种问题一眼就能定位。3.3 大端小端不要在字节流转换中自作聪明关于字节序我见过太多人把txt转16进制和数值存储的大小端混在一起。这里必须说清楚文件里的字节是线性存储的读出来是什么顺序就是什么顺序txt转hex只是把这个顺序原样显示不存在什么低字节在前高字节在后的处理。大小端只有在把多个字节解析成一个数值时才需要考虑。比如内存里有两个字节A1 B2按大端解释成整数是0xA1B2按小端解释是0xB2A1。但如果你只是做文件转hex千万不要主动去翻转字节顺序否则你得到的就不是文件原始内容而是经过你二次加工的数据。这一点在跟硬件联调时尤其容易出错——协议文档里写的01 02就是01 02别因为对方说了一句小端就把文件里的字节顺序改掉字节序转换发生在数值解析层不在文件表示层。4. 从能用变好用hexdump视图和大文件处理4.1 纯hex字符串不够用要带上偏移和ASCII对照接触hex工具多了你会发现专业的十六进制编辑器比如HxD、010 Editor都不是简单输出一长串hex而是显示成三栏地址偏移、十六进制字节、ASCII可打印字符。这叫hexdump风格非常实用。看一个例子。假设有个txt内容如下Hello 你好纯hex字符串长这样48656C6C6F0D0AE4BDA0E5A5BD换成hexdump风格00000000 48 65 6C 6C 6F 0D 0A E4 BD A0 E5 A5 BD |Hello....你好|区别很明显有了偏移量你能快速定位到文件的某个位置有了ASCII栏你能在乱码中快速辨认出哪些区域是正常可读文本哪些是二进制数据。我在排查问题时几乎不用纯hex字符串基本都是hexdump模式。ASCII栏里非可打印字节显示为点号这样控制字符的位置一目了然。4.2 大文件的内存膨胀问题别一把梭txt转hex最容易被忽略的问题是内存膨胀。一个字节转成hex后变成两个字符如果再加空格分隔一个字节最多变成三个字符。也就是说一个100MB的txt文件转出来的hex字符串可能有300MB。如果用一次性read()再hex()Python进程实际占用的内存可能是文件大小的好几倍因为中间还有临时bytes对象、字符串对象的开销。文件到了几百MB甚至GB级别直接吃满内存是常有的事。解决方式就是脚本里写的分块读取。每次读64KB转完立即处理或写入内存占用被压在固定水平跟文件大小无关。这个思路不仅适用于txt转hex任何涉及大文件读写的脚本都该这么干。我自己的习惯是凡是处理可能超过几十MB的文件一律分块不加思考。4.3 批量转换把一堆txt快速转成hex单文件工具好用但实际场景经常是一批文件要转。这时候可以写个简单的遍历脚本或者用参数扩展。最简单的批量处理片段from pathlib import Path import subprocess for path in Path(logs).glob(*.txt): output path.with_suffix(.hex) subprocess.run([python, txt2hex.py, str(path), -u, -s, , -o, str(output)]) print(fconverted: {path} - {output})如果你不想依赖subprocess可以直接在Python脚本里 import 核心函数循环处理。我个人更喜欢保持CLI工具的独立性因为单个工具可以单独测也可以被其他脚本调用组合起来更灵活。批量处理时最好保持输出文件名和输入文件名一致只改扩展名后续查找方便。5. 顺着这个项目能延伸出去的需求5.1 反向hex转回txt做转换工具一定会遇到反向需求手头有一份hex文本想还原成原来的txt。Python里这个操作同样简单hex_str 48 65 6C 6C 6F 0D 0A E4 BD A0 E5 A5 BD raw bytes.fromhex(hex_str.replace( , )) text raw.decode(utf-8) print(text)注意两点bytes.fromhex()只接受纯hex字符空格和换行都需要先清理解码时用什么编码取决于原始文件通常先试UTF-8如果报错再试GBK或者根据hex模式里是否出现大量E4/E5/E6开头的三字节序列来判断。这个反向函数我直接集成到了同一个项目里加了--reverse参数正转反转一套工具搞定。5.2 配合socket调试txt转hex再发包热词里有个c#socket以16进制发送数据其实Python做这件事更顺手。实际调试中我经常需要把一段hex字符串当成报文发出去比如协议文档规定握手包是AA 55 01 02 03 FF在Python里import socket payload bytes.fromhex(AA 55 01 02 03 FF) with socket.create_connection((192.168.1.100, 8080)) as s: s.sendall(payload) response s.recv(1024) print(response.hex( ))这个流程把txt转hex工具和网络调试串成了一条链路txt里的协议数据 - hex字符串 - bytes - socket发送 - 响应转hex查看。比起手动敲字节用txt维护报文内容要清晰得多改动起来也方便。工具只是辅助真正的理解还是来自对字节流本身的认知。5.3 打包成exe把工具交给同事如果你的工作环境里有同事不熟悉Python但你希望他们也能用上这个工具可以用PyInstaller打包成独立exepip install pyinstaller pyinstaller -F txt2hex.py生成的可执行文件在dist目录下拷到任何Windows机器都能直接运行不需要安装Python。打包后用法完全一样txt2hex.exe input.txt -d唯一要注意的是杀毒软件有时会误报PyInstaller打包的程序这属于已知现象建议走公司内部渠道分发说明来源即可。另外如果你这个zip项目是要发给别人的打包时把exe和源码都带上README里写清用法会显得专业很多。5.4 进一步的玩法Qt查看器、浮点数解析工具用顺手之后可以往两个方向继续扩展。一个是图形界面热词里出现过qt读取16进制文件如果你需要经常查看hex内容用PySide6写一个简单的文件选择框配合QPlainTextEdit展示hexdump结果体验会比命令行舒服很多代码量也不大。另一个是数据解析。hex转出来之后有时还要把特定字节段解释成有意义的值比如温度传感器返回的4个字节3F 80 00 00按IEEE 754浮点数解出来是1.0。Python里的实现是import struct raw bytes.fromhex(3F 80 00 00) value struct.unpack(f, raw)[0] # 大端解析 print(value) # 1.0这就呼应了16进制转浮点数计算器这类需求。txt转hex只是第一步配合struct模块你就能从原始字节里提取出真正的物理量完成从看字节到读懂数据的跨越。我在实际使用这个工具时最大的体会是核心转换逻辑真的只有几行真正有价值的部分是保留原始字节视角这个习惯。无论是排查乱码、核对协议报文还是研究陌生文件格式先把txt晾在一边去hex视图里看看它到底存了什么往往比在文本层猜来猜去高效得多。后来我连二进制文件分析也复用了这个脚本毕竟txt只是文件的一种工具本身并不关心扩展名。最后再分享一个小技巧调试时优先用hexdump模式不要在纯hex字符串里数偏移省下来的时间足够你多抓几次包了。本文还有配套的精品资源点击获取
返回列表