Ai
1 Star 0 Fork 0

李勃/file_header_parser

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
file_header_parser.py 10.53 KB
一键复制 编辑 原始数据 按行查看 历史
李勃 提交于 2025-04-27 09:00 +08:00 . v1
import tkinter as tk
from tkinter import ttk, filedialog, scrolledtext
import struct
import os
class FileHeaderParser:
def __init__(self, root):
self.root = root
self.root.title("文件头解析器")
self.root.geometry("1000x600") # 增加窗口宽度
# 加载模板
self.templates = {}
self.load_templates()
# 文件扩展名到模板名称的映射
self.extension_to_template = {
# 图像格式
'.bmp': 'BMP文件头',
'.png': 'PNG文件头',
'.jpg': 'JPG文件头',
'.jpeg': 'JPG文件头',
'.gif': 'GIF文件头',
'.tiff': 'TIFF文件头',
'.tif': 'TIFF文件头',
# 音频格式
'.mp3': 'MP3文件头',
'.wav': 'WAV文件头',
# 视频格式
'.mp4': 'MP4文件头',
'.avi': 'AVI文件头',
# 文档格式
'.pdf': 'PDF文件头',
'.doc': 'DOC文件头',
'.docx': 'DOC文件头',
'.xls': 'XLS文件头',
'.xlsx': 'XLS文件头',
'.ppt': 'PPT文件头',
'.pptx': 'PPT文件头',
# 压缩格式
'.zip': 'ZIP文件头',
# 可执行文件格式
'.exe': 'PE文件头',
'.dll': 'PE文件头',
'.sys': 'PE文件头',
'.drv': 'PE文件头',
'.ocx': 'PE文件头',
'.cpl': 'PE文件头',
'.scr': 'PE文件头'
}
# 顶部区域
self.top_frame = ttk.Frame(root, padding="5")
self.top_frame.pack(fill=tk.X)
# 文件选择
self.file_frame = ttk.Frame(self.top_frame)
self.file_frame.pack(side=tk.LEFT, fill=tk.X, expand=True)
ttk.Label(self.file_frame, text="文件:").pack(side=tk.LEFT, padx=(0, 5))
self.file_path = tk.StringVar()
self.file_entry = ttk.Entry(self.file_frame, textvariable=self.file_path)
self.file_entry.pack(side=tk.LEFT, fill=tk.X, expand=True)
self.browse_button = ttk.Button(self.file_frame, text="选择文件", command=self.browse_file)
self.browse_button.pack(side=tk.LEFT, padx=5)
# 解析按钮
self.parse_button = ttk.Button(self.top_frame, text="解析文件头", command=self.parse_header)
self.parse_button.pack(side=tk.RIGHT, padx=5)
# 中间区域(左右布局)
self.middle_frame = ttk.Frame(root, padding="5")
self.middle_frame.pack(fill=tk.BOTH, expand=True)
# 左侧区域
self.left_frame = ttk.Frame(self.middle_frame, width=300) # 固定左侧宽度
self.left_frame.pack(side=tk.LEFT, fill=tk.BOTH)
self.left_frame.pack_propagate(False) # 防止自动调整大小
# 模板选择
self.template_frame = ttk.Frame(self.left_frame)
self.template_frame.pack(fill=tk.X, pady=5)
ttk.Label(self.template_frame, text="选择模板:").pack(side=tk.LEFT)
self.template_var = tk.StringVar()
self.template_combo = ttk.Combobox(self.template_frame, textvariable=self.template_var, values=list(self.templates.keys()))
self.template_combo.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
self.template_combo.bind('<<ComboboxSelected>>', self.on_template_selected)
# 模板内容
ttk.Label(self.left_frame, text="模板内容:").pack(anchor=tk.W, pady=5)
self.format_text = scrolledtext.ScrolledText(self.left_frame, width=35) # 减小文本框宽度
self.format_text.pack(fill=tk.BOTH, expand=True)
# 右侧输出
self.right_frame = ttk.Frame(self.middle_frame)
self.right_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(10, 0))
ttk.Label(self.right_frame, text="解析结果:").pack(anchor=tk.W)
# 创建Treeview控件
self.result_tree = ttk.Treeview(self.right_frame, columns=('field', 'type', 'value', 'hex'), show='headings')
# 设置列标题
self.result_tree.heading('field', text='字段名')
self.result_tree.heading('type', text='类型[字节数]')
self.result_tree.heading('value', text='值')
self.result_tree.heading('hex', text='原始数据(HEX)')
# 设置列宽
self.result_tree.column('field', width=150, minwidth=100)
self.result_tree.column('type', width=100, minwidth=80)
self.result_tree.column('value', width=150, minwidth=100)
self.result_tree.column('hex', width=200, minwidth=150)
# 添加滚动条
scrollbar = ttk.Scrollbar(self.right_frame, orient=tk.VERTICAL, command=self.result_tree.yview)
self.result_tree.configure(yscrollcommand=scrollbar.set)
# 放置控件
self.result_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# 绑定列宽调整事件
self.result_tree.bind('<Configure>', self.on_tree_configure)
def load_templates(self):
try:
with open('templates.txt', 'r', encoding='utf-8') as f:
content = f.read()
current_template = None
current_content = []
for line in content.split('\n'):
line = line.strip()
if not line:
if current_template and current_content:
self.templates[current_template] = '\n'.join(current_content)
current_content = []
continue
if line.startswith('[') and line.endswith(']'):
if current_template and current_content:
self.templates[current_template] = '\n'.join(current_content)
current_template = line[1:-1]
current_content = []
else:
current_content.append(line)
if current_template and current_content:
self.templates[current_template] = '\n'.join(current_content)
except Exception as e:
print(f"加载模板文件失败: {str(e)}")
self.templates = {"错误": "加载模板失败"}
def on_tree_configure(self, event):
# 获取Treeview的宽度
tree_width = self.result_tree.winfo_width()
# 计算每列的宽度比例
total_width = tree_width - 20 # 减去滚动条宽度
self.result_tree.column('field', width=int(total_width * 0.25))
self.result_tree.column('type', width=int(total_width * 0.15))
self.result_tree.column('value', width=int(total_width * 0.25))
self.result_tree.column('hex', width=int(total_width * 0.35))
def on_template_selected(self, event):
template_name = self.template_var.get()
if template_name in self.templates:
self.format_text.delete(1.0, tk.END)
self.format_text.insert(1.0, self.templates[template_name])
def select_template_by_extension(self, file_path):
ext = os.path.splitext(file_path)[1].lower()
if ext in self.extension_to_template:
template_name = self.extension_to_template[ext]
self.template_var.set(template_name)
self.on_template_selected(None)
def browse_file(self):
filename = filedialog.askopenfilename()
if filename:
self.file_path.set(filename)
self.select_template_by_extension(filename)
def parse_header(self):
try:
# 获取文件路径
file_path = self.file_path.get()
if not file_path:
self.result_tree.delete(*self.result_tree.get_children())
return
# 获取解析格式
format_text = self.format_text.get(1.0, tk.END).strip()
if not format_text:
self.result_tree.delete(*self.result_tree.get_children())
return
# 计算需要读取的总字节数
total_bytes = 0
format_list = []
for line in format_text.split('\n'):
if not line.strip():
continue
parts = line.strip().split(',')
type_name = parts[0].strip()
size = int(parts[1].strip())
field_name = parts[2].strip() if len(parts) > 2 else ""
total_bytes += size
format_list.append((type_name, size, field_name))
# 读取文件头
with open(file_path, 'rb') as f:
header_data = f.read(total_bytes)
# 解析并显示结果
self.result_tree.delete(*self.result_tree.get_children())
offset = 0
for type_name, size, field_name in format_list:
data = header_data[offset:offset+size]
hex_data = ''.join([f'{b:02X}' for b in data])
if type_name == 'byte':
result = ' '.join([f'{b:02X}' for b in data])
elif type_name == 'num':
if size == 4:
result = str(struct.unpack('<I', data)[0]) # 小端模式
elif size == 2:
result = str(struct.unpack('<H', data)[0]) # 小端模式
elif size == 1:
result = str(data[0])
else:
result = f"不支持的数值大小: {size}"
elif type_name == 'char':
result = data.decode('ascii', errors='replace')
else:
result = f"不支持的类型: {type_name}"
field_display = field_name if field_name else ""
self.result_tree.insert('', 'end', values=(field_display, f"{type_name}[{size}]", result, hex_data))
offset += size
except Exception as e:
self.result_tree.delete(*self.result_tree.get_children())
self.result_tree.insert('', 'end', values=('错误', '', str(e), ''))
if __name__ == "__main__":
root = tk.Tk()
app = FileHeaderParser(root)
root.mainloop()
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Python
1
https://gitee.com/firexmoon/file_header_parser.git
git@gitee.com:firexmoon/file_header_parser.git
firexmoon
file_header_parser
file_header_parser
master

搜索帮助