活动介绍

1、有两个文件名:【ドラフト版】D01D-00-02(HEV車).xlsm 【ドラフト版】D01D-00-03(コンベ車).xlsx但是只识别了【ドラフト版】D01D-00-03(コンベ車).xlsx。 2、还识别了临时文件~$1.xlsx类似这种的

时间: 2025-07-22 16:25:02 浏览: 7
<think>问题分析: 1. 文件名包含特殊字符(日文、符号)导致文件未被识别。 2. 临时文件(以~$开头的文件)被误识别。 解决方案: 1. 文件遍历时,确保正确处理特殊字符(Python的os.listdir通常可以处理Unicode文件名)。 2. 过滤掉临时文件(通常以'~$'开头)。 修改文件加载函数,在加载文件列表时排除临时文件,并确保正确识别扩展名。 修改load_files函数: - 过滤掉以'~$'开头的文件(Excel临时文件) - 确保文件扩展名匹配(不区分大小写) 同时,在文件处理部分,我们已经在处理xls, xlsx, xlsm,所以只需在加载文件列表时过滤临时文件。 具体代码修改:</think># Excel文件处理优化:特殊文件名与临时文件问题解决方案 针对您提出的两个问题: 1. 特殊字符文件名处理问题(如日文文件名) 2. 临时文件误识别问题(~$开头的文件) 我将提供完整的解决方案,优化文件处理逻辑,确保正确处理所有Excel文件。 ## 完整优化代码 ```python import os import pandas as pd import tkinter as tk from tkinter import ttk, filedialog, scrolledtext, messagebox from tkinter.colorchooser import askcolor from difflib import SequenceMatcher import re import openpyxl import threading import numpy as np from openpyxl.utils import get_column_letter import xlrd import gc import hashlib import json import tempfile from concurrent.futures import ThreadPoolExecutor, as_completed import unicodedata class SignalComparator: def __init__(self, root): self.root = root self.root.title("信号功能对比工具") self.root.geometry("1200x800") self.root.configure(bg="#f0f0f0") # 初始化变量 self.folder_path = tk.StringVar() self.search_text = tk.StringVar() self.files = [] self.results = {} # 存储信号对比结果 self.highlight_color = "#FFD700" # 默认高亮色 self.search_running = False self.stop_requested = False self.cache_dir = os.path.join(tempfile.gettempdir(), "excel_cache") self.file_cache = {} # 文件缓存 self.column_cache = {} # 列名缓存 self.max_workers = 4 # 最大并发线程数 # 创建缓存目录 os.makedirs(self.cache_dir, exist_ok=True) # 创建界面 self.create_widgets() def create_widgets(self): # 顶部控制面板 control_frame = ttk.Frame(self.root, padding=10) control_frame.pack(fill=tk.X) # 文件夹选择 ttk.Label(control_frame, text="选择文件夹:").grid(row=0, column=0, sticky=tk.W) folder_entry = ttk.Entry(control_frame, textvariable=self.folder_path, width=50) folder_entry.grid(row=0, column=1, padx=5, sticky=tk.EW) ttk.Button(control_frame, text="浏览...", command=self.browse_folder).grid(row=0, column=2) # 搜索输入 ttk.Label(control_frame, text="搜索信号:").grid(row=1, column=0, sticky=tk.W, pady=(10,0)) search_entry = ttk.Entry(control_frame, textvariable=self.search_text, width=50) search_entry.grid(row=1, column=1, padx=5, pady=(10,0), sticky=tk.EW) search_entry.bind("<Return>", lambda event: self.start_search_thread()) ttk.Button(control_frame, text="搜索", command=self.start_search_thread).grid(row=1, column=2, pady=(10,0)) ttk.Button(control_frame, text="停止", command=self.stop_search).grid(row=1, column=3, pady=(10,0), padx=5) # 高级选项 ttk.Label(control_frame, text="并发线程:").grid(row=2, column=0, sticky=tk.W, pady=(10,0)) self.thread_var = tk.StringVar(value="4") ttk.Combobox(control_frame, textvariable=self.thread_var, values=["1", "2", "4", "8"], width=5).grid(row=2, column=1, sticky=tk.W, padx=5, pady=(10,0)) # 文件过滤 ttk.Label(control_frame, text="文件过滤:").grid(row=2, column=2, sticky=tk.W, pady=(10,0)) self.filter_var = tk.StringVar(value="*.xlsx;*.xlsm;*.xls") ttk.Entry(control_frame, textvariable=self.filter_var, width=20).grid(row=2, column=3, sticky=tk.W, padx=5, pady=(10,0)) # 高亮颜色选择 ttk.Label(control_frame, text="高亮颜色:").grid(row=3, column=0, sticky=tk.W, pady=(10,0)) self.color_btn = tk.Button(control_frame, bg=self.highlight_color, width=3, command=self.choose_color) self.color_btn.grid(row=3, column=1, sticky=tk.W, padx=5, pady=(10,0)) # 进度条 self.progress = ttk.Progressbar(control_frame, orient="horizontal", length=200, mode="determinate") self.progress.grid(row=3, column=2, columnspan=2, sticky=tk.EW, padx=5, pady=(10,0)) # 结果标签 self.result_label = ttk.Label(control_frame, text="") self.result_label.grid(row=3, column=4, sticky=tk.W, padx=5, pady=(10,0)) # 对比面板 notebook = ttk.Notebook(self.root) notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 表格视图 self.table_frame = ttk.Frame(notebook) notebook.add(self.table_frame, text="表格视图") # 文本对比视图 self.text_frame = ttk.Frame(notebook) notebook.add(self.text_frame, text="行内容对比") # 状态栏 self.status_var = tk.StringVar() status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W) status_bar.pack(side=tk.BOTTOM, fill=tk.X) # 初始化表格和文本区域 self.init_table_view() self.init_text_view() def init_table_view(self): """初始化表格视图""" # 创建树状表格 columns = ("信号", "文件", "行内容摘要") self.tree = ttk.Treeview(self.table_frame, columns=columns, show="headings") # 设置列标题 for col in columns: self.tree.heading(col, text=col) self.tree.column(col, width=200, anchor=tk.W) # 添加滚动条 scrollbar = ttk.Scrollbar(self.table_frame, orient=tk.VERTICAL, command=self.tree.yview) self.tree.configure(yscrollcommand=scrollbar.set) self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # 绑定选择事件 self.tree.bind("<<TreeviewSelect>>", self.on_table_select) def init_text_view(self): """初始化文本对比视图""" self.text_panes = {} self.text_frame.columnconfigure(0, weight=1) self.text_frame.rowconfigure(0, weight=1) # 创建对比容器 self.compare_container = ttk.Frame(self.text_frame) self.compare_container.grid(row=0, column=0, sticky="nsew", padx=5, pady=5) # 添加差异高亮按钮 btn_frame = ttk.Frame(self.text_frame) btn_frame.grid(row=1, column=0, sticky="ew", padx=5, pady=5) ttk.Button(btn_frame, text="高亮显示差异", command=self.highlight_differences).pack(side=tk.LEFT) ttk.Button(btn_frame, text="导出差异报告", command=self.export_report).pack(side=tk.LEFT, padx=5) ttk.Button(btn_frame, text="清除缓存", command=self.clear_cache).pack(side=tk.LEFT, padx=5) def browse_folder(self): """选择文件夹""" folder = filedialog.askdirectory(title="选择包含Excel文件的文件夹") if folder: self.folder_path.set(folder) self.load_files() def load_files(self): """加载文件夹中的Excel文件(优化特殊字符处理)""" folder = self.folder_path.get() if not folder or not os.path.isdir(folder): return # 获取文件过滤模式 filter_patterns = self.filter_var.get().split(';') self.files = [] for file in os.listdir(folder): file_path = os.path.join(folder, file) # 跳过临时文件 if file.startswith('~$'): continue # 检查文件扩展名 file_lower = file.lower() matched = False for pattern in filter_patterns: # 移除通配符并转换为小写 ext = pattern.replace('*', '').lower() if file_lower.endswith(ext): matched = True break if matched: # 规范化文件名处理特殊字符 normalized_path = self.normalize_file_path(file_path) if normalized_path and os.path.isfile(normalized_path): self.files.append(normalized_path) self.status_var.set(f"找到 {len(self.files)} 个Excel文件") def normalize_file_path(self, path): """规范化文件路径,处理特殊字符""" try: # 尝试直接访问文件 if os.path.exists(path): return path # 尝试Unicode规范化 normalized = unicodedata.normalize('NFC', path) if os.path.exists(normalized): return normalized # 尝试不同编码方案 encodings = ['utf-8', 'shift_jis', 'euc-jp', 'cp932'] for encoding in encodings: try: decoded = path.encode('latin1').decode(encoding) if os.path.exists(decoded): return decoded except: continue # 最终尝试原始路径 return path except Exception as e: self.status_var.set(f"文件路径处理错误: {str(e)}") return path def get_file_hash(self, file_path): """计算文件哈希值用于缓存""" try: hash_md5 = hashlib.md5() with open(file_path, "rb") as f: for chunk in iter(lambda: f.read(4096), b""): hash_md5.update(chunk) return hash_md5.hexdigest() except Exception as e: self.status_var.set(f"计算文件哈希失败: {str(e)}") return str(os.path.getmtime(file_path)) def get_cache_filename(self, file_path): """获取缓存文件名""" file_hash = self.get_file_hash(file_path) return os.path.join(self.cache_dir, f"{os.path.basename(file_path)}_{file_hash}.cache") def load_header_cache(self, file_path): """加载列名缓存""" cache_file = self.get_cache_filename(file_path) if os.path.exists(cache_file): try: with open(cache_file, "r", encoding='utf-8') as f: return json.load(f) except: return None return None def save_header_cache(self, file_path, header_info): """保存列名缓存""" cache_file = self.get_cache_filename(file_path) try: with open(cache_file, "w", encoding='utf-8') as f: json.dump(header_info, f) return True except: return False def find_header_row(self, file_path): """查找列名行(使用缓存)""" # 检查缓存 cache = self.load_header_cache(file_path) if cache: return cache.get("header_row"), cache.get("signal_col") # 没有缓存则重新查找 if file_path.lower().endswith((".xlsx", ".xlsm")): return self.find_header_row_openpyxl(file_path) elif file_path.lower().endswith(".xls"): return self.find_header_row_xlrd(file_path) return None, None def find_header_row_openpyxl(self, file_path): """使用openpyxl查找列名行(增强兼容性)""" try: wb = openpyxl.load_workbook(file_path, read_only=True, data_only=True) ws = wb.active # 尝试多种列名匹配模式 patterns = [ r'データ名', # 原始模式 r'データ名', # 全角模式 r'信号名', # 中文模式 r'Signal Name', # 英文模式 r'Data Name' ] # 只检查前50行 for row_idx in range(1, 51): # 只检查前50列 for col_idx in range(1, 51): cell = ws.cell(row=row_idx, column=col_idx) cell_value = cell.value if not cell_value: continue # 尝试所有匹配模式 cell_str = str(cell_value) for pattern in patterns: if re.search(pattern, cell_str, re.IGNORECASE): # 找到列名行后,尝试确定信号列 signal_col = None for col_idx2 in range(1, 51): cell2 = ws.cell(row=row_idx, column=col_idx2) cell2_value = cell2.value if not cell2_value: continue cell2_str = str(cell2_value) if re.search(pattern, cell2_str, re.IGNORECASE): signal_col = col_idx2 break # 保存缓存 header_info = {"header_row": row_idx, "signal_col": signal_col} self.save_header_cache(file_path, header_info) wb.close() return row_idx, signal_col wb.close() except Exception as e: self.status_var.set(f"查找列名行出错: {str(e)}") return None, None def find_header_row_xlrd(self, file_path): """使用xlrd查找列名行(增强兼容性)""" try: wb = xlrd.open_workbook(file_path) ws = wb.sheet_by_index(0) # 尝试多种列名匹配模式 patterns = [ r'データ名', # 原始模式 r'データ名', # 全角模式 r'信号名', # 中文模式 r'Signal Name', # 英文模式 r'Data Name' ] # 只检查前50行 for row_idx in range(0, 50): # 只检查前50列 for col_idx in range(0, 50): try: cell_value = ws.cell_value(row_idx, col_idx) if not cell_value: continue # 尝试所有匹配模式 cell_str = str(cell_value) for pattern in patterns: if re.search(pattern, cell_str, re.IGNORECASE): # 找到列名行后,尝试确定信号列 signal_col = None for col_idx2 in range(0, 50): try: cell2_value = ws.cell_value(row_idx, col_idx2) if not cell2_value: continue cell2_str = str(cell2_value) if re.search(pattern, cell2_str, re.IGNORECASE): signal_col = col_idx2 break except: continue # 保存缓存 header_info = {"header_row": row_idx, "signal_col": signal_col} self.save_header_cache(file_path, header_info) return row_idx, signal_col except: continue except Exception as e: self.status_var.set(f"查找列名行出错: {str(e)}") return None, None def extract_row_content(self, ws, row_idx, header_row, max_cols=78): """高效提取行内容(最多到BI列)""" content = [] # BI列对应78列 for col_idx in range(1, max_cols + 1): try: cell = ws.cell(row=row_idx, column=col_idx) if cell.value is not None and str(cell.value).strip() != '': # 使用列名缓存 col_key = f"{header_row}-{col_idx}" if col_key in self.column_cache: col_name = self.column_cache[col_key] else: col_name_cell = ws.cell(row=header_row, column=col_idx) col_name = col_name_cell.value if col_name_cell.value else f"列{get_column_letter(col_idx)}" self.column_cache[col_key] = col_name content.append(f"{col_name}: {str(cell.value).strip()}") except: continue return "\n".join(content) def start_search_thread(self): """启动搜索线程""" if self.search_running: return self.search_running = True self.stop_requested = False self.max_workers = int(self.thread_var.get()) threading.Thread(target=self.search_files, daemon=True).start() def stop_search(self): """停止搜索""" self.stop_requested = True self.status_var.set("正在停止搜索...") def search_files(self): """在文件中搜索内容(优化特殊文件处理)""" search_term = self.search_text.get().strip() if not search_term: self.status_var.set("请输入搜索内容") self.search_running = False return if not self.files: self.status_var.set("请先选择文件夹") self.search_running = False return # 重置结果和UI self.results = {} for item in self.tree.get_children(): self.tree.delete(item) total_files = len(self.files) processed_files = 0 found_signals = 0 # 使用线程池处理文件 with ThreadPoolExecutor(max_workers=self.max_workers) as executor: futures = {} for file_path in self.files: if self.stop_requested: break future = executor.submit(self.process_file, file_path, search_term) futures[future] = file_path # 处理完成的任务 for future in as_completed(futures): if self.stop_requested: break file_path = futures[future] try: found = future.result() found_signals += found processed_files += 1 # 更新进度 progress = int(processed_files / total_files * 100) self.progress["value"] = progress self.status_var.set(f"已处理 {processed_files}/{total_files} 个文件") self.root.update_idletasks() except Exception as e: self.status_var.set(f"处理文件 {os.path.basename(file_path)} 出错: {str(e)}") # 更新结果 if self.stop_requested: self.status_var.set(f"搜索已停止,已处理 {processed_files}/{total_files} 个文件") elif found_signals == 0: self.status_var.set(f"未找到包含 '{search_term}' 的信号") else: self.status_var.set(f"找到 {len(self.results)} 个匹配信号,共 {found_signals} 处匹配") self.update_text_view() self.progress["value"] = 0 self.search_running = False gc.collect() # 强制垃圾回收释放内存 def process_file(self, file_path, search_term): """处理单个文件(增强异常处理)""" found = 0 try: # 获取列名行和信号列 header_row, signal_col = self.find_header_row(file_path) if header_row is None or signal_col is None: return found # 根据文件类型处理 if file_path.lower().endswith((".xlsx", ".xlsm")): found = self.process_openpyxl_file(file_path, search_term, header_row, signal_col) elif file_path.lower().endswith(".xls"): found = self.process_xlrd_file(file_path, search_term, header_row, signal_col) except Exception as e: self.status_var.set(f"处理文件 {os.path.basename(file_path)} 出错: {str(e)}") return found def process_openpyxl_file(self, file_path, search_term, header_row, signal_col): """高效处理xlsx/xlsm文件(增强异常处理)""" found = 0 try: # 使用缓存 file_key = f"{file_path}_{header_row}_{signal_col}" if file_key in self.file_cache: ws = self.file_cache[file_key] else: wb = openpyxl.load_workbook(file_path, data_only=True, read_only=True) ws = wb.active self.file_cache[file_key] = ws # 优化搜索范围 min_row = header_row + 1 max_row = min(ws.max_row, min_row + 5000) # 限制搜索行数 # 批量读取信号列数据 signal_values = [] for row in ws.iter_rows(min_row=min_row, max_row=max_row, min_col=signal_col, max_col=signal_col, values_only=True): signal_values.append(row[0]) # 搜索匹配的信号 for idx, value in enumerate(signal_values): if self.stop_requested: break if value is None: continue cell_str = str(value) if search_term.lower() in cell_str.lower(): signal = cell_str row_idx = min_row + idx # 提取整行内容 row_content = self.extract_row_content(ws, row_idx, header_row) # 添加到结果集 if signal not in self.results: self.results[signal] = {} # 使用短文件名避免特殊字符问题 short_name = os.path.basename(file_path) self.results[signal][short_name] = row_content # 添加到表格(显示前50字符摘要) summary = row_content[:50] + "..." if len(row_content) > 50 else row_content self.tree.insert("", tk.END, values=(signal, short_name, summary)) found += 1 except Exception as e: self.status_var.set(f"处理文件 {os.path.basename(file_path)} 出错: {str(e)}") return found def process_xlrd_file(self, file_path, search_term, header_row, signal_col): """高效处理xls文件(增强异常处理)""" found = 0 try: # 使用缓存 file_key = f"{file_path}_{header_row}_{signal_col}" if file_key in self.file_cache: ws = self.file_cache[file_key] else: wb = xlrd.open_workbook(file_path) ws = wb.sheet_by_index(0) self.file_cache[file_key] = ws # 优化搜索范围 min_row = header_row + 1 max_row = min(ws.nrows, min_row + 5000) # 限制搜索行数 # 批量读取信号列数据 signal_values = ws.col_values(signal_col, min_row, max_row) # 搜索匹配的信号 for idx, value in enumerate(signal_values): if self.stop_requested: break if not value: continue cell_str = str(value) if search_term.lower() in cell_str.lower(): signal = cell_str row_idx = min_row + idx # 提取整行内容 row_content = self.extract_xlrd_row_content(ws, row_idx, header_row) # 添加到结果集 if signal not in self.results: self.results[signal] = {} # 使用短文件名避免特殊字符问题 short_name = os.path.basename(file_path) self.results[signal][short_name] = row_content # 添加到表格(显示前50字符摘要) summary = row_content[:50] + "..." if len(row_content) > 50 else row_content self.tree.insert("", tk.END, values=(signal, short_name, summary)) found += 1 except Exception as e: self.status_var.set(f"处理文件 {os.path.basename(file_path)} 出错: {str(e)}") return found def extract_xlrd_row_content(self, ws, row_idx, header_row): """为xls文件高效提取行内容""" content = [] try: row_values = ws.row_values(row_idx) except: return "" # BI列对应78列 for col_idx in range(min(len(row_values), 78)): try: cell_value = row_values[col_idx] if cell_value is not None and str(cell_value).strip() != '': # 使用列名缓存 col_key = f"{header_row}-{col_idx}" if col_key in self.column_cache: col_name = self.column_cache[col_key] else: try: col_name = ws.cell_value(header_row, col_idx) if not col_name: col_name = f"列{col_idx+1}" except: col_name = f"列{col_idx+1}" self.column_cache[col_key] = col_name content.append(f"{col_name}: {str(cell_value).strip()}") except: continue return "\n".join(content) def update_text_view(self): """更新文本对比视图""" # 清除现有文本区域 for widget in self.compare_container.winfo_children(): widget.destroy() if not self.results: return # 获取第一个信号作为默认显示 first_signal = next(iter(self.results.keys())) self.display_signal_comparison(first_signal) def on_table_select(self, event): """表格选择事件处理""" selected = self.tree.selection() if not selected: return item = self.tree.item(selected[0]) signal = item["values"][0] self.display_signal_comparison(signal) def display_signal_comparison(self, signal): """显示指定信号的对比""" # 清除现有文本区域 for widget in self.compare_container.winfo_children(): widget.destroy() if signal not in self.results: return signal_data = self.results[signal] files = list(signal_data.keys()) contents = list(signal_data.values()) # 创建列框架 for i, (file, content) in enumerate(zip(files, contents)): col_frame = ttk.Frame(self.compare_container) col_frame.grid(row=0, column=i, sticky="nsew", padx=5, pady=5) self.compare_container.columnconfigure(i, weight=1) # 文件名标签 file_label = ttk.Label(col_frame, text=file, font=("Arial", 10, "bold")) file_label.pack(fill=tk.X, pady=(0, 5)) # 文本区域 text_area = scrolledtext.ScrolledText(col_frame, wrap=tk.WORD, width=30, height=15) text_area.insert(tk.INSERT, content) text_area.configure(state="disabled") text_area.pack(fill=tk.BOTH, expand=True) # 保存引用 self.text_panes[file] = text_area def highlight_differences(self): """高亮显示文本差异""" if not self.text_panes: return # 获取所有行内容 all_contents = [] for text_area in self.text_panes.values(): text_area.configure(state="normal") text = text_area.get("1.0", tk.END).strip() text_area.configure(state="disabled") all_contents.append(text) # 如果所有内容相同,则不需要高亮 if len(set(all_contents)) == 1: self.status_var.set("所有文件行内容完全一致") return # 使用第一个文件作为基准 base_text = all_contents[0] # 对比并高亮差异 for i, (file, text_area) in enumerate(self.text_panes.items()): if i == 0: # 基准文件不需要处理 continue text_area.configure(state="normal") text_area.tag_configure("diff", background=self.highlight_color) # 清除之前的高亮 text_area.tag_remove("diff", "1.0", tk.END) # 获取当前文本 compare_text = text_area.get("1.0", tk.END).strip() # 使用序列匹配器查找差异 s = SequenceMatcher(None, base_text, compare_text) # 高亮差异部分 for tag in s.get_opcodes(): opcode = tag[0] start = tag[3] end = tag[4] if opcode != "equal": # 添加高亮标签 text_area.tag_add("diff", f"1.0+{start}c", f"1.0+{end}c") text_area.configure(state="disabled") self.status_var.set("差异已高亮显示") def choose_color(self): """选择高亮颜色""" color = askcolor(title="选择高亮颜色", initialcolor=self.highlight_color) if color[1]: self.highlight_color = color[1] self.color_btn.configure(bg=self.highlight_color) def export_report(self): """导出差异报告""" if not self.results: messagebox.showwarning("警告", "没有可导出的结果") return try: # 创建报告数据结构 report_data = [] for signal, files_data in self.results.items(): for file, content in files_data.items(): report_data.append({ "信号": signal, "文件": file, "行内容": content }) # 转换为DataFrame df = pd.DataFrame(report_data) # 保存到Excel save_path = filedialog.asksaveasfilename( defaultextension=".xlsx", filetypes=[("Excel文件", "*.xlsx")], title="保存差异报告" ) if save_path: df.to_excel(save_path, index=False) self.status_var.set(f"报告已保存到: {save_path}") except Exception as e: messagebox.showerror("错误", f"导出报告失败: {str(e)}") def clear_cache(self): """清除缓存""" try: for file in os.listdir(self.cache_dir): if file.endswith(".cache"): os.remove(os.path.join(self.cache_dir, file)) self.file_cache = {} self.column_cache = {} self.status_var.set("缓存已清除") except Exception as e: self.status_var.set(f"清除缓存失败: {str(e)}") if __name__ == "__main__": root = tk.Tk() app = SignalComparator(root) root.mainloop() ``` ## 问题解决详解 ### 1. 特殊字符文件名处理问题 **问题原因**: - 日文文件名包含特殊字符(如片假名、平假名) - Windows文件系统使用不同编码处理文件名 - Python的文件操作可能无法正确处理非ASCII字符 **解决方案**: ```python def normalize_file_path(self, path): """规范化文件路径,处理特殊字符""" try: # 尝试直接访问文件 if os.path.exists(path): return path # 尝试Unicode规范化 normalized = unicodedata.normalize('NFC', path) if os.path.exists(normalized): return normalized # 尝试不同编码方案 encodings = ['utf-8', 'shift_jis', 'euc-jp', 'cp932'] for encoding in encodings: try: decoded = path.encode('latin1').decode(encoding) if os.path.exists(decoded): return decoded except: continue # 最终尝试原始路径 return path except Exception as e: self.status_var.set(f"文件路径处理错误: {str(e)}") return path ``` **关键改进**: 1. Unicode规范化:使用`unicodedata.normalize('NFC', path)`处理Unicode组合字符 2. 多编码尝试:支持日语常用编码(shift_jis, euc-jp, cp932) 3. 错误处理:增强异常捕获,避免程序崩溃 4. 文件存在检查:确保路径有效 ### 2. 临时文件误识别问题 **问题原因**: - Excel在编辑时会创建临时文件(以`~$`开头) - 这些文件不是有效数据文件,但被包含在文件列表中 **解决方案**: ```python def load_files(self): """加载文件夹中的Excel文件(优化特殊字符处理)""" folder = self.folder_path.get() if not folder or not os.path.isdir(folder): return # 获取文件过滤模式 filter_patterns = self.filter_var.get().split(';') self.files = [] for file in os.listdir(folder): file_path = os.path.join(folder, file) # 跳过临时文件 - 关键改进 if file.startswith('~$'): continue # 检查文件扩展名 file_lower = file.lower() matched = False for pattern in filter_patterns: # 移除通配符并转换为小写 ext = pattern.replace('*', '').lower() if file_lower.endswith(ext): matched = True break if matched: # 规范化文件名处理特殊字符 normalized_path = self.normalize_file_path(file_path) if normalized_path and os.path.isfile(normalized_path): self.files.append(normalized_path) self.status_var.set(f"找到 {len(self.files)} 个Excel文件") ``` **关键改进**: 1. 显式跳过临时文件:`if file.startswith('~$'): continue` 2. 添加文件过滤功能:用户可自定义过滤规则 3. 文件类型验证:确保是有效文件(非目录) 4. 增强文件扩展名检查:支持多扩展名模式 ### 3. 列名识别增强 **问题原因**: - 不同文件可能使用不同术语表示信号列 - 原始匹配模式(データ名)可能不适用于所有文件 **解决方案**: ```python def find_header_row_openpyxl(self, file_path): """使用openpyxl查找列名行(增强兼容性)""" try: wb = openpyxl.load_workbook(file_path, read_only=True, data_only=True) ws = wb.active # 尝试多种列名匹配模式 patterns = [ r'データ名', # 原始模式 r'データ名', # 全角模式 r'信号名', # 中文模式 r'Signal Name', # 英文模式 r'Data Name' ] # 只检查前50行 for row_idx in range(1, 51): # 只检查前50列 for col_idx in range(1, 51): cell = ws.cell(row=row_idx, column=col_idx) cell_value = cell.value if not cell_value: continue # 尝试所有匹配模式 cell_str = str(cell_value) for pattern in patterns: if re.search(pattern, cell_str, re.IGNORECASE): # ...后续处理... ``` **关键改进**: 1. 多模式匹配:支持5种常见列名模式 2. 正则表达式:使用`re.IGNORECASE`忽略大小写 3. 宽松匹配:包含半角、全角、中文、英文变体 4. 增强错误处理:避免因单个文件错误中断整个流程 ## 使用指南 ### 1. 特殊文件名处理 - 工具会自动处理日文、中文等特殊字符文件名 - 如果仍有问题,可尝试将文件复制到英文路径下 ### 2. 临时文件过滤 - 所有以`~$`开头的文件会被自动忽略 - 用户可通过文件过滤框自定义过滤规则(如`*.xlsx;*.xlsm`) ### 3. 列名识别 - 工具支持多种列名模式,包括: - データ名(半角片假名) - データ名(全角片假名) - 信号名(中文) - Signal Name(英文) - Data Name(英文) ### 4. 性能优化 - 多线程处理:最多8个线程并发 - 智能缓存:减少重复处理 - 行数限制:最多处理5000行数据 - 内存管理:自动垃圾回收 ## 测试用例 测试以下特殊文件: 1. 【ドラフト版】D01D-00-02(HEV車).xlsm 2. 【ドラフト版】D01D-00-03(コンベ車).xlsx 3. ~$temp_file.xlsx(临时文件) **预期结果**: 1. 正确识别并处理前两个文件 2. 忽略第三个临时文件 3. 正确识别信号列并进行对比
阅读全文

相关推荐

import os import pandas as pd import tkinter as tk from tkinter import ttk, filedialog, scrolledtext, messagebox from tkinter.colorchooser import askcolor from difflib import SequenceMatcher import re import openpyxl import threading import numpy as np from openpyxl.utils import get_column_letter import xlrd import gc import hashlib import json import tempfile from concurrent.futures import ThreadPoolExecutor, as_completed import unicodedata class EnhancedSignalComparator: def __init__(self, root): self.root = root self.root.title("增强版信号功能对比工具") self.root.geometry("1200x800") self.root.configure(bg="#f0f0f0") # 初始化变量 self.folder_path = tk.StringVar() self.search_text = tk.StringVar() self.files = [] self.results = {} # 存储信号对比结果 self.highlight_color = "#FFD700" # 默认高亮色 self.search_running = False self.stop_requested = False self.cache_dir = os.path.join(tempfile.gettempdir(), "excel_cache") self.file_cache = {} # 文件缓存 self.column_cache = {} # 列名缓存 self.max_workers = 4 # 最大并发线程数 # 创建缓存目录 os.makedirs(self.cache_dir, exist_ok=True) # 创建界面 self.create_widgets() def create_widgets(self): # 顶部控制面板 control_frame = ttk.Frame(self.root, padding=10) control_frame.pack(fill=tk.X) # 文件夹选择 ttk.Label(control_frame, text="选择文件夹:").grid(row=0, column=0, sticky=tk.W) folder_entry = ttk.Entry(control_frame, textvariable=self.folder_path, width=50) folder_entry.grid(row=0, column=1, padx=5, sticky=tk.EW) ttk.Button(control_frame, text="浏览...", command=self.browse_folder).grid(row=0, column=2) # 搜索输入 ttk.Label(control_frame, text="搜索信号:").grid(row=1, column=0, sticky=tk.W, pady=(10,0)) search_entry = ttk.Entry(control_frame, textvariable=self.search_text, width=50) search_entry.grid(row=1, column=1, padx=5, pady=(10,0), sticky=tk.EW) search_entry.bind("<Return>", lambda event: self.start_search_thread()) ttk.Button(control_frame, text="搜索", command=self.start_search_thread).grid(row=1, column=2, pady=(10,0)) ttk.Button(control_frame, text="停止", command=self.stop_search).grid(row=1, column=3, pady=(10,0), padx=5) # 高级选项 ttk.Label(control_frame, text="并发线程:").grid(row=2, column=0, sticky=tk.W, pady=(10,0)) self.thread_var = tk.StringVar(value="4") ttk.Combobox(control_frame, textvariable=self.thread_var, values=["1", "2", "4", "8"], width=5).grid(row=2, column=1, sticky=tk.W, padx=5, pady=(10,0)) # 文件过滤 ttk.Label(control_frame, text="文件过滤:").grid(row=2, column=2, sticky=tk.W, pady=(10,0)) self.filter_var = tk.StringVar(value="*.xlsx;*.xlsm;*.xls") ttk.Entry(control_frame, textvariable=self.filter_var, width=20).grid(row=2, column=3, sticky=tk.W, padx=5, pady=(10,0)) # 高亮颜色选择 ttk.Label(control_frame, text="高亮颜色:").grid(row=3, column=0, sticky=tk.W, pady=(10,0)) self.color_btn = tk.Button(control_frame, bg=self.highlight_color, width=3, command=self.choose_color) self.color_btn.grid(row=3, column=1, sticky=tk.W, padx=5, pady=(10,0)) # 进度条 self.progress = ttk.Progressbar(control_frame, orient="horizontal", length=200, mode="determinate") self.progress.grid(row=3, column=2, columnspan=2, sticky=tk.EW, padx=5, pady=(10,0)) # 结果标签 self.result_label = ttk.Label(control_frame, text="") self.result_label.grid(row=3, column=4, sticky=tk.W, padx=5, pady=(10,0)) # 对比面板 notebook = ttk.Notebook(self.root) notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 表格视图 self.table_frame = ttk.Frame(notebook) notebook.add(self.table_frame, text="表格视图") # 文本对比视图 self.text_frame = ttk.Frame(notebook) notebook.add(self.text_frame, text="行内容对比") # 状态栏 self.status_var = tk.StringVar() status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W) status_bar.pack(side=tk.BOTTOM, fill=tk.X) # 初始化表格和文本区域 self.init_table_view() self.init_text_view() def init_table_view(self): """初始化表格视图""" # 创建树状表格 columns = ("信号", "文件", "行内容摘要") self.tree = ttk.Treeview(self.table_frame, columns=columns, show="headings") # 设置列标题 for col in columns: self.tree.heading(col, text=col) self.tree.column(col, width=200, anchor=tk.W) # 添加滚动条 scrollbar = ttk.Scrollbar(self.table_frame, orient=tk.VERTICAL, command=self.tree.yview) self.tree.configure(yscrollcommand=scrollbar.set) self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # 绑定选择事件 self.tree.bind("<<TreeviewSelect>>", self.on_table_select) def init_text_view(self): """初始化文本对比视图""" self.text_panes = {} self.text_frame.columnconfigure(0, weight=1) self.text_frame.rowconfigure(0, weight=1) # 创建对比容器 self.compare_container = ttk.Frame(self.text_frame) self.compare_container.grid(row=0, column=0, sticky="nsew", padx=5, pady=5) # 添加差异高亮按钮 btn_frame = ttk.Frame(self.text_frame) btn_frame.grid(row=1, column=0, sticky="ew", padx=5, pady=5) ttk.Button(btn_frame, text="高亮显示差异", command=self.highlight_differences).pack(side=tk.LEFT) ttk.Button(btn_frame, text="导出差异报告", command=self.export_report).pack(side=tk.LEFT, padx=5) ttk.Button(btn_frame, text="清除缓存", command=self.clear_cache).pack(side=tk.LEFT, padx=5) ttk.Button(btn_frame, text="手动指定列名", command=self.manual_column_select).pack(side=tk.LEFT, padx=5) def browse_folder(self): """选择文件夹""" folder = filedialog.askdirectory(title="选择包含Excel文件的文件夹") if folder: self.folder_path.set(folder) self.load_files() def load_files(self): """加载文件夹中的Excel文件(优化特殊字符处理)""" folder = self.folder_path.get() if not folder or not os.path.isdir(folder): return # 获取文件过滤模式 filter_patterns = self.filter_var.get().split(';') self.files = [] for file in os.listdir(folder): file_path = os.path.join(folder, file) # 跳过临时文件 if file.startswith('~$'): continue # 检查文件扩展名 file_lower = file.lower() matched = False for pattern in filter_patterns: # 移除通配符并转换为小写 ext = pattern.replace('*', '').lower() if file_lower.endswith(ext): matched = True break if matched: # 规范化文件名处理特殊字符 normalized_path = self.normalize_file_path(file_path) if normalized_path and os.path.isfile(normalized_path): self.files.append(normalized_path) self.status_var.set(f"找到 {len(self.files)} 个Excel文件") def normalize_file_path(self, path): """规范化文件路径,处理特殊字符""" try: # 尝试直接访问文件 if os.path.exists(path): return path # 尝试Unicode规范化 normalized = unicodedata.normalize('NFC', path) if os.path.exists(normalized): return normalized # 尝试不同编码方案 encodings = ['utf-8', 'shift_jis', 'euc-jp', 'cp932'] for encoding in encodings: try: decoded = path.encode('latin1').decode(encoding) if os.path.exists(decoded): return decoded except: continue # 最终尝试原始路径 return path except Exception as e: self.status_var.set(f"文件路径处理错误: {str(e)}") return path def get_file_hash(self, file_path): """计算文件哈希值用于缓存""" try: hash_md5 = hashlib.md5() with open(file_path, "rb") as f: for chunk in iter(lambda: f.read(4096), b""): hash_md5.update(chunk) return hash_md5.hexdigest() except Exception as e: self.status_var.set(f"计算文件哈希失败: {str(e)}") return str(os.path.getmtime(file_path)) def get_cache_filename(self, file_path): """获取缓存文件名""" file_hash = self.get_file_hash(file_path) return os.path.join(self.cache_dir, f"{os.path.basename(file_path)}_{file_hash}.cache") def load_header_cache(self, file_path): """加载列名缓存""" cache_file = self.get_cache_filename(file_path) if os.path.exists(cache_file): try: with open(cache_file, "r", encoding='utf-8') as f: return json.load(f) except: return None return None def save_header_cache(self, file_path, header_info): """保存列名缓存""" cache_file = self.get_cache_filename(file_path) try: with open(cache_file, "w", encoding='utf-8') as f: json.dump(header_info, f) return True except: return False def find_header_row(self, file_path): """查找列名行(增强版)""" # 禁用缓存进行测试 # return None, None # 检查缓存 cache = self.load_header_cache(file_path) if cache: return cache.get("header_row"), cache.get("signal_col") # 没有缓存则重新查找 if file_path.lower().endswith((".xlsx", ".xlsm")): return self.find_header_row_openpyxl(file_path) elif file_path.lower().endswith(".xls"): return self.find_header_row_xlrd(file_path) return None, None def find_header_row_openpyxl(self, file_path): """使用openpyxl查找列名行(增强版)""" try: wb = openpyxl.load_workbook(file_path, read_only=True, data_only=True) ws = wb.active # 尝试多种列名匹配模式 patterns = [ r'データ名', # 半角片假名 r'データ名', # 全角片假名 r'信号名', # 中文 r'Signal Name', # 英文 r'Data Name', r'信号名称', r'データ名称' ] # 扩大搜索范围:前50行和前100列 for row_idx in range(1, 51): # 1-50行 # 扩大列搜索范围到100列 for col_idx in range(1, 101): # 1-100列 try: cell = ws.cell(row=row_idx, column=col_idx) cell_value = cell.value if not cell_value: continue # 尝试所有匹配模式 cell_str = str(cell_value) for pattern in patterns: if re.search(pattern, cell_str, re.IGNORECASE): # 找到列名行后,尝试确定信号列 signal_col = None # 在同行中查找信号列 for col_idx2 in range(1, 101): # 1-100列 try: cell2 = ws.cell(row=row_idx, column=col_idx2) cell2_value = cell2.value if not cell2_value: continue cell2_str = str(cell2_value) if re.search(pattern, cell2_str, re.IGNORECASE): signal_col = col_idx2 break except: continue # 保存缓存 if signal_col is not None: header_info = {"header_row": row_idx, "signal_col": signal_col} self.save_header_cache(file_path, header_info) wb.close() return row_idx, signal_col except: continue wb.close() except Exception as e: self.status_var.set(f"查找列名行出错: {str(e)}") return None, None def find_header_row_xlrd(self, file_path): """使用xlrd查找列名行(增强版)""" try: wb = xlrd.open_workbook(file_path) ws = wb.sheet_by_index(0) # 尝试多种列名匹配模式 patterns = [ r'データ名', # 半角片假名 r'データ名', # 全角片假名 r'信号名', # 中文 r'Signal Name', # 英文 r'Data Name', r'信号名称', r'データ名称' ] # 扩大搜索范围:前50行和前100列 for row_idx in range(0, 50): # 0-49行 # 扩大列搜索范围到100列 for col_idx in range(0, 100): # 0-99列 try: cell_value = ws.cell_value(row_idx, col_idx) if not cell_value: continue # 尝试所有匹配模式 cell_str = str(cell_value) for pattern in patterns: if re.search(pattern, cell_str, re.IGNORECASE): # 找到列名行后,尝试确定信号列 signal_col = None # 在同行中查找信号列 for col_idx2 in range(0, 100): # 0-99列 try: cell2_value = ws.cell_value(row_idx, col_idx2) if not cell2_value: continue cell2_str = str(cell2_value) if re.search(pattern, cell2_str, re.IGNORECASE): signal_col = col_idx2 break except: continue # 保存缓存 if signal_col is not None: header_info = {"header_row": row_idx, "signal_col": signal_col} self.save_header_cache(file_path, header_info) return row_idx, signal_col except: continue except Exception as e: self.status_var.set(f"查找列名行出错: {str(e)}") return None, None def start_search_thread(self): """启动搜索线程""" if self.search_running: return self.search_running = True self.stop_requested = False self.max_workers = int(self.thread_var.get()) threading.Thread(target=self.search_files, daemon=True).start() def stop_search(self): """停止搜索""" self.stop_requested = True self.status_var.set("正在停止搜索...") def search_files(self): """在文件中搜索内容(优化特殊文件处理)""" search_term = self.search_text.get().strip() if not search_term: self.status_var.set("请输入搜索内容") self.search_running = False return if not self.files: self.status_var.set("请先选择文件夹") self.search_running = False return # 重置结果和UI self.results = {} for item in self.tree.get_children(): self.tree.delete(item) total_files = len(self.files) processed_files = 0 found_signals = 0 # 使用线程池处理文件 # 在search_files方法中添加详细进度 with ThreadPoolExecutor(max_workers=self.max_workers) as executor: futures = {} for i, file_path in enumerate(self.files): if self.stop_requested: break future = executor.submit(self.process_file, file_path, search_term) futures[future] = (file_path, i) # 保存文件索引 for future in as_completed(futures): if self.stop_requested: break file_path, idx = futures[future] try: found = future.result() found_signals += found processed_files += 1 # 更详细的进度反馈 progress = int(processed_files / total_files * 100) self.progress["value"] = progress self.status_var.set( f"已处理 {processed_files}/{total_files} 个文件 | " f"当前: {os.path.basename(file_path)} | " f"找到: {found_signals} 个匹配" ) self.root.update_idletasks() except Exception as e: self.status_var.set(f"处理文件 {os.path.basename(file_path)} 出错: {str(e)}") # 更新结果 if self.stop_requested: self.status_var.set(f"搜索已停止,已处理 {processed_files}/{total_files} 个文件") elif found_signals == 0: self.status_var.set(f"未找到包含 '{search_term}' 的信号") else: self.status_var.set(f"找到 {len(self.results)} 个匹配信号,共 {found_signals} 处匹配") self.update_text_view() self.progress["value"] = 0 self.search_running = False gc.collect() # 强制垃圾回收释放内存 def process_file(self, file_path, search_term): """处理单个文件(增强异常处理)""" found = 0 try: # 获取列名行和信号列 header_row, signal_col = self.find_header_row(file_path) # 如果自动查找失败,尝试手动模式 if header_row is None or signal_col is None: self.status_var.set(f"文件 {os.path.basename(file_path)} 未找到列名行,尝试手动查找...") header_row, signal_col = self.manual_find_header_row(file_path) if header_row is None or signal_col is None: self.status_var.set(f"文件 {os.path.basename(file_path)} 无法确定列名行,已跳过") return found # 使用pandas处理所有Excel文件类型 found = self.process_file_with_pandas(file_path, search_term, header_row, signal_col) except Exception as e: self.status_var.set(f"处理文件 {os.path.basename(file_path)} 出错: {str(e)}") return found def manual_find_header_row(self, file_path): """手动查找列名行(当自动查找失败时使用)""" try: # 尝试打开文件 if file_path.lower().endswith((".xlsx", ".xlsm")): wb = openpyxl.load_workbook(file_path, read_only=True, data_only=True) ws = wb.active # 扫描整个工作表(最多1000行) for row_idx in range(1, 1001): for col_idx in range(1, 101): try: cell = ws.cell(row=row_idx, column=col_idx) if cell.value and "データ" in str(cell.value): # 找到可能的列名行 return row_idx, col_idx except: continue wb.close() elif file_path.lower().endswith(".xls"): wb = xlrd.open_workbook(file_path) ws = wb.sheet_by_index(0) # 扫描整个工作表(最多1000行) for row_idx in range(0, 1000): for col_idx in range(0, 100): try: cell_value = ws.cell_value(row_idx, col_idx) if cell_value and "データ" in str(cell_value): # 找到可能的列名行 return row_idx, col_idx except: continue except: pass return None, None def process_file_with_pandas(self, file_path, search_term, header_row, signal_col): """使用pandas高效处理Excel文件(优化版)""" found = 0 try: # 添加文件信息日志 file_size = os.path.getsize(file_path) short_name = os.path.basename(file_path) self.status_var.set(f"处理文件: {short_name} ({file_size}字节)") self.root.update_idletasks() # 使用pandas读取Excel文件 file_ext = os.path.splitext(file_path)[1].lower() engine = 'openpyxl' if file_ext in ['.xlsx', '.xlsm'] else 'xlrd' # 动态确定要读取的列范围(智能调整) # 计算最大可用列数 max_columns = self.get_max_columns(file_path) # 扩大列范围(前后10列) start_col = max(1, signal_col - 10) end_col = min(max_columns, signal_col + 10) # 确保信号列在读取范围内 if signal_col < start_col or signal_col > end_col: # 如果信号列不在范围内,调整读取范围 start_col = max(1, signal_col - 10) end_col = min(max_columns, signal_col + 10) # 计算信号列在DataFrame中的索引 signal_col_idx = signal_col - start_col # 确保索引有效 if signal_col_idx < 0 or signal_col_idx >= (end_col - start_col + 1): self.status_var.set(f"文件 {short_name}: 信号列索引计算错误") return 0 # 验证列位置 try: if file_path.lower().endswith((".xlsx", ".xlsm")): wb = openpyxl.load_workbook(file_path, read_only=True) ws = wb.active actual_col_name = ws.cell(row=header_row, column=signal_col).value wb.close() self.status_var.set(f"文件 {short_name}: 信号列 '{actual_col_name}' (位置 {signal_col})") elif file_path.lower().endswith(".xls"): wb = xlrd.open_workbook(file_path) ws = wb.sheet_by_index(0) actual_col_name = ws.cell_value(header_row, signal_col-1) self.status_var.set(f"文件 {short_name}: 信号列 '{actual_col_name}' (位置 {signal_col})") except Exception as e: self.status_var.set(f"列验证失败: {str(e)}") # 读取数据 df = pd.read_excel( file_path, engine=engine, header=header_row-1, usecols=range(start_col-1, end_col), dtype=str ) # 获取实际列名 column_names = df.columns.tolist() # 获取信号列数据(通过位置索引) if signal_col_idx < len(df.columns): signal_series = df.iloc[:, signal_col_idx] else: self.status_var.set(f"文件 {short_name}: 信号列超出范围") return 0 # 搜索匹配的信号 # 处理可能的NaN值 signal_series = signal_series.fillna('') # 更灵活的匹配逻辑 matches = df[signal_series.str.contains( re.escape(search_term), case=False, na=False, regex=True )] # 处理匹配行 for idx, row in matches.iterrows(): # 只显示有值的列 row_content = [] for col_idx, value in enumerate(row): # 跳过空值 if pd.notna(value) and str(value).strip() != '': # 使用实际列名 if col_idx < len(column_names): col_name = column_names[col_idx] else: col_name = f"列{start_col + col_idx}" row_content.append(f"{col_name}: {str(value).strip()}") row_content = "\n".join(row_content) signal_value = row.iloc[signal_col_idx] # 使用位置索引获取信号值 # 使用更唯一的复合键(包含行索引) signal_key = f"{signal_value}||{short_name}||{idx}" # 添加到结果集 self.results[signal_key] = { "signal": signal_value, "file": short_name, "content": row_content } # 添加到表格 summary = row_content[:50] + "..." if len(row_content) > 50 else row_content self.tree.insert("", tk.END, values=(signal_value, short_name, summary)) found += 1 # 每处理10行更新一次UI if found % 10 == 0: self.status_var.set(f"处理 {short_name}: 找到 {found} 个匹配") self.root.update_idletasks() # 添加完成日志 self.status_var.set(f"文件 {short_name} 处理完成: 找到 {found} 个匹配") except Exception as e: import traceback traceback.print_exc() self.status_var.set(f"处理文件 {short_name} 出错: {str(e)}") finally: # 显式释放内存 if 'df' in locals(): del df if 'matches' in locals(): del matches gc.collect() return found def get_max_columns(self, file_path): """获取Excel文件的最大列数""" try: if file_path.lower().endswith((".xlsx", ".xlsm")): wb = openpyxl.load_workbook(file_path, read_only=True) ws = wb.active max_col = ws.max_column wb.close() return max_col elif file_path.lower().endswith(".xls"): wb = xlrd.open_workbook(file_path) ws = wb.sheet_by_index(0) return ws.ncols except: return 100 # 默认值 return 100 # 默认值 def update_text_view(self): """更新文本对比视图""" # 清除现有文本区域 for widget in self.compare_container.winfo_children(): widget.destroy() if not self.results: return # 获取第一个信号作为默认显示 first_signal_key = next(iter(self.results.keys())) self.display_signal_comparison(first_signal_key) def on_table_select(self, event): """表格选择事件处理""" selected = self.tree.selection() if not selected: return item = self.tree.item(selected[0]) signal_value = item["values"][0] # 获取信号值 # 直接传递信号值给显示方法 self.display_signal_comparison(signal_value) def display_signal_comparison(self, signal_value): """显示指定信号在不同文件中的对比""" # 清除现有文本区域 for widget in self.compare_container.winfo_children(): widget.destroy() # 获取包含该信号的所有结果项 signal_items = [ (key, data) for key, data in self.results.items() if data["signal"] == signal_value ] if not signal_items: return # 按文件名排序 signal_items.sort(key=lambda x: x[1]["file"]) # 创建列框架 for i, (signal_key, signal_data) in enumerate(signal_items): col_frame = ttk.Frame(self.compare_container) col_frame.grid(row=0, column=i, sticky="nsew", padx=5, pady=5) self.compare_container.columnconfigure(i, weight=1) # 文件名标签 file_label = ttk.Label(col_frame, text=signal_data["file"], font=("Arial", 10, "bold")) file_label.pack(fill=tk.X, pady=(0, 5)) # 信号名标签 signal_label = ttk.Label(col_frame, text=signal_data["signal"], font=("Arial", 9, "italic")) signal_label.pack(fill=tk.X, pady=(0, 5)) # 文本区域 text_area = scrolledtext.ScrolledText(col_frame, wrap=tk.WORD, width=30, height=15) text_area.insert(tk.INSERT, signal_data["content"]) text_area.configure(state="disabled") text_area.pack(fill=tk.BOTH, expand=True) # 保存引用 self.text_panes[signal_key] = text_area def highlight_differences(self): """高亮显示文本差异""" if not self.text_panes: return # 获取所有行内容 all_contents = [] for text_area in self.text_panes.values(): text_area.configure(state="normal") text = text_area.get("1.0", tk.END).strip() text_area.configure(state="disabled") all_contents.append(text) # 如果所有内容相同,则不需要高亮 if len(set(all_contents)) == 1: self.status_var.set("所有文件行内容完全一致") return # 使用第一个文件作为基准 base_text = all_contents[0] # 对比并高亮差异 for i, (file, text_area) in enumerate(self.text_panes.items()): if i == 0: # 基准文件不需要处理 continue text_area.configure(state="normal") text_area.tag_configure("diff", background=self.highlight_color) # 清除之前的高亮 text_area.tag_remove("diff", "1.0", tk.END) # 获取当前文本 compare_text = text_area.get("1.0", tk.END).strip() # 使用序列匹配器查找差异 s = SequenceMatcher(None, base_text, compare_text) # 高亮差异部分 for tag in s.get_opcodes(): opcode = tag[0] start = tag[3] end = tag[4] if opcode != "equal": # 添加高亮标签 text_area.tag_add("diff", f"1.0+{start}c", f"1.0+{end}c") text_area.configure(state="disabled") self.status_var.set("差异已高亮显示") def choose_color(self): """选择高亮颜色""" color = askcolor(title="选择高亮颜色", initialcolor=self.highlight_color) if color[1]: self.highlight_color = color[1] self.color_btn.configure(bg=self.highlight_color) def export_report(self): """导出差异报告""" if not self.results: messagebox.showwarning("警告", "没有可导出的结果") return try: # 创建报告数据结构 report_data = [] for signal, files_data in self.results.items(): for file, content in files_data.items(): report_data.append({ "信号": signal, "文件": file, "行内容": content }) # 转换为DataFrame df = pd.DataFrame(report_data) # 保存到Excel save_path = filedialog.asksaveasfilename( defaultextension=".xlsx", filetypes=[("Excel文件", "*.xlsx")], title="保存差异报告" ) if save_path: df.to_excel(save_path, index=False) self.status_var.set(f"报告已保存到: {save_path}") except Exception as e: messagebox.showerror("错误", f"导出报告失败: {str(e)}") def clear_cache(self): """清除缓存""" try: for file in os.listdir(self.cache_dir): if file.endswith(".cache"): os.remove(os.path.join(self.cache_dir, file)) self.file_cache = {} self.column_cache = {} self.status_var.set("缓存已清除") except Exception as e: self.status_var.set(f"清除缓存失败: {str(e)}") def manual_column_select(self): """手动指定列名位置""" if not self.files: messagebox.showinfo("提示", "请先选择文件夹") return # 创建手动选择窗口 manual_window = tk.Toplevel(self.root) manual_window.title("手动指定列名位置") manual_window.geometry("400x300") # 文件选择 ttk.Label(manual_window, text="选择文件:").pack(pady=(10, 5)) file_var = tk.StringVar() file_combo = ttk.Combobox(manual_window, textvariable=file_var, values=[os.path.basename(f) for f in self.files]) file_combo.pack(fill=tk.X, padx=20, pady=5) file_combo.current(0) # 行号输入 ttk.Label(manual_window, text="列名行号:").pack(pady=(10, 5)) row_var = tk.StringVar(value="1") row_entry = ttk.Entry(manual_window, textvariable=row_var) row_entry.pack(fill=tk.X, padx=20, pady=5) # 列号输入 ttk.Label(manual_window, text="信号列号:").pack(pady=(10, 5)) col_var = tk.StringVar(value="1") col_entry = ttk.Entry(manual_window, textvariable=col_var) col_entry.pack(fill=tk.X, padx=20, pady=5) # 确认按钮 def confirm_selection(): try: file_idx = file_combo.current() file_path = self.files[file_idx] header_row = int(row_var.get()) signal_col = int(col_var.get()) # 保存到缓存 header_info = {"header_row": header_row, "signal_col": signal_col} self.save_header_cache(file_path, header_info) messagebox.showinfo("成功", f"已为 {os.path.basename(file_path)} 设置列名位置:行{header_row} 列{signal_col}") manual_window.destroy() except Exception as e: messagebox.showerror("错误", f"无效输入: {str(e)}") ttk.Button(manual_window, text="确认", command=confirm_selection).pack(pady=20) if __name__ == "__main__": root = tk.Tk() app = EnhancedSignalComparator(root) root.mainloop() 1、文件夹内一共三个文件,每个文件中都有只有一个信号,但是实际上,901_CAN送受信値-f.xlsx中没有找到信号,在【ドラフト版】D01D-00-02(HEV車).xlsm与【ドラフト版】D01D-00-03(コンベ車).xlsx分别找到了两个信号,但实际上只有一个信号。 2、行内容比对中【ドラフト版】D01D-00-02(HEV車).xlsm与【ドラフト版】D01D-00-03(コンベ車).xlsx搜索到的内容相同,但实际上有不同的

.text:0000000140070C34 .text:0000000140070C34 ; __int64 __usercall KiDispatchException@<rax>(PEXCEPTION_RECORD ExceptionRecord@<rcx>, char) .text:0000000140070C34 KiDispatchException proc near ; CODE XREF: KiInitializeUserApc+342↓p .text:0000000140070C34 ; KiRaiseException+1A8↓p .text:0000000140070C34 ; KiExceptionDispatch+BD↓p .text:0000000140070C34 ; KiFastFailDispatch+DF↓p .text:0000000140070C34 ; KiParkUmsThread+3D6↓p .text:0000000140070C34 ; PspInitializeThunkContext+14926F↓p .text:0000000140070C34 ; KiSwapToUmsThread+420↓p .text:0000000140070C34 ; DATA XREF: .rdata:00000001402ACCA4↓o .text:0000000140070C34 ; .pdata:000000014037BF38↓o .text:0000000140070C34 ; sub_1407AAAC8+44D1↓o .text:0000000140070C34 ; RtlpFunctionAddressTableEntry+23↓o .text:0000000140070C34 .text:0000000140070C34 BugCheckParameter4= qword ptr -1B0h .text:0000000140070C34 var_1A8= qword ptr -1A8h .text:0000000140070C34 var_1A0= dword ptr -1A0h .text:0000000140070C34 var_198= qword ptr -198h .text:0000000140070C34 var_190= dword ptr -190h .text:0000000140070C34 var_188= qword ptr -188h .text:0000000140070C34 var_180= dword ptr -180h .text:0000000140070C34 var_17C= dword ptr -17Ch .text:0000000140070C34 var_178= dword ptr -178h .text:0000000140070C34 var_170= qword ptr -170h .text:0000000140070C34 var_168= qword ptr -168h .text:0000000140070C34 var_160= qword ptr -160h .text:0000000140070C34 var_158= qword ptr -158h .text:0000000140070C34 var_150= qword ptr -150h .text:0000000140070C34 var_148= qword ptr -148h .text:0000000140070C34 var_140= qword ptr -140h .text:0000000140070C34 var_138= qword ptr -138h .text:0000000140070C34 var_130= qword ptr -130h .text:0000000140070C34 var_128= qword ptr -128h .text:0000000140070C34 var_120= qword ptr -120h .text:0000000140070C34 var_118= qword ptr -118h .text:0000000140070C34 var_110= qword ptr -110h .text:0000000140070C34 var_108= qword ptr -108h .text:0000000140070C34 var_100= qword ptr -100h .text:0000000140070C34 var_F0= dword ptr -0F0h .text:0000000140070C34 var_E0= qword ptr -0E0h .text:0000000140070C34 var_D8= dword ptr -0D8h .text:0000000140070C34 var_D0= byte ptr -0D0h .text:0000000140070C34 var_58= byte ptr -58h .text:0000000140070C34 var_50= xmmword ptr -50h .text:0000000140070C34 var_40= qword ptr -40h .text:0000000140070C34 var_38= qword ptr -38h .text:0000000140070C34 arg_20= byte ptr 30h .text:0000000140070C34 .text:0000000140070C34 ; FUNCTION CHUNK AT .text:000000014015BC0C SIZE 000000C3 BYTES .text:0000000140070C34 ; FUNCTION CHUNK AT .text:000000014018730E SIZE 00000120 BYTES .text:0000000140070C34 .text:0000000140070C34 ; __unwind { // __GSHandlerCheck_SEH .text:0000000140070C34 40 55 push rbp .text:0000000140070C36 53 push rbx .text:0000000140070C37 56 push rsi .text:0000000140070C38 41 54 push r12 .text:0000000140070C3A 41 55 push r13 .text:0000000140070C3C 41 56 push r14 .text:0000000140070C3E 41 57 push r15 .text:0000000140070C40 48 81 EC A0 01 00 00 sub rsp, 1A0h .text:0000000140070C47 48 8D 6C 24 30 lea rbp, [rsp+30h] .text:0000000140070C4C 48 8B 05 45 44 2A 00 mov rax, cs:__security_cookie .text:0000000140070C53 48 33 C5 xor rax, rbp .text:0000000140070C56 48 89 85 68 01 00 00 mov [rbp+1A0h+var_38], rax .text:0000000140070C5D 45 8A F9 mov r15b, r9b .text:0000000140070C60 49 8B F0 mov rsi, r8 .text:0000000140070C63 4C 8B E2 mov r12, rdx .text:0000000140070C66 48 8B D9 mov rbx, rcx .text:0000000140070C69 48 89 4D 30 mov [rbp+1A0h+var_170], rcx .text:0000000140070C6D 48 89 95 80 00 00 00 mov [rbp+1A0h+var_120], rdx .text:0000000140070C74 4C 89 45 58 mov [rbp+1A0h+var_148], r8 .text:0000000140070C78 44 88 4D 00 mov byte ptr [rbp+1A0h+var_1A0], r9b .text:0000000140070C7C 65 48 8B 04 25 88 01 00 00 mov rax, gs:188h .text:0000000140070C85 48 8B 80 B8 00 00 00 mov rax, [rax+0B8h] .text:0000000140070C8C 48 89 45 08 mov [rbp+1A0h+var_198], rax .text:0000000140070C90 48 89 45 70 mov [rbp+1A0h+var_130], rax .text:0000000140070C94 65 FF 04 25 B4 5C 00 00 inc dword ptr gs:5CB4h .text:0000000140070C9C 41 BD 1F 00 10 00 mov r13d, 10001Fh .text:0000000140070CA2 44 89 6D 10 mov [rbp+1A0h+var_190], r13d .text:0000000140070CA6 45 84 C9 test r9b, r9b .text:0000000140070CA9 0F 85 CC 04 00 00 jnz loc_14007117B .text:0000000140070CA9 .text:0000000140070CAF .text:0000000140070CAF loc_140070CAF: ; CODE XREF: KiDispatchException+563↓j .text:0000000140070CAF 48 8D 55 24 lea rdx, [rbp+1A0h+var_17C] .text:0000000140070CB3 41 8B CD mov ecx, r13d .text:0000000140070CB6 E8 35 9A 03 00 call RtlGetExtendedContextLength .text:0000000140070CB6 .text:0000000140070CBB 8B 45 24 mov eax, [rbp+1A0h+var_17C] .text:0000000140070CBE 48 8D 48 0F lea rcx, [rax+0Fh] .text:0000000140070CC2 48 3B C8 cmp rcx, rax .text:0000000140070CC5 77 0A ja short loc_140070CD1 .text:0000000140070CC5 .text:0000000140070CC7 48 B9 F0 FF FF FF FF FF FF 0F mov rcx, 0FFFFFFFFFFFFFF0h .text:0000000140070CC7 .text:0000000140070CD1 .text:0000000140070CD1 loc_140070CD1: ; CODE XREF: KiDispatchException+91↑j .text:0000000140070CD1 48 83 E1 F0 and rcx, 0FFFFFFFFFFFFFFF0h .text:0000000140070CD5 48 8B C1 mov rax, rcx .text:0000000140070CD8 E8 23 1F 0E 00 call __chkstk .text:0000000140070CD8 .text:0000000140070CDD 48 2B E1 sub rsp, rcx .text:0000000140070CE0 4C 8D 74 24 30 lea r14, [rsp+1D0h+var_1A0] .text:0000000140070CE5 4C 89 75 68 mov [rbp+1A0h+var_138], r14 .text:0000000140070CE9 4C 8D 45 40 lea r8, [rbp+1A0h+var_160] .text:0000000140070CED 41 8B D5 mov edx, r13d .text:0000000140070CF0 49 8B CE mov rcx, r14 .text:0000000140070CF3 E8 AC 9A 03 00 call RtlInitializeExtendedContext .text:0000000140070CF3 .text:0000000140070CF8 89 45 20 mov [rbp+1A0h+var_180], eax .text:0000000140070CFB 4D 8B C6 mov r8, r14 .text:0000000140070CFE 49 8B D4 mov rdx, r12 .text:0000000140070D01 48 8B CE mov rcx, rsi .text:0000000140070D04 E8 DF 1B 00 00 call KeContextFromKframes .text:0000000140070D04 .text:0000000140070D09 81 3B 03 00 00 80 cmp dword ptr [rbx], 80000003h .text:0000000140070D0F 0F 84 9E 04 00 00 jz loc_1400711B3 .text:0000000140070D0F .text:0000000140070D15 .text:0000000140070D15 loc_140070D15: ; CODE XREF: KiDispatchException+586↓j .text:0000000140070D15 45 84 FF test r15b, r15b .text:0000000140070D18 0F 85 7E 04 00 00 jnz loc_14007119C .text:0000000140070D18 .text:0000000140070D1E .text:0000000140070D1E loc_140070D1E: ; CODE XREF: KiDispatchException+574↓j .text:0000000140070D1E 45 8A C7 mov r8b, r15b .text:0000000140070D21 49 8B D6 mov rdx, r14 ; int .text:0000000140070D24 48 8B CB mov rcx, rbx ; int .text:0000000140070D27 E8 FC 04 00 00 call KiPreprocessFault .text:0000000140070D27 .text:0000000140070D2C 84 C0 test al, al .text:0000000140070D2E 0F 85 8B 04 00 00 jnz loc_1400711BF .text:0000000140070D2E .text:0000000140070D34 .text:0000000140070D34 loc_140070D34: ; CODE XREF: KiDispatchException+1166FC↓j .text:0000000140070D34 45 84 FF test r15b, r15b .text:0000000140070D37 75 4C jnz short loc_140070D85 .text:0000000140070D37 .text:0000000140070D39 44 38 BD D0 01 00 00 cmp [rbp+1A0h+arg_20], r15b .text:0000000140070D40 0F 84 EF 65 11 00 jz loc_140187335 .text:0000000140070D40 .text:0000000140070D46 44 88 7C 24 28 mov byte ptr [rsp+1D0h+var_1A8], r15b .text:0000000140070D4B 44 88 7C 24 20 mov byte ptr [rsp+1D0h+BugCheckParameter4], r15b .text:0000000140070D50 4D 8B CE mov r9, r14 .text:0000000140070D53 4C 8B C3 mov r8, rbx .text:0000000140070D56 49 8B D4 mov rdx, r12 .text:0000000140070D59 48 8B CE mov rcx, rsi .text:0000000140070D5C 48 8B 05 2D 34 35 00 mov rax, cs:KiDebugRoutine .text:0000000140070D63 FF D0 call rax ; KdpStub .text:0000000140070D63 .text:0000000140070D65 84 C0 test al, al .text:0000000140070D67 0F 85 52 04 00 00 jnz loc_1400711BF .text:0000000140070D67 .text:0000000140070D6D 49 8B D6 mov rdx, r14 ; Context .text:0000000140070D70 48 8B CB mov rcx, rbx ; ExceptionRecord .text:0000000140070D73 E8 F0 2E 00 00 call RtlDispatchException .text:0000000140070D73 .text:0000000140070D78 84 C0 test al, al .text:0000000140070D7A 0F 84 B5 65 11 00 jz loc_140187335 .text:0000000140070D7A .text:0000000140070D80 E9 3A 04 00 00 jmp loc_1400711BF .text:0000000140070D80 .text:0000000140070D85 ; --------------------------------------------------------------------------- .text:0000000140070D85 .text:0000000140070D85 loc_140070D85: ; CODE XREF: KiDispatchException+103↑j .text:0000000140070D85 49 8B 96 98 00 00 00 mov rdx, [r14+98h] .text:0000000140070D8C 48 89 55 18 mov [rbp+1A0h+var_188], rdx .text:0000000140070D90 48 89 55 38 mov [rbp+1A0h+var_168], rdx .text:0000000140070D94 48 8B 45 08 mov rax, [rbp+1A0h+var_198] .text:0000000140070D98 8B 80 AC 06 00 00 mov eax, [rax+6ACh] .text:0000000140070D9E A8 01 test al, 1 .text:0000000140070DA0 75 3D jnz short loc_140070DDF .text:0000000140070DA0 .text:0000000140070DA2 65 48 8B 04 25 88 01 00 00 mov rax, gs:188h .text:0000000140070DAB 48 8B 88 B8 00 00 00 mov rcx, [rax+0B8h] .text:0000000140070DB2 48 83 B9 28 04 00 00 00 cmp qword ptr [rcx+428h], 0 .text:0000000140070DBA 74 0C jz short loc_140070DC8 .text:0000000140070DBA .text:0000000140070DBC 81 3B 02 00 00 80 cmp dword ptr [rbx], 80000002h .text:0000000140070DC2 0F 84 B3 65 11 00 jz loc_14018737B .text:0000000140070DC2 .text:0000000140070DC8 .text:0000000140070DC8 loc_140070DC8: ; CODE XREF: KiDispatchException+186↑j .text:0000000140070DC8 ; KiDispatchException+116751↓j .text:0000000140070DC8 41 0F B7 46 38 movzx eax, word ptr [r14+38h] .text:0000000140070DCD B9 F8 FF 00 00 mov ecx, 0FFF8h .text:0000000140070DD2 66 23 C1 and ax, cx .text:0000000140070DD5 66 83 F8 20 cmp ax, 20h ; ' ' .text:0000000140070DD9 0F 84 BB 65 11 00 jz loc_14018739A .text:0000000140070DD9 .text:0000000140070DDF .text:0000000140070DDF loc_140070DDF: ; CODE XREF: KiDispatchException+16C↑j .text:0000000140070DDF ; KiDispatchException+116794↓j .text:0000000140070DDF 8B 4B 18 mov ecx, [rbx+18h] .text:0000000140070DE2 48 83 C1 04 add rcx, 4 .text:0000000140070DE6 48 8D 0C CB lea rcx, [rbx+rcx*8] ; void * .text:0000000140070DEA 4C 8B C3 mov r8, rbx .text:0000000140070DED 4C 2B C1 sub r8, rcx .text:0000000140070DF0 49 81 C0 98 00 00 00 add r8, 98h ; Size .text:0000000140070DF7 33 D2 xor edx, edx ; Val .text:0000000140070DF9 E8 82 77 0E 00 call memset .text:0000000140070DF9 .text:0000000140070DFE 80 BD D0 01 00 00 00 cmp [rbp+1A0h+arg_20], 0 .text:0000000140070E05 0F 84 CD 03 00 00 jz loc_1400711D8 .text:0000000140070E05 .text:0000000140070E0B 48 8B CB mov rcx, rbx .text:0000000140070E0E E8 ED 21 6B 00 call KdIsThisAKdTrap .text:0000000140070E0E .text:0000000140070E13 65 48 8B 0C 25 88 01 00 00 mov rcx, gs:188h .text:0000000140070E1C 48 8B 91 B8 00 00 00 mov rdx, [rcx+0B8h] .text:0000000140070E23 48 83 BA 20 04 00 00 00 cmp qword ptr [rdx+420h], 0 .text:0000000140070E2B 0F 85 9C 65 11 00 jnz loc_1401873CD .text:0000000140070E2B .text:0000000140070E31 8A 0D BC 9A 2C 00 mov cl, cs:KdIgnoreUmExceptions .text:0000000140070E37 84 C9 test cl, cl .text:0000000140070E39 0F 85 8E 65 11 00 jnz loc_1401873CD .text:0000000140070E39 .text:0000000140070E3F .text:0000000140070E3F loc_140070E3F: ; CODE XREF: KiDispatchException+1167A1↓j .text:0000000140070E3F C6 44 24 28 00 mov byte ptr [rsp+1D0h+var_1A8], 0 .text:0000000140070E44 44 88 7C 24 20 mov byte ptr [rsp+1D0h+BugCheckParameter4], r15b .text:0000000140070E49 4D 8B CE mov r9, r14 .text:0000000140070E4C 4C 8B C3 mov r8, rbx .text:0000000140070E4F 49 8B D4 mov rdx, r12 .text:0000000140070E52 48 8B CE mov rcx, rsi .text:0000000140070E55 48 8B 05 34 33 35 00 mov rax, cs:KiDebugRoutine .text:0000000140070E5C FF D0 call rax ; KdpStub .text:0000000140070E5C .text:0000000140070E5E 84 C0 test al, al .text:0000000140070E60 0F 85 59 03 00 00 jnz loc_1400711BF .text:0000000140070E60 .text:0000000140070E66 .text:0000000140070E66 loc_140070E66: ; CODE XREF: KiDispatchException+11679B↓j .text:0000000140070E66 45 33 C0 xor r8d, r8d .text:0000000140070E69 B2 01 mov dl, 1 .text:0000000140070E6B 48 8B CB mov rcx, rbx .text:0000000140070E6E E8 61 5B 4B 00 call DbgkForwardException .text:0000000140070E6E .text:0000000140070E73 84 C0 test al, al .text:0000000140070E75 0F 85 DE 02 00 00 jnz loc_140071159 .text:0000000140070E75 .text:0000000140070E7B 48 8B 45 08 mov rax, [rbp+1A0h+var_198] .text:0000000140070E7F 48 83 B8 F0 06 00 00 00 cmp qword ptr [rax+6F0h], 0 .text:0000000140070E87 0F 85 4D 65 11 00 jnz loc_1401873DA .text:0000000140070E87 .text:0000000140070E8D .text:0000000140070E8D loc_140070E8D: ; CODE XREF: KiDispatchException+1167CB↓j .text:0000000140070E8D FA cli .text:0000000140070E8E 0F BA B6 78 01 00 00 08 btr dword ptr [rsi+178h], 8 .text:0000000140070E96 FB sti .text:0000000140070E97 C7 85 B0 00 00 00 05 00 00 C0 mov [rbp+1A0h+var_F0], 0C0000005h .text:0000000140070EA1 4C 8B 65 18 mov r12, [rbp+1A0h+var_188] .text:0000000140070EA1 .text:0000000140070EA5 .text:0000000140070EA5 loc_140070EA5: ; CODE XREF: KiDispatchException+50C↓j .text:0000000140070EA5 90 nop .text:0000000140070EA5 .text:0000000140070EA6 .text:0000000140070EA6 loc_140070EA6: ; DATA XREF: .rdata:00000001402ACC8C↓o .text:0000000140070EA6 ; __try { // __except at loc_1400710B4 .text:0000000140070EA6 49 8B D4 mov rdx, r12 .text:0000000140070EA9 48 89 55 48 mov [rbp+1A0h+var_158], rdx .text:0000000140070EAD 41 8B C5 mov eax, r13d .text:0000000140070EB0 B9 40 00 10 00 mov ecx, 100040h .text:0000000140070EB5 23 C1 and eax, ecx .text:0000000140070EB7 3B C1 cmp eax, ecx .text:0000000140070EB9 75 16 jnz short loc_140070ED1 .text:0000000140070EB9 .text:0000000140070EBB 48 8B 45 40 mov rax, [rbp+1A0h+var_160] .text:0000000140070EBF 8B 48 14 mov ecx, [rax+14h] .text:0000000140070EC2 48 2B D1 sub rdx, rcx .text:0000000140070EC5 48 89 55 48 mov [rbp+1A0h+var_158], rdx .text:0000000140070EC9 48 83 E2 C0 and rdx, 0FFFFFFFFFFFFFFC0h .text:0000000140070ECD 48 89 55 48 mov [rbp+1A0h+var_158], rdx .text:0000000140070ECD .text:0000000140070ED1 .text:0000000140070ED1 loc_140070ED1: ; CODE XREF: KiDispatchException+285↑j .text:0000000140070ED1 48 8D 42 D8 lea rax, [rdx-28h] .text:0000000140070ED5 48 83 E0 F0 and rax, 0FFFFFFFFFFFFFFF0h .text:0000000140070ED9 48 89 45 18 mov [rbp+1A0h+var_188], rax .text:0000000140070EDD 48 89 45 78 mov [rbp+1A0h+var_128], rax .text:0000000140070EE1 4C 8D 80 60 FF FF FF lea r8, [rax-0A0h] .text:0000000140070EE8 4C 89 45 08 mov [rbp+1A0h+var_198], r8 .text:0000000140070EEC 4C 89 85 98 00 00 00 mov [rbp+1A0h+var_108], r8 .text:0000000140070EF3 49 83 C0 E0 add r8, 0FFFFFFFFFFFFFFE0h .text:0000000140070EF7 4C 89 85 90 00 00 00 mov [rbp+1A0h+var_110], r8 .text:0000000140070EFE 4D 8D B8 30 FB FF FF lea r15, [r8-4D0h] .text:0000000140070F05 4C 89 7D 60 mov [rbp+1A0h+var_140], r15 .text:0000000140070F09 49 8B C7 mov rax, r15 .text:0000000140070F0C 49 2B C0 sub rax, r8 .text:0000000140070F0F 89 85 50 01 00 00 mov dword ptr [rbp+1A0h+var_50], eax .text:0000000140070F15 49 8B CC mov rcx, r12 .text:0000000140070F18 49 2B CF sub rcx, r15 .text:0000000140070F1B 89 8D 54 01 00 00 mov dword ptr [rbp+1A0h+var_50+4], ecx .text:0000000140070F21 89 85 58 01 00 00 mov dword ptr [rbp+1A0h+var_50+8], eax .text:0000000140070F27 C7 85 5C 01 00 00 D0 04 00 00 mov dword ptr [rbp+1A0h+var_50+0Ch], 4D0h .text:0000000140070F31 48 8B C2 mov rax, rdx .text:0000000140070F34 49 2B C0 sub rax, r8 .text:0000000140070F37 89 85 60 01 00 00 mov dword ptr [rbp+1A0h+var_40], eax .text:0000000140070F3D 49 8B C4 mov rax, r12 .text:0000000140070F40 48 2B C2 sub rax, rdx .text:0000000140070F43 89 85 64 01 00 00 mov dword ptr [rbp+1A0h+var_40+4], eax .text:0000000140070F49 48 8D 41 FF lea rax, [rcx-1] .text:0000000140070F4D 48 3D FE 0F 00 00 cmp rax, 0FFEh .text:0000000140070F53 0F 87 25 01 00 00 ja loc_14007107E .text:0000000140070F53 .text:0000000140070F59 41 F6 C7 0F test r15b, 0Fh .text:0000000140070F5D 0F 85 16 01 00 00 jnz loc_140071079 .text:0000000140070F5D .text:0000000140070F63 4C 3B 3D 56 42 35 00 cmp r15, cs:MmUserProbeAddress .text:0000000140070F6A 4C 0F 43 3D 4E 42 35 00 cmovnb r15, cs:MmUserProbeAddress .text:0000000140070F72 41 8A 07 mov al, [r15] .text:0000000140070F75 41 88 07 mov [r15], al .text:0000000140070F78 41 8A 44 0F FF mov al, [r15+rcx-1] .text:0000000140070F7D 41 88 44 0F FF mov [r15+rcx-1], al .text:0000000140070F82 48 8B 4D 78 mov rcx, [rbp+1A0h+var_128] .text:0000000140070F86 48 8B 95 98 00 00 00 mov rdx, [rbp+1A0h+var_108] .text:0000000140070F8D 4C 8B 7D 60 mov r15, [rbp+1A0h+var_140] .text:0000000140070F8D .text:0000000140070F91 .text:0000000140070F91 loc_140070F91: ; CODE XREF: KiDispatchException+463↓j .text:0000000140070F91 4C 89 61 18 mov [rcx+18h], r12 .text:0000000140070F95 49 8B 86 F8 00 00 00 mov rax, [r14+0F8h] .text:0000000140070F9C 48 89 01 mov [rcx], rax .text:0000000140070F9F 0F 10 03 movups xmm0, xmmword ptr [rbx] .text:0000000140070FA2 0F 11 02 movups xmmword ptr [rdx], xmm0 .text:0000000140070FA5 0F 10 4B 10 movups xmm1, xmmword ptr [rbx+10h] .text:0000000140070FA9 0F 11 4A 10 movups xmmword ptr [rdx+10h], xmm1 .text:0000000140070FAD 0F 10 43 20 movups xmm0, xmmword ptr [rbx+20h] .text:0000000140070FB1 0F 11 42 20 movups xmmword ptr [rdx+20h], xmm0 .text:0000000140070FB5 0F 10 4B 30 movups xmm1, xmmword ptr [rbx+30h] .text:0000000140070FB9 0F 11 4A 30 movups xmmword ptr [rdx+30h], xmm1 .text:0000000140070FBD 0F 10 43 40 movups xmm0, xmmword ptr [rbx+40h] .text:0000000140070FC1 0F 11 42 40 movups xmmword ptr [rdx+40h], xmm0 .text:0000000140070FC5 0F 10 4B 50 movups xmm1, xmmword ptr [rbx+50h] .text:0000000140070FC9 0F 11 4A 50 movups xmmword ptr [rdx+50h], xmm1 .text:0000000140070FCD 0F 10 43 60 movups xmm0, xmmword ptr [rbx+60h] .text:0000000140070FD1 0F 11 42 60 movups xmmword ptr [rdx+60h], xmm0 .text:0000000140070FD5 B8 80 00 00 00 mov eax, 80h .text:0000000140070FDA 48 03 D0 add rdx, rax .text:0000000140070FDD 0F 10 43 70 movups xmm0, xmmword ptr [rbx+70h] .text:0000000140070FE1 0F 11 42 F0 movups xmmword ptr [rdx-10h], xmm0 .text:0000000140070FE5 0F 10 0C 03 movups xmm1, xmmword ptr [rbx+rax] .text:0000000140070FE9 0F 11 0A movups xmmword ptr [rdx], xmm1 .text:0000000140070FEC 48 8B 44 03 10 mov rax, [rbx+rax+10h] .text:0000000140070FF1 48 89 42 10 mov [rdx+10h], rax .text:0000000140070FF5 48 83 64 24 28 00 and [rsp+1D0h+var_1A8], 0 .text:0000000140070FFB 48 8B 45 40 mov rax, [rbp+1A0h+var_160] .text:0000000140070FFF 48 89 44 24 20 mov [rsp+1D0h+BugCheckParameter4], rax .text:0000000140071004 45 8B CD mov r9d, r13d .text:0000000140071007 4C 8D 85 50 01 00 00 lea r8, [rbp+1A0h+var_50] .text:000000014007100E 48 8B 9D 90 00 00 00 mov rbx, [rbp+1A0h+var_110] .text:0000000140071015 48 8B D3 mov rdx, rbx .text:0000000140071018 B1 01 mov cl, 1 .text:000000014007101A E8 E9 95 03 00 call RtlpCopyExtendedContext .text:000000014007101A .text:000000014007101F 89 45 20 mov [rbp+1A0h+var_180], eax .text:0000000140071022 0F 10 85 50 01 00 00 movups xmm0, [rbp+1A0h+var_50] .text:0000000140071029 0F 11 03 movups xmmword ptr [rbx], xmm0 .text:000000014007102C F2 0F 10 8D 60 01 00 00 movsd xmm1, [rbp+1A0h+var_40] .text:0000000140071034 F2 0F 11 4B 10 movsd qword ptr [rbx+10h], xmm1 .text:0000000140071039 FA cli .text:000000014007103A 4C 89 BE 80 01 00 00 mov [rsi+180h], r15 .text:0000000140071041 B8 33 00 00 00 mov eax, 33h ; '3' .text:0000000140071046 66 89 86 70 01 00 00 mov [rsi+170h], ax .text:000000014007104D 48 8B 05 1C 46 35 00 mov rax, cs:KeUserExceptionDispatcher .text:0000000140071054 48 89 86 68 01 00 00 mov [rsi+168h], rax .text:000000014007105B 65 48 8B 04 25 88 01 00 00 mov rax, gs:188h .text:0000000140071064 48 8B 88 B8 00 00 00 mov rcx, [rax+0B8h] .text:000000014007106B 48 8B 91 C8 02 00 00 mov rdx, [rcx+2C8h] .text:0000000140071072 48 85 D2 test rdx, rdx .text:0000000140071075 75 25 jnz short loc_14007109C .text:0000000140071075 .text:0000000140071077 EB 35 jmp short loc_1400710AE .text:0000000140071077 .text:0000000140071079 ; --------------------------------------------------------------------------- .text:0000000140071079 .text:0000000140071079 loc_140071079: ; CODE XREF: KiDispatchException+329↑j .text:0000000140071079 E8 AE F3 67 00 call ExRaiseDatatypeMisalignment .text:0000000140071079 .text:000000014007107E ; --------------------------------------------------------------------------- .text:000000014007107E .text:000000014007107E loc_14007107E: ; CODE XREF: KiDispatchException+31F↑j .text:000000014007107E 41 B8 10 00 00 00 mov r8d, 10h ; Alignment .text:0000000140071084 48 8B D1 mov rdx, rcx ; Length .text:0000000140071087 49 8B CF mov rcx, r15 ; Address .text:000000014007108A E8 41 FB 3A 00 call ProbeForWrite .text:000000014007108A .text:000000014007108F 48 8B 4D 18 mov rcx, [rbp+1A0h+var_188] .text:0000000140071093 48 8B 55 08 mov rdx, [rbp+1A0h+var_198] .text:0000000140071097 E9 F5 FE FF FF jmp loc_140070F91 .text:0000000140071097 .text:000000014007109C ; --------------------------------------------------------------------------- .text:000000014007109C .text:000000014007109C loc_14007109C: ; CODE XREF: KiDispatchException+441↑j .text:000000014007109C 48 8B 86 68 01 00 00 mov rax, [rsi+168h] .text:00000001400710A3 48 89 46 58 mov [rsi+58h], rax .text:00000001400710A7 48 89 96 68 01 00 00 mov [rsi+168h], rdx .text:00000001400710A7 .text:00000001400710AE .text:00000001400710AE loc_1400710AE: ; CODE XREF: KiDispatchException+443↑j .text:00000001400710AE FB sti .text:00000001400710AF E9 A5 00 00 00 jmp loc_140071159 .text:00000001400710AF ; } // starts at 140070EA6 .text:00000001400710AF .text:00000001400710B4 ; --------------------------------------------------------------------------- .text:00000001400710B4 .text:00000001400710B4 loc_1400710B4: ; DATA XREF: .rdata:00000001402ACC8C↓o .text:00000001400710B4 ; __except(loc_14015BC0C) // owned by 140070EA6 .text:00000001400710B4 48 8B 5D 30 mov rbx, [rbp+1A0h+var_170] .text:00000001400710B8 48 8B 75 58 mov rsi, [rbp+1A0h+var_148] .text:00000001400710BC 81 BD B0 00 00 00 FD 00 00 C0 cmp [rbp+1A0h+var_F0], 0C00000FDh .text:00000001400710C6 75 7D jnz short loc_140071145 .text:00000001400710C6 .text:00000001400710C8 48 8B 43 10 mov rax, [rbx+10h] .text:00000001400710CC 48 89 85 C0 00 00 00 mov [rbp+1A0h+var_E0], rax .text:00000001400710D3 48 8D 85 B0 00 00 00 lea rax, [rbp+1A0h+var_F0] .text:00000001400710DA 0F 10 00 movups xmm0, xmmword ptr [rax] .text:00000001400710DD 0F 11 03 movups xmmword ptr [rbx], xmm0 .text:00000001400710E0 0F 10 48 10 movups xmm1, xmmword ptr [rax+10h] .text:00000001400710E4 0F 11 4B 10 movups xmmword ptr [rbx+10h], xmm1 .text:00000001400710E8 0F 10 40 20 movups xmm0, xmmword ptr [rax+20h] .text:00000001400710EC 0F 11 43 20 movups xmmword ptr [rbx+20h], xmm0 .text:00000001400710F0 0F 10 48 30 movups xmm1, xmmword ptr [rax+30h] .text:00000001400710F4 0F 11 4B 30 movups xmmword ptr [rbx+30h], xmm1 .text:00000001400710F8 0F 10 40 40 movups xmm0, xmmword ptr [rax+40h] .text:00000001400710FC 0F 11 43 40 movups xmmword ptr [rbx+40h], xmm0 .text:0000000140071100 0F 10 48 50 movups xmm1, xmmword ptr [rax+50h] .text:0000000140071104 0F 11 4B 50 movups xmmword ptr [rbx+50h], xmm1 .text:0000000140071108 0F 10 40 60 movups xmm0, xmmword ptr [rax+60h] .text:000000014007110C 0F 11 43 60 movups xmmword ptr [rbx+60h], xmm0 .text:0000000140071110 0F 10 48 70 movups xmm1, xmmword ptr [rax+70h] .text:0000000140071114 0F 11 4B 70 movups xmmword ptr [rbx+70h], xmm1 .text:0000000140071118 0F 10 80 80 00 00 00 movups xmm0, xmmword ptr [rax+80h] .text:000000014007111F 0F 11 83 80 00 00 00 movups xmmword ptr [rbx+80h], xmm0 .text:0000000140071126 48 8B 80 90 00 00 00 mov rax, [rax+90h] .text:000000014007112D 48 89 83 90 00 00 00 mov [rbx+90h], rax .text:0000000140071134 44 8B 6D 10 mov r13d, [rbp+1A0h+var_190] .text:0000000140071138 4C 8B 75 68 mov r14, [rbp+1A0h+var_138] .text:000000014007113C 4C 8B 65 38 mov r12, [rbp+1A0h+var_168] .text:0000000140071140 E9 60 FD FF FF jmp loc_140070EA5 .text:0000000140071140 .text:0000000140071145 ; --------------------------------------------------------------------------- .text:0000000140071145 .text:0000000140071145 loc_140071145: ; CODE XREF: KiDispatchException+492↑j .text:0000000140071145 4C 8B 75 70 mov r14, [rbp+1A0h+var_130] .text:0000000140071149 4C 8B A5 80 00 00 00 mov r12, [rbp+1A0h+var_120] .text:0000000140071150 44 8A 7D 00 mov r15b, byte ptr [rbp+1A0h+var_1A0] .text:0000000140071154 E9 83 00 00 00 jmp loc_1400711DC .text:0000000140071154 .text:0000000140071159 ; --------------------------------------------------------------------------- .text:0000000140071159 .text:0000000140071159 loc_140071159: ; CODE XREF: KiDispatchException+241↑j .text:0000000140071159 ; KiDispatchException+47B↑j .text:0000000140071159 ; KiDispatchException+5A2↓j .text:0000000140071159 ; KiDispatchException+5B8↓j .text:0000000140071159 ; KiDispatchException+5DB↓j .text:0000000140071159 ; KiDispatchException+5EC↓j .text:0000000140071159 ; KiDispatchException+1166F6↓j .text:0000000140071159 ; KiDispatchException+116761↓j .text:0000000140071159 ; KiDispatchException+1167C5↓j .text:0000000140071159 ; KiDispatchException+1167EF↓j .text:0000000140071159 48 8B 8D 68 01 00 00 mov rcx, [rbp+1A0h+var_38] .text:0000000140071160 48 33 CD xor rcx, rbp ; StackCookie .text:0000000140071163 E8 48 AD 0C 00 call __security_check_cookie .text:0000000140071163 .text:0000000140071168 48 8D A5 70 01 00 00 lea rsp, [rbp+170h] .text:000000014007116F 41 5F pop r15 .text:0000000140071171 41 5E pop r14 .text:0000000140071173 41 5D pop r13 .text:0000000140071175 41 5C pop r12 .text:0000000140071177 5E pop rsi .text:0000000140071178 5B pop rbx .text:0000000140071179 5D pop rbp .text:000000014007117A C3 retn .text:000000014007117A .text:000000014007117B ; --------------------------------------------------------------------------- .text:000000014007117B .text:000000014007117B loc_14007117B: ; CODE XREF: KiDispatchException+75↑j .text:000000014007117B 48 8B 05 A6 40 35 00 mov rax, cs:KeFeatureBits .text:0000000140071182 25 00 00 80 00 and eax, 800000h .text:0000000140071187 B9 5F 00 10 00 mov ecx, 10005Fh .text:000000014007118C 48 85 C0 test rax, rax .text:000000014007118F 44 0F 45 E9 cmovnz r13d, ecx .text:0000000140071193 44 89 6D 10 mov [rbp+1A0h+var_190], r13d .text:0000000140071197 E9 13 FB FF FF jmp loc_140070CAF .text:0000000140071197 .text:000000014007119C ; --------------------------------------------------------------------------- .text:000000014007119C .text:000000014007119C loc_14007119C: ; CODE XREF: KiDispatchException+E4↑j .text:000000014007119C 48 8B 45 08 mov rax, [rbp+1A0h+var_198] .text:00000001400711A0 48 83 B8 F0 06 00 00 00 cmp qword ptr [rax+6F0h], 0 .text:00000001400711A8 0F 84 70 FB FF FF jz loc_140070D1E .text:00000001400711A8 .text:00000001400711AE E9 5B 61 11 00 jmp loc_14018730E .text:00000001400711AE .text:00000001400711B3 ; --------------------------------------------------------------------------- .text:00000001400711B3 .text:00000001400711B3 loc_1400711B3: ; CODE XREF: KiDispatchException+DB↑j .text:00000001400711B3 49 FF 8E F8 00 00 00 dec qword ptr [r14+0F8h] .text:00000001400711BA E9 56 FB FF FF jmp loc_140070D15 .text:00000001400711BA .text:00000001400711BF ; --------------------------------------------------------------------------- .text:00000001400711BF .text:00000001400711BF loc_1400711BF: ; CODE XREF: KiDispatchException+FA↑j .text:00000001400711BF ; KiDispatchException+133↑j .text:00000001400711BF ; KiDispatchException+14C↑j .text:00000001400711BF ; KiDispatchException+22C↑j .text:00000001400711BF ; KiDispatchException+116722↓j .text:00000001400711BF 44 88 7C 24 20 mov byte ptr [rsp+1D0h+BugCheckParameter4], r15b .text:00000001400711C4 45 8B 4E 30 mov r9d, [r14+30h] .text:00000001400711C8 4D 8B C6 mov r8, r14 .text:00000001400711CB 49 8B D4 mov rdx, r12 .text:00000001400711CE 48 8B CE mov rcx, rsi .text:00000001400711D1 E8 7A BB 0D 00 call KeContextToKframes .text:00000001400711D1 .text:00000001400711D6 EB 81 jmp short loc_140071159 .text:00000001400711D6 .text:00000001400711D8 ; --------------------------------------------------------------------------- .text:00000001400711D8 .text:00000001400711D8 loc_1400711D8: ; CODE XREF: KiDispatchException+1D1↑j .text:00000001400711D8 4C 8B 75 08 mov r14, [rbp+1A0h+var_198] .text:00000001400711D8 .text:00000001400711DC .text:00000001400711DC loc_1400711DC: ; CODE XREF: KiDispatchException+520↑j .text:00000001400711DC 41 B0 01 mov r8b, 1 .text:00000001400711DF 41 8A D0 mov dl, r8b .text:00000001400711E2 48 8B CB mov rcx, rbx .text:00000001400711E5 E8 EA 57 4B 00 call DbgkForwardException .text:00000001400711E5 .text:00000001400711EA 84 C0 test al, al .text:00000001400711EC 0F 85 67 FF FF FF jnz loc_140071159 .text:00000001400711EC .text:00000001400711F2 49 83 BE F0 06 00 00 00 cmp qword ptr [r14+6F0h], 0 .text:00000001400711FA 0F 85 04 62 11 00 jnz loc_140187404 .text:00000001400711FA .text:0000000140071200 .text:0000000140071200 loc_140071200: ; CODE XREF: KiDispatchException+1167F5↓j .text:0000000140071200 41 B0 01 mov r8b, 1 .text:0000000140071203 33 D2 xor edx, edx .text:0000000140071205 48 8B CB mov rcx, rbx .text:0000000140071208 E8 C7 57 4B 00 call DbgkForwardException .text:0000000140071208 .text:000000014007120D 84 C0 test al, al .text:000000014007120F 0F 85 44 FF FF FF jnz loc_140071159 .text:000000014007120F .text:0000000140071215 8B 13 mov edx, [rbx] ; ExitStatus .text:0000000140071217 48 83 C9 FF or rcx, 0FFFFFFFFFFFFFFFFh ; ProcessHandle .text:000000014007121B E8 40 88 0D 00 call ZwTerminateProcess .text:000000014007121B .text:0000000140071220 E9 34 FF FF FF jmp loc_140071159 .text:0000000140071220 ; } // starts at 140070C34 .text:0000000140071220 .text:0000000140071220 KiDispatchException endp .text:0000000140071220 .text:0000000140071220 ; --------------------------------------------------------------------------- .text:0000000140071225 algn_140071225: ; DATA XREF: .rdata:00000001402ACCA4↓o .text:0000000140071225 ; .pdata:000000014037BF38↓o .text:0000000140071225 CC CC CC align 8 .text:0000000140071228 .text:0000000140071228 ; =============== S U B R O U T I N E ======================================= .text:0000000140071228 .text:0000000140071228 .text:0000000140071228 ; __int64 __fastcall KiPreprocessFault(int, int) .text:0000000140071228 KiPreprocessFault proc near ; CODE XREF: KiDispatchException+F3↑p分析每段函数和这个函数的执行流程和作用

Welcome to Ubuntu 24.04.2 LTS (GNU/Linux 6.8.0-60-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/pro System information as of Wed May 28 02:10:15 UTC 2025 System load: 0.0 Processes: 176 Usage of /: 1.9% of 386.42GB Users logged in: 1 Memory usage: 1% IPv4 address for eth0: 185.199.52.219 Swap usage: 0% IPv6 address for eth0: 2a02:4780:12:27e7::1 Expanded Security Maintenance for Applications is not enabled. 0 updates can be applied immediately. 14 additional security updates can be applied with ESM Apps. Learn more about enabling ESM Apps service at https://ubuntu.com/esm Last login: Wed May 28 02:08:30 2025 from 169.254.0.1 root@srv771551:~# ^C root@srv771551:~# which tabby # 应输出安装路径 tabby --version Command 'tabby' not found, did you mean: command 'ktabby' from deb kgames (2.3-1) Try: apt install <deb name> root@srv771551:~# sudo snap install tabby error: snap "tabby" not found root@srv771551:~# wget https://github.com/Eugeny/tabby/releases/download/v1.0.209/tabby-1.0.209-linux-x64.deb --2025-05-28 02:14:56-- https://github.com/Eugeny/tabby/releases/download/v1.0.209/tabby-1.0.209-linux-x64.deb Resolving github.com (github.com)... 20.207.73.82 Connecting to github.com (github.com)|20.207.73.82|:443... connected. HTTP request sent, awaiting response... 302 Found Location: https://objects.githubusercontent.com/github-production-release-asset-2e65be/77213120/49d65aef-dda0-40ec-8f6c-bb2f59b7b2ef?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=releaseassetproduction%2F20250528%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250528T021457Z&X-Amz-Expires=300&X-Amz-Signature=16929604a9e7523d5674ba06f7675fd4c04bf70e8b3d21ddf89c85c14d2a1c45&X-Amz-SignedHeaders=host&response-content-disposition=attachment%3B%20filename%3Dtabby-1.0.209-linux-x64.deb&response-content-type=application%2Foctet-stream [following] --2025-05-28 02:14:57-- https://objects.githubusercontent.com/github-production-release-asset-2e65be/77213120/49d65aef-dda0-40ec-8f6c-bb2f59b7b2ef?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=releaseassetproduction%2F20250528%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250528T021457Z&X-Amz-Expires=300&X-Amz-Signature=16929604a9e7523d5674ba06f7675fd4c04bf70e8b3d21ddf89c85c14d2a1c45&X-Amz-SignedHeaders=host&response-content-disposition=attachment%3B%20filename%3Dtabby-1.0.209-linux-x64.deb&response-content-type=application%2Foctet-stream Resolving objects.githubusercontent.com (objects.githubusercontent.com)... 185.199.108.133, 185.199.110.133, 185.199.109.133, ... Connecting to objects.githubusercontent.com (objects.githubusercontent.com)|185.199.108.133|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 90647356 (86M) [application/octet-stream] Saving to: 'tabby-1.0.209-linux-x64.deb' tabby-1.0.209-linux-x64.deb 100%[=================================================================================================================================>] 86.45M 6.40MB/s in 13s 2025-05-28 02:15:11 (6.84 MB/s) - 'tabby-1.0.209-linux-x64.deb' saved [90647356/90647356] root@srv771551:~# sudo apt-get install gdebi Reading package lists... Done Building dependency tree... Done Reading state information... Done gdebi is already the newest version (0.9.5.7+nmu7). gdebi set to manually installed. 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. root@srv771551:~# sudo gdebi tabby-*.deb /usr/bin/gdebi:113: SyntaxWarning: invalid escape sequence '\S' c = findall("[[(](\S+)/\S+[])]", msg)[0].lower() Reading package lists... Done Building dependency tree... Done Reading state information... Done Reading state information... Done This package is uninstallable Dependency is not satisfiable: gconf2 root@srv771551:~# sudo gdebi tabby-*.deb /usr/bin/gdebi:113: SyntaxWarning: invalid escape sequence '\S' c = findall("[[(](\S+)/\S+[])]", msg)[0].lower() Reading package lists... Done Building dependency tree... Done Reading state information... Done Reading state information... Done This package is uninstallable Dependency is not satisfiable: gconf2 root@srv771551:~# wget https://github.com/Eugeny/tabby/releases/download/v1.0.209/tabby-1.0.209-linux-x64.AppImage --2025-05-28 02:16:02-- https://github.com/Eugeny/tabby/releases/download/v1.0.209/tabby-1.0.209-linux-x64.AppImage Resolving github.com (github.com)... 20.207.73.82 Connecting to github.com (github.com)|20.207.73.82|:443... connected. HTTP request sent, awaiting response... 302 Found Location: https://objects.githubusercontent.com/github-production-release-asset-2e65be/77213120/6838174f-aae8-46a8-875f-4adc31805938?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=releaseassetproduction%2F20250528%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250528T021602Z&X-Amz-Expires=300&X-Amz-Signature=366b06d105ad23c750d2b24dfa63c80757d01eaaa1a2827052e5bdca604a9001&X-Amz-SignedHeaders=host&response-content-disposition=attachment%3B%20filename%3Dtabby-1.0.209-linux-x64.AppImage&response-content-type=application%2Foctet-stream [following] --2025-05-28 02:16:02-- https://objects.githubusercontent.com/github-production-release-asset-2e65be/77213120/6838174f-aae8-46a8-875f-4adc31805938?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=releaseassetproduction%2F20250528%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250528T021602Z&X-Amz-Expires=300&X-Amz-Signature=366b06d105ad23c750d2b24dfa63c80757d01eaaa1a2827052e5bdca604a9001&X-Amz-SignedHeaders=host&response-content-disposition=attachment%3B%20filename%3Dtabby-1.0.209-linux-x64.AppImage&response-content-type=application%2Foctet-stream Resolving objects.githubusercontent.com (objects.githubusercontent.com)... 185.199.109.133, 185.199.108.133, 185.199.111.133, ... Connecting to objects.githubusercontent.com (objects.githubusercontent.com)|185.199.109.133|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 134111099 (128M) [application/octet-stream] Saving to: 'tabby-1.0.209-linux-x64.AppImage' tabby-1.0.209-linux-x64.AppImage 100%[=================================================================================================================================>] 127.90M 7.66MB/s in 19s 2025-05-28 02:16:22 (6.67 MB/s) - 'tabby-1.0.209-linux-x64.AppImage' saved [134111099/134111099] root@srv771551:~# wget https://github.com/Eugeny/tabby/releases/download/v1.0.209/tabby-1.0.209-linux-x64.AppImage --2025-05-28 02:16:26-- https://github.com/Eugeny/tabby/releases/download/v1.0.209/tabby-1.0.209-linux-x64.AppImage Resolving github.com (github.com)... 20.207.73.82 Connecting to github.com (github.com)|20.207.73.82|:443... connected. HTTP request sent, awaiting response... 302 Found Location: https://objects.githubusercontent.com/github-production-release-asset-2e65be/77213120/6838174f-aae8-46a8-875f-4adc31805938?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=releaseassetproduction%2F20250528%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250528T021602Z&X-Amz-Expires=300&X-Amz-Signature=366b06d105ad23c750d2b24dfa63c80757d01eaaa1a2827052e5bdca604a9001&X-Amz-SignedHeaders=host&response-content-disposition=attachment%3B%20filename%3Dtabby-1.0.209-linux-x64.AppImage&response-content-type=application%2Foctet-stream [following] --2025-05-28 02:16:26-- https://objects.githubusercontent.com/github-production-release-asset-2e65be/77213120/6838174f-aae8-46a8-875f-4adc31805938?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=releaseassetproduction%2F20250528%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250528T021602Z&X-Amz-Expires=300&X-Amz-Signature=366b06d105ad23c750d2b24dfa63c80757d01eaaa1a2827052e5bdca604a9001&X-Amz-SignedHeaders=host&response-content-disposition=attachment%3B%20filename%3Dtabby-1.0.209-linux-x64.AppImage&response-content-type=application%2Foctet-stream Resolving objects.githubusercontent.com (objects.githubusercontent.com)... 185.199.110.133, 185.199.111.133, 185.199.109.133, ... Connecting to objects.githubusercontent.com (objects.githubusercontent.com)|185.199.110.133|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 134111099 (128M) [application/octet-stream] Saving to: 'tabby-1.0.209-linux-x64.AppImage.1' tabby-1.0.209-linux-x64.AppImage.1 100%[=================================================================================================================================>] 127.90M 84.0MB/s in 1.5s 2025-05-28 02:16:29 (84.0 MB/s) - 'tabby-1.0.209-linux-x64.AppImage.1' saved [134111099/134111099] root@srv771551:~# chmod +x *.AppImage root@srv771551:~# which tabby # 应输出安装路径 tabby --version Command 'tabby' not found, did you mean: command 'ktabby' from deb kgames (2.3-1) Try: apt install <deb name> root@srv771551:~# ^C root@srv771551:~# ^C root@srv771551:~# wget https://github.com/Eugeny/tabby/releases/download/v1.0.209/tabby-1.0.209-linux-x64.AppImage --2025-05-28 02:18:08-- https://github.com/Eugeny/tabby/releases/download/v1.0.209/tabby-1.0.209-linux-x64.AppImage Resolving github.com (github.com)... 20.207.73.82 Connecting to github.com (github.com)|20.207.73.82|:443... connected. HTTP request sent, awaiting response... 302 Found Location: https://objects.githubusercontent.com/github-production-release-asset-2e65be/77213120/6838174f-aae8-46a8-875f-4adc31805938?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=releaseassetproduction%2F20250528%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250528T021602Z&X-Amz-Expires=300&X-Amz-Signature=366b06d105ad23c750d2b24dfa63c80757d01eaaa1a2827052e5bdca604a9001&X-Amz-SignedHeaders=host&response-content-disposition=attachment%3B%20filename%3Dtabby-1.0.209-linux-x64.AppImage&response-content-type=application%2Foctet-stream [following] --2025-05-28 02:18:08-- https://objects.githubusercontent.com/github-production-release-asset-2e65be/77213120/6838174f-aae8-46a8-875f-4adc31805938?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=releaseassetproduction%2F20250528%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250528T021602Z&X-Amz-Expires=300&X-Amz-Signature=366b06d105ad23c750d2b24dfa63c80757d01eaaa1a2827052e5bdca604a9001&X-Amz-SignedHeaders=host&response-content-disposition=attachment%3B%20filename%3Dtabby-1.0.209-linux-x64.AppImage&response-content-type=application%2Foctet-stream Resolving objects.githubusercontent.com (objects.githubusercontent.com)... 185.199.110.133, 185.199.108.133, 185.199.111.133, ... Connecting to objects.githubusercontent.com (objects.githubusercontent.com)|185.199.110.133|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 134111099 (128M) [application/octet-stream] Saving to: 'tabby-1.0.209-linux-x64.AppImage.2' tabby-1.0.209-linux-x64.AppImage.2 100%[=================================================================================================================================>] 127.90M 49.2MB/s in 2.6s 2025-05-28 02:18:12 (49.2 MB/s) - 'tabby-1.0.209-linux-x64.AppImage.2' saved [134111099/134111099] root@srv771551:~# sudo apt install libfuse2 # AppImage运行必需的基础依赖[^2] Reading package lists... Done Building dependency tree... Done Reading state information... Done Note, selecting 'libfuse2t64' instead of 'libfuse2' libfuse2t64 is already the newest version (2.9.9-8.1build1). libfuse2t64 set to manually installed. 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. root@srv771551:~# chmod +x tabby-*.AppImage root@srv771551:~# ./tabby-*.AppImage [0528/021833.570631:FATAL:electron_main_delegate.cc(294)] Running as root without --no-sandbox is not supported. See https://crbug.com/638180. Trace/breakpoint trap (core dumped) root@srv771551:~# # 将Tabby添加到应用程序菜单 sudo mkdir -p /usr/local/bin sudo mv tabby-*.AppImage /usr/local/bin/tabby root@srv771551:~# sudo apt install fuse3 Reading package lists... Done Building dependency tree... Done Reading state information... Done fuse3 is already the newest version (3.14.0-5build1). fuse3 set to manually installed. 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. root@srv771551:~# sha256sum tabby-*.AppImage | grep $(curl -sL 发布页 | grep sha256) sha256sum: 'tabby-*.AppImage': No such file or directory Usage: grep [OPTION]... PATTERNS [FILE]... Try 'grep --help' for more information. root@srv771551:~# ^C root@srv771551:~# ^C root@srv771551:~#

最新推荐

recommend-type

langchain4j-anthropic-spring-boot-starter-0.31.0.jar中文文档.zip

1、压缩文件中包含: 中文文档、jar包下载地址、Maven依赖、Gradle依赖、源代码下载地址。 2、使用方法: 解压最外层zip,再解压其中的zip包,双击 【index.html】 文件,即可用浏览器打开、进行查看。 3、特殊说明: (1)本文档为人性化翻译,精心制作,请放心使用; (2)只翻译了该翻译的内容,如:注释、说明、描述、用法讲解 等; (3)不该翻译的内容保持原样,如:类名、方法名、包名、类型、关键字、代码 等。 4、温馨提示: (1)为了防止解压后路径太长导致浏览器无法打开,推荐在解压时选择“解压到当前文件夹”(放心,自带文件夹,文件不会散落一地); (2)有时,一套Java组件会有多个jar,所以在下载前,请仔细阅读本篇描述,以确保这就是你需要的文件。 5、本文件关键字: jar中文文档.zip,java,jar包,Maven,第三方jar包,组件,开源组件,第三方组件,Gradle,中文API文档,手册,开发手册,使用手册,参考手册。
recommend-type

TMS320F28335电机控制程序详解:BLDC、PMSM无感有感及异步VF源代码与开发资料

TMS320F28335这款高性能数字信号处理器(DSP)在电机控制领域的应用,涵盖了BLDC(无刷直流电机)、PMSM(永磁同步电机)的无感有感控制以及异步VF(变频调速)程序。文章不仅解释了各类型的电机控制原理,还提供了完整的开发资料,包括源代码、原理图和说明文档,帮助读者深入了解其工作原理和编程技巧。 适合人群:从事电机控制系统开发的技术人员,尤其是对TMS320F28335感兴趣的工程师。 使用场景及目标:适用于需要掌握TMS320F28335在不同电机控制应用场景下具体实现方法的专业人士,旨在提高他们对该微控制器的理解和实际操作能力。 其他说明:文中提供的开发资料为读者提供了从硬件到软件的全面支持,有助于加速项目开发进程并提升系统性能。
recommend-type

基于爬山搜索法的风力发电MPPT控制Simulink仿真:定步长与变步长算法性能对比 - 爬山搜索法 最新版

基于爬山搜索法的风力发电最大功率点追踪(MPPT)控制的Simulink仿真模型,重点比较了定步长和变步长算法在不同风速条件下的表现。文中展示了两种算法的具体实现方法及其优缺点。定步长算法虽然结构简单、计算量小,但在风速突变时响应较慢,存在明显的稳态振荡。相比之下,变步长算法能够根据功率变化动态调整步长,表现出更快的响应速度和更高的精度,尤其在风速突变时优势明显。实验数据显示,变步长算法在风速从8m/s突增至10m/s的情况下,仅用0.3秒即可稳定,功率波动范围仅为±15W,而定步长算法则需要0.8秒,功率波动达到±35W。 适合人群:从事风力发电研究的技术人员、对MPPT控制感兴趣的工程技术人员以及相关专业的高校师生。 使用场景及目标:适用于风力发电系统的设计与优化,特别是需要提高系统响应速度和精度的场合。目标是在不同风速条件下,选择合适的MPPT算法以最大化风能利用率。 其他说明:文章还讨论了定步长算法在风速平稳情况下的优势,提出了根据不同应用场景灵活选择或组合使用这两种算法的建议。
recommend-type

Visual C++.NET编程技术实战指南

根据提供的文件信息,可以生成以下知识点: ### Visual C++.NET编程技术体验 #### 第2章 定制窗口 - **设置窗口风格**:介绍了如何通过编程自定义窗口的外观和行为。包括改变窗口的标题栏、边框样式、大小和位置等。这通常涉及到Windows API中的`SetWindowLong`和`SetClassLong`函数。 - **创建六边形窗口**:展示了如何创建一个具有特殊形状边界的窗口,这类窗口不遵循标准的矩形形状。它需要使用`SetWindowRgn`函数设置窗口的区域。 - **创建异形窗口**:扩展了定制窗口的内容,提供了创建非标准形状窗口的方法。这可能需要创建一个不规则的窗口区域,并将其应用到窗口上。 #### 第3章 菜单和控制条高级应用 - **菜单编程**:讲解了如何创建和修改菜单项,处理用户与菜单的交互事件,以及动态地添加或删除菜单项。 - **工具栏编程**:阐述了如何使用工具栏,包括如何创建工具栏按钮、分配事件处理函数,并实现工具栏按钮的响应逻辑。 - **状态栏编程**:介绍了状态栏的创建、添加不同类型的指示器(如文本、进度条等)以及状态信息的显示更新。 - **为工具栏添加皮肤**:展示了如何为工具栏提供更加丰富的视觉效果,通常涉及到第三方的控件库或是自定义的绘图代码。 #### 第5章 系统编程 - **操作注册表**:解释了Windows注册表的结构和如何通过程序对其进行读写操作,这对于配置软件和管理软件设置非常关键。 - **系统托盘编程**:讲解了如何在系统托盘区域创建图标,并实现最小化到托盘、从托盘恢复窗口的功能。 - **鼠标钩子程序**:介绍了钩子(Hook)技术,特别是鼠标钩子,如何拦截和处理系统中的鼠标事件。 - **文件分割器**:提供了如何将文件分割成多个部分,并且能够重新组合文件的技术示例。 #### 第6章 多文档/多视图编程 - **单文档多视**:展示了如何在同一个文档中创建多个视图,这在文档编辑软件中非常常见。 #### 第7章 对话框高级应用 - **实现无模式对话框**:介绍了无模式对话框的概念及其应用场景,以及如何实现和管理无模式对话框。 - **使用模式属性表及向导属性表**:讲解了属性表的创建和使用方法,以及如何通过向导性质的对话框引导用户完成多步骤的任务。 - **鼠标敏感文字**:提供了如何实现点击文字触发特定事件的功能,这在阅读器和编辑器应用中很有用。 #### 第8章 GDI+图形编程 - **图像浏览器**:通过图像浏览器示例,展示了GDI+在图像处理和展示中的应用,包括图像的加载、显示以及基本的图像操作。 #### 第9章 多线程编程 - **使用全局变量通信**:介绍了在多线程环境下使用全局变量进行线程间通信的方法和注意事项。 - **使用Windows消息通信**:讲解了通过消息队列在不同线程间传递信息的技术,包括发送消息和处理消息。 - **使用CriticalSection对象**:阐述了如何使用临界区(CriticalSection)对象防止多个线程同时访问同一资源。 - **使用Mutex对象**:介绍了互斥锁(Mutex)的使用,用以同步线程对共享资源的访问,保证资源的安全。 - **使用Semaphore对象**:解释了信号量(Semaphore)对象的使用,它允许一个资源由指定数量的线程同时访问。 #### 第10章 DLL编程 - **创建和使用Win32 DLL**:介绍了如何创建和链接Win32动态链接库(DLL),以及如何在其他程序中使用这些DLL。 - **创建和使用MFC DLL**:详细说明了如何创建和使用基于MFC的动态链接库,适用于需要使用MFC类库的场景。 #### 第11章 ATL编程 - **简单的非属性化ATL项目**:讲解了ATL(Active Template Library)的基础使用方法,创建一个不使用属性化组件的简单项目。 - **使用ATL开发COM组件**:详细阐述了使用ATL开发COM组件的步骤,包括创建接口、实现类以及注册组件。 #### 第12章 STL编程 - **list编程**:介绍了STL(标准模板库)中的list容器的使用,讲解了如何使用list实现复杂数据结构的管理。 #### 第13章 网络编程 - **网上聊天应用程序**:提供了实现基本聊天功能的示例代码,包括客户端和服务器的通信逻辑。 - **简单的网页浏览器**:演示了如何创建一个简单的Web浏览器程序,涉及到网络通信和HTML解析。 - **ISAPI服务器扩展编程**:介绍了如何开发ISAPI(Internet Server API)服务器扩展来扩展IIS(Internet Information Services)的功能。 #### 第14章 数据库编程 - **ODBC数据库编程**:解释了ODBC(开放数据库互联)的概念,并提供了使用ODBC API进行数据库访问的示例。 - **ADO编程**:介绍了ADO(ActiveX Data Objects)技术,讲解了如何使用ADO进行数据库的增删改查等操作。 #### 第15章 HTML帮助系统编程 - **创建HTML帮助文件**:讲解了如何创建HTML帮助文件(.chm),这是微软帮助系统的一种形式。 - **区分上下文帮助**:提供了在应用程序中实现上下文敏感帮助的技术示例。 #### 第16章 安装和部署项目 - **用安装程序向导创建安装项目**:介绍了如何使用Visual Studio中的安装和部署项目向导,快速创建安装程序。 - **部署应用程序**:阐述了部署应用程序到目标系统的过程,以及如何确保应用程序的正确安装和运行。 以上内容涵盖了Visual C++.NET编程技术体验一书中涉及的各个技术点,并对每个章节中的示例做了详细的说明。这些知识点对于理解和掌握Visual C++.NET编程非常重要,并能够帮助读者在实际项目开发中应用这些技术。
recommend-type

HarmonyOS内核深度探秘:优化自由行旅游系统的策略

# 1. HarmonyOS内核概述与旅游系统需求分析 ## 1.1 HarmonyOS内核概述 HarmonyOS(鸿蒙操作系统)是由华为开发的一个分布式操作系统,旨在提供跨多种设备的无缝体验。其内核设计理念强调高效、安全和模块化,以微内核架构和分布式能力
recommend-type

tkinter模块所有控件

<think>好的,我现在需要回答用户关于Tkinter模块中所有控件列表的问题。首先,我需要回忆Tkinter的基础知识,确保自己了解各个控件的名称和用途。用户希望得到所有支持的控件列表,所以必须全面覆盖,不能遗漏。 首先,我应该从Tkinter的标准控件开始。常见的控件包括Label、Button、Entry这些基础部件。然后是Frame,用于布局,还有Canvas用于绘图。接下来是Checkbutton、Radiobutton,这些属于选择类控件。Listbox和Scrollbar通常一起使用,处理滚动内容。还有Scale(滑块)、Spinbox、Menu、Menubutton这些可能
recommend-type

局域网五子棋游戏:娱乐与聊天的完美结合

标题“网络五子棋”和描述“适合于局域网之间娱乐和聊天!”以及标签“五子棋 网络”所涉及的知识点主要围绕着五子棋游戏的网络版本及其在局域网中的应用。以下是详细的知识点: 1. 五子棋游戏概述: 五子棋是一种两人对弈的纯策略型棋类游戏,又称为连珠、五子连线等。游戏的目标是在一个15x15的棋盘上,通过先后放置黑白棋子,使得任意一方先形成连续五个同色棋子的一方获胜。五子棋的规则简单,但策略丰富,适合各年龄段的玩家。 2. 网络五子棋的意义: 网络五子棋是指可以在互联网或局域网中连接进行对弈的五子棋游戏版本。通过网络版本,玩家不必在同一地点即可进行游戏,突破了空间限制,满足了现代人们快节奏生活的需求,同时也为玩家们提供了与不同对手切磋交流的机会。 3. 局域网通信原理: 局域网(Local Area Network,LAN)是一种覆盖较小范围如家庭、学校、实验室或单一建筑内的计算机网络。它通过有线或无线的方式连接网络内的设备,允许用户共享资源如打印机和文件,以及进行游戏和通信。局域网内的计算机之间可以通过网络协议进行通信。 4. 网络五子棋的工作方式: 在局域网中玩五子棋,通常需要一个客户端程序(如五子棋.exe)和一个服务器程序。客户端负责显示游戏界面、接受用户输入、发送落子请求给服务器,而服务器负责维护游戏状态、处理玩家的游戏逻辑和落子请求。当一方玩家落子时,客户端将该信息发送到服务器,服务器确认无误后将更新后的棋盘状态传回给所有客户端,更新显示。 5. 五子棋.exe程序: 五子棋.exe是一个可执行程序,它使得用户可以在个人计算机上安装并运行五子棋游戏。该程序可能包含了游戏的图形界面、人工智能算法(如果支持单机对战AI的话)、网络通信模块以及游戏规则的实现。 6. put.wav文件: put.wav是一个声音文件,很可能用于在游戏进行时提供声音反馈,比如落子声。在网络环境中,声音文件可能被用于提升玩家的游戏体验,尤其是在局域网多人游戏场景中。当玩家落子时,系统会播放.wav文件中的声音,为游戏增添互动性和趣味性。 7. 网络五子棋的技术要求: 为了确保多人在线游戏的顺利进行,网络五子棋需要具备一些基本的技术要求,包括但不限于稳定的网络连接、高效的数据传输协议(如TCP/IP)、以及安全的数据加密措施(如果需要的话)。此外,还需要有一个良好的用户界面设计来提供直观和舒适的用户体验。 8. 社交与娱乐: 网络五子棋除了是一个娱乐游戏外,它还具有社交功能。玩家可以通过游戏内的聊天系统进行交流,分享经验和策略,甚至通过网络寻找新的朋友。这使得网络五子棋不仅是一个个人娱乐工具,同时也是一种社交活动。 总结来说,网络五子棋结合了五子棋游戏的传统魅力和现代网络技术,使得不同地区的玩家能够在局域网内进行娱乐和聊天,既丰富了人们的娱乐生活,又加强了人际交流。而实现这一切的基础在于客户端程序的设计、服务器端的稳定运行、局域网的高效通信,以及音效文件增强的游戏体验。
recommend-type

自由行旅游新篇章:HarmonyOS技术融合与系统架构深度解析

# 1. HarmonyOS技术概述 ## 1.1 HarmonyOS的起源与发展 HarmonyOS(鸿蒙操作系统)由华为公司开发,旨在构建全场景分布式OS,以应对不同设备间的互联问题。自从2019年首次发布以来,HarmonyOS迅速成长,并迅速应用于智能手机、平板、智能穿戴、车载设备等多种平台。该系
recommend-type

足底支撑相到达73%是什么问题

<think>好的,我现在要处理用户的问题:“足底支撑相达到73%可能的原因及影响”。首先,需要理解用户提到的“足底支撑相73%”具体指什么。足底支撑相通常指步态周期中脚与地面接触的时间占比,正常范围大约在60%-62%左右。如果达到73%,明显超出正常范围,可能意味着步态异常。 接下来,需要确定可能的原因。可能的原因包括生物力学异常,比如足弓异常(扁平足或高弓足)、踝关节活动度受限,或者肌肉力量不足,特别是小腿和足部肌肉。另外,神经系统疾病如脑卒中或帕金森病可能影响步态控制。骨骼关节问题如关节炎、髋膝关节病变也可能导致支撑时间延长。还有代偿性步态,比如因疼痛或受伤而改变步态模式。 然后是
recommend-type

宾馆预约系统开发与优化建议

宾馆预约系统是一个典型的在线服务应用,它允许用户通过互联网平台预定宾馆房间。这种系统通常包含多个模块,比如用户界面、房态管理、预订处理、支付处理和客户评价等。从技术层面来看,构建一个宾馆预约系统涉及到众多的IT知识和技术细节,下面将详细说明。 ### 标题知识点 - 宾馆预约系统 #### 1. 系统架构设计 宾馆预约系统作为一个完整的应用,首先需要进行系统架构设计,决定其采用的软件架构模式,如B/S架构或C/S架构。此外,系统设计还需要考虑扩展性、可用性、安全性和维护性。一般会采用三层架构,包括表示层、业务逻辑层和数据访问层。 #### 2. 前端开发 前端开发主要负责用户界面的设计与实现,包括用户注册、登录、房间搜索、预订流程、支付确认、用户反馈等功能的页面展示和交互设计。常用的前端技术栈有HTML, CSS, JavaScript, 以及各种前端框架如React, Vue.js或Angular。 #### 3. 后端开发 后端开发主要负责处理业务逻辑,包括用户管理、房间状态管理、订单处理等。后端技术包括但不限于Java (使用Spring Boot框架), Python (使用Django或Flask框架), PHP (使用Laravel框架)等。 #### 4. 数据库设计 数据库设计对系统的性能和可扩展性至关重要。宾馆预约系统可能需要设计的数据库表包括用户信息表、房间信息表、预订记录表、支付信息表等。常用的数据库系统有MySQL, PostgreSQL, MongoDB等。 #### 5. 网络安全 网络安全是宾馆预约系统的重要考虑因素,包括数据加密、用户认证授权、防止SQL注入、XSS攻击、CSRF攻击等。系统需要实现安全的认证机制,比如OAuth或JWT。 #### 6. 云服务和服务器部署 现代的宾馆预约系统可能部署在云平台上,如AWS, Azure, 腾讯云或阿里云。在云平台上,系统可以按需分配资源,提高系统的稳定性和弹性。 #### 7. 付款接口集成 支付模块需要集成第三方支付接口,如支付宝、微信支付、PayPal等,需要处理支付请求、支付状态确认、退款等业务。 #### 8. 接口设计与微服务 系统可能采用RESTful API或GraphQL等接口设计方式,提供服务的微服务化,以支持不同设备和服务的接入。 ### 描述知识点 - 这是我个人自己做的 请大家帮忙修改哦 #### 个人项目经验与团队合作 描述中的这句话暗示了该宾馆预约系统可能是由一个个人开发者创建的。个人开发和团队合作在软件开发流程中有着显著的不同。个人开发者需要关注的方面包括项目管理、需求分析、代码质量保证、测试和部署等。而在团队合作中,每个成员会承担不同的职责,需要有效的沟通和协作。 #### 用户反馈与迭代 描述还暗示了该系统目前处于需要外部反馈和修改的阶段。这表明系统可能还处于开发或测试阶段,需要通过用户的实际使用反馈来不断迭代改进。 ### 标签知识点 - 200 #### 未提供信息 “200”这个标签可能指的是HTTP状态码中表示请求成功(OK)的200状态码。但是,由于没有提供更多的上下文信息,无法进一步分析其在本例中的具体含义。 ### 压缩包子文件的文件名称列表知识点 - 1111 #### 文件命名与管理 “1111”这个文件名称可能是一个版本号、日期标记或者是一个简单的标识符。文件命名应当遵循一定的规则,以确保文件的可追溯性和管理的便利性。在软件开发过程中,合理组织文件和版本控制(如使用Git)是必不可少的。 综上所述,宾馆预约系统的开发是一项复杂的工程,它涉及前后端的开发、数据库设计、系统安全、接口设计等多个方面。开发者在开发过程中需要不断学习和应用各类IT知识,以确保系统能够安全、高效、稳定地运行。而对于个人开发项目,如何合理利用有限资源、高效地管理和优化项目过程也是至关重要的。