R数据显示不全,最大行数options(max.print=1000000)

博客内容展示了代码 options(max.print=1000000),这是在R语言中设置最大打印数量的操作,属于信息技术领域中数据分析相关内容。
options(max.print=1000000) 
import base64 import bz2 import gzip import lzma import zlib import tkinter as tk from tkinter import ttk, scrolledtext, filedialog, messagebox import os import threading import queue import re import ast import chardet from datetime import datetime import keyword class PythonDecoderApp: def __init__(self, root): self.root = root self.root.title("Python代码解码工具") self.root.geometry("1000x800") self.root.minsize(800, 600) # 设置主题和字体 self.setup_theme() # 创建任务队列用于线程间通信 self.queue = queue.Queue() self.running = True # 创建主框架 self.main_frame = ttk.Frame(root, padding="10") self.main_frame.pack(fill=tk.BOTH, expand=True) # 创建输入区域 self.create_input_area() # 创建选项区域 self.create_options_area() # 创建输出区域 - 专注于Python代码显示 self.create_python_output_area() # 创建状态栏 self.status_var = tk.StringVar() self.status_var.set("就绪") self.status_bar = ttk.Label(root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W) self.status_bar.pack(side=tk.BOTTOM, fill=tk.X) # 存储解码结果的变量 self.raw_data = None self.text_data = None self.python_code = None # 启动队列处理线程 self.root.after(100, self.process_queue) # 窗口关闭处理 self.root.protocol("WM_DELETE_WINDOW", self.on_close) def setup_theme(self): """设置界面主题""" try: self.style = ttk.Style() # 尝试使用clam主题,如果可用则使用默认主题 available_themes = self.style.theme_names() if 'clam' in available_themes: self.style.theme_use('clam') # 配置样式 self.style.configure("TLabel", font=("Consolas", 10)) self.style.configure("TButton", font=("Consolas", 10)) self.style.configure("TCombobox", font=("Consolas", 10)) self.style.configure("Title.TLabel", font=("Consolas", 12, "bold")) except Exception as e: print(f"主题设置失败: {e}") def create_input_area(self): """创建输入区域""" input_frame = ttk.LabelFrame(self.main_frame, text="输入编码数据", padding="10") input_frame.pack(fill=tk.X, pady=(0, 10)) # 输入格式说明 format_label = ttk.Label(input_frame, text="支持Base64、Base16、Base32编码,支持bz2、gzip、lzma、zlib压缩格式", foreground="blue") format_label.pack(anchor=tk.W, pady=(0, 5)) # 输入文本框 self.input_text = scrolledtext.ScrolledText(input_frame, wrap=tk.WORD, height=8, font=("Consolas", 10)) self.input_text.pack(fill=tk.BOTH, expand=True, pady=(0, 5)) # 输入控制按钮 input_buttons = ttk.Frame(input_frame) input_buttons.pack(fill=tk.X) ttk.Button(input_buttons, text="从文件加载", command=self.load_from_file).pack(side=tk.LEFT, padx=5) ttk.Button(input_buttons, text="清空输入", command=lambda: self.input_text.delete(1.0, tk.END)).pack(side=tk.RIGHT, padx=5) ttk.Button(input_buttons, text="加载示例", command=self.load_example).pack(side=tk.LEFT, padx=5) ttk.Button(input_buttons, text="验证Base64", command=self.validate_base64).pack(side=tk.LEFT, padx=5) def create_options_area(self): """创建选项区域""" options_frame = ttk.LabelFrame(self.main_frame, text="解码选项", padding="10") options_frame.pack(fill=tk.X, pady=(0, 10)) # 第一行:解码模式 mode_frame = ttk.Frame(options_frame) mode_frame.pack(fill=tk.X, pady=(0, 10)) ttk.Label(mode_frame, text="解码模式:").pack(side=tk.LEFT, padx=5) self.mode_var = tk.StringVar(value="auto") auto_radio = ttk.Radiobutton(mode_frame, text="自动检测 (推荐)", variable=self.mode_var, value="auto", command=self.toggle_method) auto_radio.pack(side=tk.LEFT, padx=5) manual_radio = ttk.Radiobutton(mode_frame, text="手动选择", variable=self.mode_var, value="manual", command=self.toggle_method) manual_radio.pack(side=tk.LEFT, padx=5) # 第二行:解码方法 method_frame = ttk.Frame(options_frame) method_frame.pack(fill=tk.X, pady=(0, 10)) ttk.Label(method_frame, text="解码方法:").pack(side=tk.LEFT, padx=5) self.method_var = tk.StringVar(value="base64") self.method_combobox = ttk.Combobox(method_frame, textvariable=self.method_var, state="disabled", values=["base64", "base16", "base32", "bz2", "gzip", "lzma", "zlib"]) self.method_combobox.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True) # 第三行:高级选项 advanced_frame = ttk.Frame(options_frame) advanced_frame.pack(fill=tk.X, pady=(0, 10)) # Python代码优化选项 self.python_detect_var = tk.BooleanVar(value=True) ttk.Checkbutton(advanced_frame, text="自动检测并优化Python代码", variable=self.python_detect_var).pack(side=tk.LEFT, padx=5) # 多层解码选项 self.multilayer_var = tk.BooleanVar(value=True) ttk.Checkbutton(advanced_frame, text="启用多层解码", variable=self.multilayer_var).pack(side=tk.LEFT, padx=5) # 语法高亮选项 self.syntax_highlight_var = tk.BooleanVar(value=True) ttk.Checkbutton(advanced_frame, text="启用语法高亮", variable=self.syntax_highlight_var).pack(side=tk.LEFT, padx=5) # 执行按钮 self.decode_button = ttk.Button(options_frame, text="执行解码", command=self.start_decoding) self.decode_button.pack(fill=tk.X, pady=5) def create_python_output_area(self): """创建专门用于显示Python代码的输出区域""" # 主输出框架 output_frame = ttk.LabelFrame(self.main_frame, text="Python代码输出", padding="10") output_frame.pack(fill=tk.BOTH, expand=True) # 顶部信息栏 info_frame = ttk.Frame(output_frame) info_frame.pack(fill=tk.X, pady=(0, 5)) # 结果标签 self.result_label = ttk.Label(info_frame, text="等待解码...") self.result_label.pack(side=tk.LEFT, padx=5) # 代码信息标签 self.code_info_label = ttk.Label(info_frame, text="", foreground="blue") self.code_info_label.pack(side=tk.RIGHT, padx=5) # 代码编辑区域 - 使用Text控件以便后续添加语法高亮 text_frame = ttk.Frame(output_frame) text_frame.pack(fill=tk.BOTH, expand=True) # 添加行号 self.line_numbers = tk.Text(text_frame, width=4, padx=5, pady=5, takefocus=0, border=0, background='lightgray', state='disabled') self.line_numbers.pack(side=tk.LEFT, fill=tk.Y) # 代码显示区域 self.code_text = scrolledtext.ScrolledText( text_frame, wrap=tk.WORD, font=("Consolas", 11), undo=True, maxundo=-1 ) self.code_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 配置标签用于语法高亮 self.code_text.tag_configure("keyword", foreground="blue", font=("Consolas", 11, "bold")) self.code_text.tag_configure("string", foreground="green") self.code_text.tag_configure("comment", foreground="gray") self.code_text.tag_configure("function", foreground="purple") self.code_text.tag_configure("class", foreground="darkblue", font=("Consolas", 11, "bold")) self.code_text.tag_configure("number", foreground="red") # 底部按钮栏 button_frame = ttk.Frame(output_frame) button_frame.pack(fill=tk.X, pady=(5, 0)) # 左侧按钮组 left_buttons = ttk.Frame(button_frame) left_buttons.pack(side=tk.LEFT) ttk.Button(left_buttons, text="运行代码", command=self.run_code).pack(side=tk.LEFT, padx=5) ttk.Button(left_buttons, text="保存为.py文件", command=self.save_python_file).pack(side=tk.LEFT, padx=5) ttk.Button(left_buttons, text="复制代码", command=self.copy_code).pack(side=tk.LEFT, padx=5) # 右侧按钮组 right_buttons = ttk.Frame(button_frame) right_buttons.pack(side=tk.RIGHT) ttk.Button(right_buttons, text="格式化代码", command=self.format_code).pack(side=tk.LEFT, padx=5) ttk.Button(right_buttons, text="清空输出", command=self.clear_output).pack(side=tk.LEFT, padx=5) def toggle_method(self): """切换解码模式""" if self.mode_var.get() == "manual": self.method_combobox.config(state="readonly") else: self.method_combobox.config(state="disabled") def validate_base64(self): """验证Base64格式""" input_data = self.input_text.get(1.0, tk.END).strip() if not input_data: messagebox.showwarning("警告", "请输入要验证的数据") return # 简单的Base64验证 try: # 清理数据 cleaned = re.sub(r'[^A-Za-z0-9+/=]', '', input_data) if len(cleaned) % 4 != 0: cleaned += '=' * (4 - len(cleaned) % 4) base64.b64decode(cleaned, validate=True) messagebox.showinfo("验证结果", "Base64格式有效") except Exception as e: messagebox.showerror("验证结果", f"Base64格式无效: {str(e)}") def load_example(self): """加载示例数据""" # 创建一个Base64编码的Python代码示例 example_code = ''' import base64 import zlib def hello_world(): """一个简单的Hello World函数""" print("Hello, World!") return True class ExampleClass: """示例类""" def __init__(self, name): self.name = name def greet(self): print(f"Hello, {self.name}!") if __name__ == "__main__": hello_world() obj = ExampleClass("Python") obj.greet() ''' # 压缩并编码示例代码 compressed = zlib.compress(example_code.encode('utf-8')) encoded = base64.b64encode(compressed).decode('utf-8') self.input_text.delete(1.0, tk.END) self.input_text.insert(tk.END, encoded) self.status_var.set("已加载示例数据") def load_from_file(self): """从文件加载数据""" filename = filedialog.askopenfilename( title="选择包含编码数据的文件", filetypes=[("文本文件", "*.txt"), ("Python文件", "*.py"), ("所有文件", "*.*")] ) if filename: self.status_var.set(f"正在加载 {os.path.basename(filename)}...") def load_file_thread(): try: max_size = 10 * 1024 * 1024 # 10MB file_size = os.path.getsize(filename) if file_size > max_size: self.queue.put(("show_error", f"文件过大(超过10MB),请选择较小的文件")) return # 尝试多种编码读取文件 encodings = ['utf-8', 'gbk', 'utf-16', 'latin-1'] content = None for encoding in encodings: try: with open(filename, 'r', encoding=encoding) as f: content = f.read() break except UnicodeDecodeError: continue if content is None: # 如果文本编码都失败,尝试二进制读取 with open(filename, 'rb') as f: binary_data = f.read() detected = chardet.detect(binary_data) encoding = detected.get('encoding', 'utf-8') try: content = binary_data.decode(encoding) except: content = binary_data.decode('utf-8', errors='ignore') self.queue.put(("update_status", f"已从 {os.path.basename(filename)} 加载数据")) self.root.after(0, lambda: self.update_input_text(content, filename)) except Exception as e: self.queue.put(("show_error", f"加载文件失败: {str(e)}")) self.queue.put(("update_status", "文件加载失败")) threading.Thread(target=load_file_thread, daemon=True).start() def update_input_text(self, content, filename): """更新输入文本框""" self.input_text.delete(1.0, tk.END) self.input_text.insert(tk.END, content) self.status_var.set(f"已从 {os.path.basename(filename)} 加载数据") def clear_output(self): """清空输出区域""" self.code_text.config(state=tk.NORMAL) self.code_text.delete(1.0, tk.END) self.code_text.config(state=tk.DISABLED) self.result_label.config(text="等待解码...") self.code_info_label.config(text="") self.update_line_numbers() self.raw_data = None self.text_data = None self.python_code = None self.status_var.set("输出已清空") def update_line_numbers(self): """更新行号显示""" self.line_numbers.config(state=tk.NORMAL) self.line_numbers.delete(1.0, tk.END) # 获取代码行数 code_content = self.code_text.get(1.0, tk.END) line_count = code_content.count('\n') # 添加行号 if line_count > 0: line_numbers_text = '\n'.join(str(i) for i in range(1, line_count + 1)) self.line_numbers.insert(1.0, line_numbers_text) self.line_numbers.config(state=tk.DISABLED') def apply_syntax_highlighting(self): """应用Python语法高亮""" if not self.syntax_highlight_var.get(): return # 清除所有现有标签 for tag in self.code_text.tag_names(): if tag != "sel": # 保留选择标签 self.code_text.tag_remove(tag, "1.0", tk.END) # 获取代码内容 code_content = self.code_text.get(1.0, tk.END) # Python关键字 python_keywords = keyword.kwlist + ['print', 'len', 'range', 'type', 'str', 'int', 'float', 'list', 'dict', 'tuple', 'set'] # 高亮关键字 for kw in python_keywords: start_idx = "1.0" while True: start_idx = self.code_text.search(r'\b' + re.escape(kw) + r'\b', start_idx, stopindex=tk.END, regexp=True) if not start_idx: break end_idx = f"{start_idx}+{len(kw)}c" self.code_text.tag_add("keyword", start_idx, end_idx) start_idx = end_idx # 高亮字符串 self.highlight_pattern(r'(\"\"\"|\'\'\'|\"|\')(.*?)(\"\"\"|\'\'\'|\"|\')', "string") # 高亮注释 self.highlight_pattern(r'#.*$', "comment", regexp=True) # 高亮函数定义 self.highlight_pattern(r'def\s+(\w+)\s*\(', "function", regexp=True, group=1) # 高亮类定义 self.highlight_pattern(r'class\s+(\w+)\s*\(?', "class", regexp=True, group=1) # 高亮数字 self.highlight_pattern(r'\b\d+\b', "number", regexp=True) def highlight_pattern(self, pattern, tag, regexp=False, group=0): """高亮匹配模式的文本""" start_idx = "1.0" while True: if regexp: start_idx = self.code_text.search(pattern, start_idx, stopindex=tk.END, regexp=True) else: start_idx = self.code_text.search(pattern, start_idx, stopindex=tk.END) if not start_idx: break if group > 0: # 获取匹配的文本内容 match_start = self.code_text.index(start_idx) content = self.code_text.get(start_idx, f"{start_idx}+100c") match_obj = re.search(pattern, content) if match_obj and len(match_obj.groups()) >= group: matched_text = match_obj.group(group) end_idx = f"{start_idx}+{len(matched_text)}c" self.code_text.tag_add(tag, start_idx, end_idx) start_idx = end_idx else: start_idx = f"{start_idx}+1c" else: end_idx = self.code_text.index(f"{start_idx}+{len(pattern)}c") self.code_text.tag_add(tag, start_idx, end_idx) start_idx = end_idx def copy_code(self): """复制代码到剪贴板""" try: self.code_text.config(state=tk.NORMAL) content = self.code_text.get(1.0, tk.END).strip() self.code_text.config(state=tk.DISABLED) if content: self.root.clipboard_clear() self.root.clipboard_append(content) self.status_var.set("代码已复制到剪贴板") else: messagebox.showwarning("警告", "没有代码可复制") except Exception as e: messagebox.showerror("错误", f"复制失败: {str(e)}") def save_python_file(self): """保存Python代码到文件""" if not self.python_code and not self.text_data: messagebox.showwarning("警告", "没有可保存的Python代码,请先执行解码") return filename = filedialog.asksaveasfilename( title="保存Python代码", defaultextension=".py", filetypes=[("Python文件", "*.py"), ("所有文件", "*.*")] ) if filename: def save_thread(): try: content = self.python_code if self.python_code else self.text_data with open(filename, 'w', encoding='utf-8') as f: f.write(content) self.queue.put(("update_status", f"Python代码已保存到 {os.path.basename(filename)}")) self.queue.put(("show_info", f"Python代码已保存到 {filename}")) except Exception as e: self.queue.put(("show_error", f"保存文件失败: {str(e)}")) self.queue.put(("update_status", "保存文件失败")) threading.Thread(target=save_thread, daemon=True).start() def format_code(self): """格式化Python代码""" if not self.python_code and not self.text_data: messagebox.showwarning("警告", "没有可格式化的代码") return try: # 简单的代码格式化 - 调整缩进和空格 code = self.python_code if self.python_code else self.text_data # 分割行并处理缩进 lines = code.split('\n') formatted_lines = [] indent_level = 0 for line in lines: stripped = line.strip() if not stripped: # 空行 formatted_lines.append('') continue # 减少缩进级别(如果行以这些关键字结尾) if stripped.startswith(('return', 'break', 'continue', 'pass')): indent_level = max(0, indent_level - 1) # 添加当前缩进 formatted_line = ' ' * indent_level + stripped formatted_lines.append(formatted_line) # 增加缩进级别(如果行以这些关键字结尾) if stripped.endswith((':', ':\\')) and not stripped.startswith('#'): indent_level += 1 formatted_code = '\n'.join(formatted_lines) # 更新显示 self.code_text.config(state=tk.NORMAL) self.code_text.delete(1.0, tk.END) self.code_text.insert(tk.END, formatted_code) self.code_text.config(state=tk.DISABLED) self.update_line_numbers() self.apply_syntax_highlighting() self.status_var.set("代码已格式化") except Exception as e: messagebox.showerror("错误", f"格式化代码失败: {str(e)}") def run_code(self): """尝试运行Python代码(在安环境中)""" if not self.python_code and not self.text_data: messagebox.showwarning("警告", "没有可运行的代码") return code = self.python_code if self.python_code else self.text_data # 安检查 - 禁止危险操作 dangerous_patterns = [ r'__import__\s*\(', r'eval\s*\(', r'exec\s*\(', r'compile\s*\(', r'open\s*\(', r'file\s*\(', r'os\.', r'sys\.', r'subprocess\.', r'import\s+os', r'import\s+sys', r'import\s+subprocess' ] for pattern in dangerous_patterns: if re.search(pattern, code, re.IGNORECASE): messagebox.showerror("安警告", "代码包含可能危险的操作,无法在沙箱中运行") return # 在对话框中显示执行结果 try: # 创建新窗口显示执行结果 result_window = tk.Toplevel(self.root) result_window.title("代码执行结果") result_window.geometry("600x400") result_window.transient(self.root) result_window.grab_set() # 创建输出区域 output_text = scrolledtext.ScrolledText(result_window, wrap=tk.WORD, font=("Consolas", 10)) output_text.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 重定向输出 import io import sys from contextlib import redirect_stdout, redirect_stderr old_stdout = sys.stdout old_stderr = sys.stderr output_buffer = io.StringIO() try: sys.stdout = output_buffer sys.stderr = output_buffer # 执行代码 exec(code) # 恢复标准输出 sys.stdout = old_stdout sys.stderr = old_stderr # 显示结果 output_text.insert(tk.END, output_buffer.getvalue()) if not output_buffer.getvalue(): output_text.insert(tk.END, "代码执行完成,无输出") output_text.config(state=tk.DISABLED) except Exception as e: # 恢复标准输出 sys.stdout = old_stdout sys.stderr = old_stderr output_text.insert(tk.END, f"执行错误: {str(e)}") output_text.config(state=tk.DISABLED) except Exception as e: messagebox.showerror("错误", f"无法执行代码: {str(e)}") # Python代码检测与优化 def is_python_code(self, text): """检测文本是否为Python代码""" if not text or not text.strip(): return False # 先尝试使用AST解析验证语法 try: # 移除可能的编码声明和特殊字符 cleaned_text = re.sub(r'^#.*coding[:=]\s*[\w-]+', '', text, flags=re.IGNORECASE) cleaned_text = re.sub(r'[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]', '', cleaned_text) # 尝试解析为Python AST ast.parse(cleaned_text) return True except SyntaxError: # 如果AST解析失败,使用特征模式匹配 pass # Python特征模式 python_patterns = [ r'^import\s+\w+', # 导入语句 r'^from\s+\w+\s+import', # 从模块导入 r'^def\s+\w+\s*\(', # 函数定义 r'^class\s+\w+', # 类定义 r'^\s*if\s+.*?:', # if语句 r'^\s*for\s+.*?:', # for循环 r'^\s*while\s+.*?:', # while循环 r'print\s*\(', # print函数 r'^\s*#', # 注释 ] # 检查是否有多个Python特征 matches = 0 lines = text.split('\n')[:20] # 检查前20行 for line in lines: for pattern in python_patterns: if re.search(pattern, line.strip(), re.IGNORECASE): matches += 1 break return matches >= 3 # 至少匹配3个模式 def clean_python_code(self, text): """清理和优化Python代码显示""" if not text: return text # 移除可能的控制字符 cleaned = re.sub(r'[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]', '', text) # 修复常见的编码问题 lines = cleaned.split('\n') cleaned_lines = [] for line in lines: # 移除行首行尾的异常空白字符 cleaned_line = line.rstrip() if cleaned_line: cleaned_lines.append(cleaned_line) result = '\n'.join(cleaned_lines) # 尝试规范化缩进 try: # 检测缩进类型 spaces = sum(1 for c in result if c == ' ') tabs = sum(1 for c in result if c == '\t') if tabs > spaces and spaces > 0: # 混合缩进,转换为空格 result = result.replace('\t', ' ') elif spaces > tabs and tabs > 0: # 混合缩进,转换为制表符 result = re.sub(r' ', '\t', result) except: pass # 如果处理失败,保持原样 return result # 解码功能实现 def clean_input_data(self, data): """清理输入数据,移除可能的非编码字符""" if isinstance(data, str): # 移除空格、换行等可能干扰解码的字符 cleaned = re.sub(r'[^A-Za-z0-9+/=]', '', data) return cleaned return data def decode_base64(self, encoded_data): """Base64解码""" try: # 确保输入是字节类型 if isinstance(encoded_data, str): encoded_data = encoded_data.encode('utf-8') # 添加必要的填充 padding = 4 - len(encoded_data) % 4 if padding != 4: encoded_data += b'=' * padding result = base64.b64decode(encoded_data, validate=True) return result, "Base64解码成功" except Exception as e: return None, f"Base64解码失败: {str(e)}" def decode_base16(self, encoded_data): """Base16解码""" try: if isinstance(encoded_data, str): encoded_data = encoded_data.encode('utf-8') result = base64.b16decode(encoded_data, casefold=True) return result, "Base16解码成功" except Exception as e: return None, f"Base16解码失败: {str(e)}" def decode_base32(self, encoded_data): """Base32解码""" try: if isinstance(encoded_data, str): encoded_data = encoded_data.encode('utf-8') result = base64.b32decode(encoded_data, casefold=True) return result, "Base32解码成功" except Exception as e: return None, f"Base32解码失败: {str(e)}" def decompress_bz2(self, data): """bz2解压缩""" try: return bz2.decompress(data), "bz2解压缩成功" except Exception as e: return None, f"bz2解压缩失败: {str(e)}" def decompress_gzip(self, data): """gzip解压缩""" try: return gzip.decompress(data), "gzip解压缩成功" except Exception as e: return None, f"gzip解压缩失败: {str(e)}" def decompress_lzma(self, data): """lzma解压缩""" try: return lzma.decompress(data), "lzma解压缩成功" except Exception as e: return None, f"lzma解压缩失败: {str(e)}" def decompress_zlib(self, data): """zlib解压缩""" try: return zlib.decompress(data), "zlib解压缩成功" except Exception as e: return None, f"zlib解压缩失败: {str(e)}" def decode_text(self, data): """将二进制数据解码为文本""" # 优先尝试Python常用编码 encodings = ['utf-8', 'gbk', 'utf-16', 'latin-1', 'gb2312', 'ascii'] for encoding in encodings: try: return data.decode(encoding), f"使用{encoding}编码解析成功" except UnicodeDecodeError: continue # 使用chardet检测编码 try: detected = chardet.detect(data) encoding = detected.get('encoding', 'utf-8') confidence = detected.get('confidence', 0) if confidence > 0.5: # 置信度大于50% try: return data.decode(encoding), f"使用检测到的{encoding}编码解析成功(置信度:{confidence:.2f})" except UnicodeDecodeError: pass except: pass # 最后尝试忽略错误解码 try: return data.decode('utf-8', errors='ignore'), "使用utf-8编码解析(忽略错误)" except: return None, "无法解析为文本" def multilayer_decode(self, data, max_depth=5): """多层解码,递归解压缩直到无法继续""" if max_depth <= 0: return data, "达到最大递归深度" current_data = data decode_log = [] # 尝试各种解压缩方法 decompressors = [ ("bz2", self.decompress_bz2), ("gzip", self.decompress_gzip), ("lzma", self.decompress_lzma), ("zlib", self.decompress_zlib) ] for name, decompressor in decompressors: result, msg = decompressor(current_data) if result: decode_log.append(f"第{6-max_depth}层: {msg}") # 递归尝试进一步解压缩 deeper_result, deeper_log = self.multilayer_decode(result, max_depth-1) if deeper_result != result: # 如果进一步解压缩成功 decode_log.extend(deeper_log) return deeper_result, decode_log else: return result, decode_log return current_data, decode_log def auto_decode(self, encoded_data): """自动解码流程""" # 1. 清理输入数据 cleaned_data = self.clean_input_data(encoded_data) # 2. 尝试各种编码解码 decoders = [ ("base64", self.decode_base64), ("base16", self.decode_base16), ("base32", self.decode_base32) ] decoded_data = None decode_log = [] for name, decoder in decoders: result, msg = decoder(cleaned_data) if result: decoded_data = result decode_log.append(f"{msg}") break if not decoded_data: return None, "无法使用任何编码格式解码输入数据" # 3. 多层解压缩(如果启用) if self.multilayer_var.get(): final_data, decompress_log = self.multilayer_decode(decoded_data) decode_log.extend(decompress_log) else: final_data = decoded_data # 4. 尝试解析为文本 text, text_msg = self.decode_text(final_data) decode_log.append(text_msg) if text: return (final_data, text), "; ".join(decode_log) return (final_data, None), "; ".join(decode_log) def manual_decode(self, encoded_data, method): """手动选择解码方法""" # 清理输入数据 cleaned_data = self.clean_input_data(encoded_data) if method in ["base64", "base16", "base32"]: # 编码解码 if method == "base64": result, msg = self.decode_base64(cleaned_data) elif method == "base16": result, msg = self.decode_base16(cleaned_data) elif method == "base32": result, msg = self.decode_base32(cleaned_data) if not result: return None, f"解码失败: {msg}" # 尝试解压缩(如果看起来是压缩数据) if len(result) > 0: # 检查是否是压缩格式 decompressors = [ ("bz2", self.decompress_bz2), ("gzip", self.decompress_gzip), ("lzma", self.decompress_lzma), ("zlib", self.decompress_zlib) ] for name, decompressor in decompressors: decompressed, decompress_msg = decompressor(result) if decompressed: result = decompressed msg += f"; {decompress_msg}" break text, text_msg = self.decode_text(result) return (result, text), f"{msg}; {text_msg}" else: # 先进行Base64解码,然后解压缩 decoded_b64, msg = self.decode_base64(cleaned_data) if not decoded_b64: return None, f"解码失败: {msg}" if method == "bz2": result, decompress_msg = self.decompress_bz2(decoded_b64) elif method == "gzip": result, decompress_msg = self.decompress_gzip(decoded_b64) elif method == "lzma": result, decompress_msg = self.decompress_lzma(decoded_b64) elif method == "zlib": result, decompress_msg = self.decompress_zlib(decoded_b64) else: return None, "无效的解码方法" if not result: return None, f"解码失败: {decompress_msg}" text, text_msg = self.decode_text(result) return (result, text), f"{msg}; {decompress_msg}; {text_msg}" def update_code_info(self, code): """更新代码信息""" if not code: self.code_info_label.config(text="") return lines = code.split('\n') line_count = len(lines) char_count = len(code) # 统计Python元素 function_count = len(re.findall(r'def\s+\w+\s*\(', code)) class_count = len(re.findall(r'class\s+\w+', code)) import_count = len(re.findall(r'import\s+\w+', code)) + len(re.findall(r'from\s+\w+\s+import', code)) info_text = f"行数: {line_count} | 字符: {char_count}" if function_count > 0: info_text += f" | 函数: {function_count}" if class_count > 0: info_text += f" | 类: {class_count}" if import_count > 0: info_text += f" | 导入: {import_count}" self.code_info_label.config(text=info_text) def process_queue(self): """处理后台线程发送到UI线程的任务""" while not self.queue.empty(): try: task = self.queue.get_nowait() task_type = task[0] if task_type == "update_status": self.status_var.set(task[1]) elif task_type == "update_result": self.result_label.config(text=task[1]) self.status_var.set("解码完成") elif task_type == "display_output": self.code_text.config(state=tk.NORMAL) self.code_text.delete(1.0, tk.END) self.code_text.insert(tk.END, task[1]) self.code_text.config(state=tk.DISABLED) self.update_line_numbers() self.apply_syntax_highlighting() self.update_code_info(task[1]) elif task_type == "show_error": messagebox.showerror("错误", task[1]) elif task_type == "show_info": messagebox.showinfo("提示", task[1]) except Exception as e: print(f"处理队列任务出错: {e}") if self.running: self.root.after(100, self.process_queue) def start_decoding(self): """开始解码过程""" self.decode_button.config(state=tk.DISABLED) self.clear_output() input_data = self.input_text.get(1.0, tk.END).strip() if not input_data: messagebox.showwarning("警告", "请输入编码数据") self.decode_button.config(state=tk.NORMAL) return def decode_thread(): try: self.queue.put(("update_status", "正在解码...")) if self.mode_var.get() == "auto": result, msg = self.auto_decode(input_data) else: method = self.method_var.get() result, msg = self.manual_decode(input_data, method) if result: self.raw_data, self.text_data = result self.python_code = None # 检测并优化Python代码 if self.text_data and self.python_detect_var.get(): if self.is_python_code(self.text_data): self.python_code = self.clean_python_code(self.text_data) msg += "; 检测到Python代码并已优化显示" else: msg += "; 未检测到Python代码特征" # 准备显示内容 display_text = "" if self.python_code: display_text = self.python_code elif self.text_data: if len(self.text_data) > 100000: display_text = self.text_data[:100000] + "\n\n...内容过长,已截断显示..." else: display_text = self.text_data else: display_text = f"# 二进制数据({len(self.raw_data)}字节)\n" display_text += "# 无法解析为Python代码\n" display_text += f"# 前100字节(十六进制):\n# " display_text += ' '.join(f'{b:02x}' for b in self.raw_data[:100]) self.queue.put(("update_result", msg)) self.queue.put(("display_output", display_text)) else: self.queue.put(("show_error", msg)) self.queue.put(("update_status", "解码失败")) except Exception as e: error_msg = f"解码过程中发生错误: {str(e)}" self.queue.put(("show_error", error_msg)) self.queue.put(("update_status", "解码过程中发生错误")) finally: self.root.after(0, lambda: self.decode_button.config(state=tk.NORMAL)) threading.Thread(target=decode_thread, daemon=True).start() if __name__ == "__main__": root = tk.Tk() app = PythonDecoderApp(root) root.mainloop()修改错误
09-23
优化打印速度 import iconv from 'iconv-lite'; // 需先安装:npm install iconv-lite class BluetoothPrinter { constructor() { this.deviceId = null; this.serviceId = null; this.characteristicId = null; this.isConnected = false; this.debugLogs = []; this.bluetoothAdapterOpened = false; this.isInitializing = false; this.isConnecting = false; // 连接中状态锁 this.printerEncoding = 'GBK'; // 兼容性更好的编码 this.CHUNK_SIZE = 32; // 最小分片确保稳定 this.setupConnectionMonitor(); } // 添加调试日志 addDebugLog(message) { const timestamp = new Date().toLocaleTimeString(); this.debugLogs.unshift(`[${timestamp}] ${message}`); if (this.debugLogs.length > 50) { this.debugLogs = this.debugLogs.slice(0, 50); } console.log(`[蓝牙打印] ${message}`); } // 获取调试日志 getDebugLogs() { return this.debugLogs; } // 清空调试日志 clearDebugLogs() { this.debugLogs = []; } // 设置连接状态监控 setupConnectionMonitor() { uni.onBLEConnectionStateChange((res) => { this.addDebugLog(`连接状态变化: 设备 ${res.deviceId} 连接状态: ${res.connected}`); // 同步内部连接状态 if (res.connected) { this.isConnected = true; this.deviceId = res.deviceId; } else if (this.deviceId === res.deviceId) { this.isConnected = false; this.deviceId = null; this.serviceId = null; this.characteristicId = null; uni.showToast({ title: '打印机连接已断开', icon: 'none', duration: 2000 }); } }); } // 检查蓝牙适配器状态 async checkBluetoothAdapter() { return new Promise((resolve) => { if (this.isInitializing) { this.addDebugLog('蓝牙适配器正在初始化中...'); const checkInit = setInterval(() => { if (!this.isInitializing) { clearInterval(checkInit); this._getAdapterState(resolve); } }, 100); return; } // 有缓存设备ID且适配器已开启,直接返回状态 const cachedDeviceId = uni.getStorageSync('connectedDeviceId'); if (this.bluetoothAdapterOpened && cachedDeviceId) { this.addDebugLog(`存在缓存设备ID: ${cachedDeviceId},直接返回适配器状态`); this._getAdapterState(resolve); return; } if (!this.bluetoothAdapterOpened) { this.initBluetooth().then(() => { this._getAdapterState(resolve); }).catch(() => { this._getAdapterState(resolve); }); return; } this._getAdapterState(resolve); }); } // 内部方法:实际获取蓝牙适配器状态 _getAdapterState(resolve) { uni.getBluetoothAdapterState({ success: (res) => { this.addDebugLog(`蓝牙适配器状态: 可用=${res.available}, 开启=${res.discovering}`); resolve(res); }, fail: (err) => { this.addDebugLog(`获取蓝牙适配器状态失败: ${err.errMsg}`); resolve(null); } }); } // 初始化蓝牙适配器(有缓存则关闭旧连接) async initBluetooth() { if (this.isInitializing) { this.addDebugLog('蓝牙适配器正在初始化中,请勿重复调用'); return false; } try { this.isInitializing = true; this.addDebugLog('开始初始化蓝牙适配器'); // 检查本地是否有缓存的已连接设备ID const cachedDeviceId = uni.getStorageSync('connectedDeviceId'); if (cachedDeviceId) { this.addDebugLog(`发现缓存设备ID: ${cachedDeviceId},跳过关闭旧连接步骤`); } else { // 无缓存时,才关闭旧连接 this.addDebugLog('无缓存设备,尝试关闭旧的蓝牙连接'); try { await new Promise((resolve) => { uni.closeBluetoothAdapter({ success: resolve, fail: resolve // 失败也继续 }); }); this.addDebugLog('旧连接关闭完成'); } catch (e) { this.addDebugLog('关闭旧适配器失败(可能未开启)'); } } // 打开蓝牙适配器 const res = await new Promise((resolve, reject) => { uni.openBluetoothAdapter({ success: (res) => { this.addDebugLog('蓝牙适配器初始化成功'); this.bluetoothAdapterOpened = true; resolve(res); }, fail: (err) => { this.addDebugLog(`蓝牙适配器初始化失败: ${err.errMsg}`); reject(err); } }); }); return true; } catch (error) { this.addDebugLog(`蓝牙初始化失败: ${error.errMsg || error.message}`); let errorMessage = '蓝牙初始化失败'; if (error.errCode === 10001) { errorMessage = '蓝牙适配器可用,请检查手机蓝牙是否开启'; } else if (error.errMsg && error.errMsg.includes('not init')) { errorMessage = '蓝牙未初始化,请稍后重试'; } uni.showToast({ title: errorMessage, icon: 'none', duration: 3000 }); return false; } finally { this.isInitializing = false; } } // 权限检查 async checkPermissions() { try { const systemInfo = uni.getSystemInfoSync(); this.addDebugLog(`当前平台: ${systemInfo.platform}, 系统版本: ${systemInfo.system}`); // 微信小程序平台 if (systemInfo.platform === 'devtools' || systemInfo.platform === 'ios' || systemInfo.platform === 'android') { try { await new Promise((resolve, reject) => { wx.authorize({ scope: 'scope.bluetooth', success: () => { this.addDebugLog('微信小程序蓝牙权限授权成功'); resolve(true); }, fail: (err) => { this.addDebugLog(`微信小程序蓝牙权限授权失败: ${err.errMsg}`); if (err.errMsg.includes('auth deny')) { uni.showModal({ title: '权限申请', content: '需要蓝牙权限才能连接打印机,请在设置中开启', confirmText: '去设置', success: (res) => { if (res.confirm) { wx.openSetting(); } } }); } resolve(false); } }); }); } catch (e) { this.addDebugLog(`微信小程序权限申请异常: ${e.message}`); } } this.addDebugLog('权限检查通过'); return true; } catch (error) { this.addDebugLog(`权限检查失败: ${error.message}`); uni.showToast({ title: '权限检查失败,请确保已开启蓝牙权限', icon: 'none', duration: 3000 }); return false; } } // 开始扫描设备 async startScan() { try { this.addDebugLog('开始扫描蓝牙设备'); const adapterState = await this.checkBluetoothAdapter(); if (!adapterState || !adapterState.available) { throw new Error('蓝牙适配器可用'); } const permissionsOk = await this.checkPermissions(); if (!permissionsOk) { throw new Error('缺少必要权限'); } // 先停止可能正在进行的扫描 await this.stopScan(); const res = await new Promise((resolve, reject) => { uni.startBluetoothDevicesDiscovery({ services: [], allowDuplicatesKey: true, interval: 0, success: (res) => { this.addDebugLog('开始扫描设备成功'); resolve(res); }, fail: (err) => { this.addDebugLog(`开始扫描失败: ${err.errMsg}`); reject(err); } }); }); return true; } catch (error) { this.addDebugLog(`开始扫描失败: ${error.errMsg || error.message}`); let errorMessage = '扫描失败'; if (error.errMsg && error.errMsg.includes('not init')) { errorMessage = '蓝牙未初始化,请检查蓝牙是否开启'; } else if (error.errMsg && error.errMsg.includes('already discovering')) { errorMessage = '正在扫描中,请先停止当前扫描'; } uni.showToast({ title: errorMessage, icon: 'none', duration: 3000 }); return false; } } // 停止扫描 async stopScan() { try { if (this.bluetoothAdapterOpened) { const res = await new Promise((resolve, reject) => { uni.stopBluetoothDevicesDiscovery({ success: (res) => { this.addDebugLog('停止扫描成功'); resolve(res); }, fail: (err) => { this.addDebugLog(`停止扫描失败: ${err.errMsg}`); reject(err); } }); }); } else { this.addDebugLog('蓝牙未初始化,无需停止扫描'); } return true; } catch (error) { this.addDebugLog(`停止扫描失败: ${error.errMsg || error.message}`); return false; } } // 设备发现回调 onDeviceFound(callback) { uni.onBluetoothDeviceFound((res) => { const devices = res.devices; this.addDebugLog(`发现 ${devices.length} 个设备`); if (devices.length > 0) { const printers = this.filterPrinters(devices); if (printers.length > 0) { this.addDebugLog(`筛选出 ${printers.length} 个可能的打印机设备`); callback(printers); } else { callback(devices); } } }); } // 筛选打印机设备 filterPrinters(devices) { return devices.filter(device => { if (!device.name && !device.localName) return false; const name = (device.name || '').toUpperCase(); const localName = (device.localName || '').toUpperCase(); const printerKeywords = [ 'PRINTER', 'POS', 'BT-P', 'BLE', 'PRT', 'MPT', 'APEX', 'STAR', 'EPSON', 'ZONERICH', 'SNBC', '打印', '票据', '小票', 'POS机' ]; const isPrinter = printerKeywords.some(keyword => name.includes(keyword) || localName.includes(keyword) ); if (isPrinter) { this.addDebugLog(`发现打印机设备: ${device.name || device.localName} (${device.deviceId})`); } return isPrinter; }); } // 连接设备(核心修复:防重复连接 + 已连接错误捕获) async connectDevice(deviceId, deviceName = '未知设备') { // 1. 防重复调用 if (this.isConnecting) { this.addDebugLog(`正在连接设备中,请勿重复调用(目标设备:${deviceId})`); uni.showToast({ title: '连接中,请稍候...', icon: 'none' }); return false; } // 2. 已连接校验 if (this.isConnected && this.deviceId === deviceId) { this.addDebugLog(`设备 ${deviceName} (${deviceId}) 已处于连接状态`); uni.showToast({ title: '设备已连接', icon: 'success' }); return true; } // 3. 已有其他连接:先断开 if (this.isConnected && this.deviceId !== deviceId) { this.addDebugLog(`当前已连接设备:${this.deviceId},正在断开以连接新设备`); await this.disconnect(true); } try { this.isConnecting = true; this.addDebugLog(`开始连接设备: ${deviceName} (${deviceId})`); await this.stopScan(); uni.showLoading({ title: '连接中...', mask: true }); const adapterState = await this.checkBluetoothAdapter(); if (!adapterState || !adapterState.available) { throw new Error('蓝牙适配器可用'); } // 4. 建立BLE连接(捕获已连接错误) const connectRes = await new Promise((resolve, reject) => { uni.createBLEConnection({ deviceId: deviceId, timeout: 10000, success: (res) => { this.addDebugLog('BLE连接成功'); resolve(res); }, fail: (err) => { this.addDebugLog(`BLE连接失败: ${err.errMsg}`); if (err.errMsg.includes('already connect')) { this.addDebugLog(`设备已连接,忽略错误`); resolve({ errMsg: '设备已连接', code: 0 }); } else { reject(new Error(`连接失败: ${err.errMsg}`)); } } }); }); this.deviceId = deviceId; // 等待连接稳定 await new Promise(resolve => setTimeout(resolve, 1500)); // 获取服务 this.addDebugLog('开始获取服务...'); const services = await this.getServices(deviceId); this.addDebugLog(`发现 ${services.services.length} 个服务`); if (!services.services || services.services.length === 0) { throw new Error('未找到任何服务'); } // 寻找合适的服务 let targetService = null; const commonServiceUUIDs = [ '0000FF00-0000-1000-8000-00805F9B34FB', '0000FF01-0000-1000-8000-00805F9B34FB', '0000FFE0-0000-1000-8000-00805F9B34FB', '0000FFE5-0000-1000-8000-00805F9B34FB', 'E7810A71-73AE-499D-8C15-DAA9EEF5A6E0', '49535343-FE7D-4AE5-8FA9-9FAFD205E455' ]; for (const service of services.services) { const serviceUUID = service.uuid.toUpperCase(); this.addDebugLog(`服务UUID: ${serviceUUID}`); if (commonServiceUUIDs.includes(serviceUUID)) { targetService = service; this.addDebugLog(`找到匹配的服务: ${serviceUUID}`); break; } } if (!targetService) { targetService = services.services[0]; this.addDebugLog(`使用第一个服务: ${targetService.uuid}`); } this.serviceId = targetService.uuid; // 获取特征值 this.addDebugLog(`获取特征值,服务ID: ${this.serviceId}`); const characteristics = await this.getCharacteristics(deviceId, this.serviceId); this.addDebugLog(`发现 ${characteristics.characteristics.length} 个特征值`); if (!characteristics.characteristics || characteristics.characteristics.length === 0) { throw new Error('未找到任何特征值'); } // 寻找可写的特征值 let writeCharacteristic = null; const writeCharacteristics = characteristics.characteristics.filter(char => { const props = char.properties; return props.write || props.writeWithoutResponse; }); this.addDebugLog(`找到 ${writeCharacteristics.length} 个可写特征值`); if (writeCharacteristics.length > 0) { writeCharacteristics.forEach((char, index) => { this.addDebugLog( `可写特征值 ${index + 1}: ${char.uuid}, 属性: ${JSON.stringify(char.properties)}`); }); writeCharacteristic = writeCharacteristics.find(char => char.properties.write) || writeCharacteristics[0]; } if (!writeCharacteristic) { throw new Error('未找到可写的特征值'); } this.characteristicId = writeCharacteristic.uuid; this.isConnected = true; // 初始化打印机 await this.initPrinter(); // 更新缓存 uni.setStorageSync('connectedDeviceId', deviceId); this.addDebugLog(`缓存设备ID: ${deviceId}`); this.addDebugLog('连接流程完成'); uni.hideLoading(); uni.showToast({ title: '连接成功', icon: 'success', duration: 2000 }); return true; } catch (error) { uni.hideLoading(); this.addDebugLog(`连接设备失败: ${error.message}`); if (this.deviceId && !this.isConnected) { try { await this.disconnect(true); } catch (e) { this.addDebugLog(`断开连接失败: ${e.message}`); } } let errorMessage = error.message; if (error.message.includes('10009')) errorMessage = '设备连接超时,请重试'; else if (error.message.includes('10012')) errorMessage = '设备连接被拒绝'; else if (error.message.includes('not init')) errorMessage = '蓝牙未初始化'; uni.showToast({ title: errorMessage, icon: 'none', duration: 3000 }); return false; } finally { this.isConnecting = false; } } // 自动重连方法 async autoReconnect(deviceId, deviceName) { // 已连接则跳过 if (this.isConnected && this.deviceId === deviceId) { this.addDebugLog(`设备 ${deviceName} 已连接,无需自动重连`); return true; } // 正在连接中则跳过 if (this.isConnecting) { this.addDebugLog(`正在连接其他设备,跳过自动重连`); return false; } this.addDebugLog(`尝试自动重连设备: ${deviceName} (${deviceId})`); return this.connectDevice(deviceId, deviceName); } // 初始化打印机 async initPrinter() { try { this.addDebugLog('开始初始化打印机参数'); // 初始化指令集 const initCommands = [ [0x1B, 0x40], // 初始化打印机 [0x1B, 0x74, this.getEncodingCommand()], // 设置编码 [0x1B, 0x33, 0x10], // 行间距8点 [0x1B, 0x21, 0x00], // 恢复默认字体 [0x1B, 0x4D, 0x00] // 字体A ]; // 发送初始化指令 for (const cmd of initCommands) { await this.writeRawData(cmd); await new Promise(resolve => setTimeout(resolve, 10)); } this.addDebugLog('打印机初始化完成'); return true; } catch (error) { this.addDebugLog(`打印机初始化失败: ${error.message}`); return false; } } // 根据编码获取指令参数 getEncodingCommand() { switch (this.printerEncoding.toUpperCase()) { case 'GB2312': return 0x00; case 'GBK': return 0x01; case 'BIG5': return 0x02; case 'UTF-8': return 0x03; default: return 0x00; } } // 获取服务 getServices(deviceId) { return new Promise((resolve, reject) => { if (!this.bluetoothAdapterOpened) { reject(new Error('蓝牙适配器未初始化')); return; } uni.getBLEDeviceServices({ deviceId: deviceId, success: resolve, fail: (err) => { this.addDebugLog(`获取服务失败: ${err.errMsg}`); reject(err); } }); }); } // 获取特征值 getCharacteristics(deviceId, serviceId) { return new Promise((resolve, reject) => { if (!this.bluetoothAdapterOpened) { reject(new Error('蓝牙适配器未初始化')); return; } uni.getBLEDeviceCharacteristics({ deviceId: deviceId, serviceId: serviceId, success: resolve, fail: (err) => { this.addDebugLog(`获取特征值失败: ${err.errMsg}`); reject(err); } }); }); } // 断开连接(支持切换设备时清除缓存) async disconnect(isSwitchDevice = false) { if (this.deviceId) { this.addDebugLog(`断开连接: ${this.deviceId}(切换设备:${isSwitchDevice})`); // 切换设备时清除缓存 if (!isSwitchDevice) { uni.removeStorageSync('connectedDeviceId'); this.addDebugLog(`已清除缓存设备ID: ${this.deviceId}`); } if (this.bluetoothAdapterOpened) { try { await new Promise((resolve, reject) => { uni.closeBLEConnection({ deviceId: this.deviceId, success: resolve, fail: (err) => { if (err.errMsg.includes('not connect')) { this.addDebugLog(`设备未连接,忽略断开错误`); resolve(); } else { reject(err); } } }); }); this.addDebugLog('断开连接成功'); } catch (error) { this.addDebugLog(`断开连接失败: ${error.errMsg || error.message}`); } } } this.deviceId = null; this.serviceId = null; this.characteristicId = null; this.isConnected = false; } // 写入原始字节数据 async writeRawData(dataArray) { if (!this.isConnected || !this.deviceId || !this.serviceId || !this.characteristicId) { throw new Error('打印机未连接'); } if (!this.bluetoothAdapterOpened) { throw new Error('蓝牙适配器未初始化'); } try { const arrayBuffer = new Uint8Array(dataArray).buffer; await new Promise((resolve, reject) => { uni.writeBLECharacteristicValue({ deviceId: this.deviceId, serviceId: this.serviceId, characteristicId: this.characteristicId, value: arrayBuffer, success: () => { this.addDebugLog(`写入原始数据成功: [${dataArray.join(', ')}]`); resolve(); }, fail: (err) => { this.addDebugLog(`写入原始数据失败: ${err.errMsg}`); reject(err); } }); }); return true; } catch (error) { throw new Error(`写入失败: ${error.errMsg || error.message}`); } } // 核心打印方法 async printText(text, options = {}) { const defaultOptions = { addNewline: true, cutPaper: false, align: 'left', bold: false, underline: false }; const opts = { ...defaultOptions, ...options }; if (!text) { this.addDebugLog('打印内容为空'); return false; } try { this.addDebugLog(`准备打印文本: ${text.substr(0, 20)}... 配置: ${JSON.stringify(opts)}`); // 设置格式(减少间隔) await this.setPrintFormat(opts); // 编码转换 const encodedData = this.encodeText(text); // 分片传输(增大分片到128) const chunks = this.splitIntoChunks(encodedData); this.addDebugLog(`编码后: ${encodedData.length} 字节,分为 ${chunks.length} 片`); // 优化:减少分片间隔(原30→20ms) for (let i = 0; i < chunks.length; i++) { await this.writeRawData(chunks[i]); // 小优化:最后一片等待 if (i < chunks.length - 1) { await new Promise(resolve => setTimeout(resolve, this.CHUNK_DELAY)); } } // 自动换行 if (opts.addNewline) { await this.writeRawData([0x0D, 0x0A]); } // 切纸(优化:减少等待时间) if (opts.cutPaper) { await new Promise(resolve => setTimeout(resolve, 50)); // 原100→50ms await this.writeRawData([0x1D, 0x56, 0x00]); // 更快的切纸指令(原0x1B,0x69) } // 恢复默认格式(减少间隔) await this.setPrintFormat(defaultOptions); this.addDebugLog('文本打印完成'); return true; } catch (error) { this.addDebugLog(`打印失败: ${error.message}`); let errorMessage = '打印失败'; if (error.message.includes('not init')) errorMessage = '蓝牙未初始化'; else if (error.message.includes('disconnected')) { errorMessage = '设备已断开连接'; this.isConnected = false; } uni.showToast({ title: errorMessage, icon: 'none' }); return false; } } // 设置打印格式 async setPrintFormat(options) { const commands = []; // 对齐方式 switch (options.align) { case 'center': commands.push([0x1B, 0x61, 0x01]); break; case 'right': commands.push([0x1B, 0x61, 0x02]); break; default: commands.push([0x1B, 0x61, 0x00]); } // 加粗 commands.push([0x1B, 0x45, options.bold ? 0x01 : 0x00]); // 下划线 commands.push([0x1B, 0x2D, options.underline ? 0x01 : 0x00]); // 发送指令 for (const cmd of commands) { await this.writeRawData(cmd); await new Promise(resolve => setTimeout(resolve, 20)); } this.addDebugLog(`设置打印格式: ${JSON.stringify(options)}`); } // 编码转换 encodeText(text) { try { const buffer = iconv.encode(text, this.printerEncoding); return Array.from(buffer); } catch (error) { this.addDebugLog(`编码转换失败: ${error.message}`); return Array.from(iconv.encode(text, 'GB2312')); } } // 数据分片 splitIntoChunks(data, chunkSize = this.CHUNK_SIZE) { const chunks = []; for (let i = 0; i < data.length; i += chunkSize) { chunks.push(data.slice(i, i + chunkSize)); } return chunks; } // 兼容旧接口 async writeData(data) { if (typeof data === 'string') { return this.printText(data); } if (Array.isArray(data) && data.every(item => typeof item === 'number')) { const chunks = this.splitIntoChunks(data); this.addDebugLog(`字节数组长度: ${data.length},分为 ${chunks.length} 片传输`); return this.printWithChunks(chunks); } this.addDebugLog('writeData:支持的数据格式'); return false; } // 分片打印方法 async printWithChunks(chunks) { try { for (let i = 0; i < chunks.length; i++) { const chunk = chunks[i]; const success = await this.writeRawData(chunk); if (!success) { this.addDebugLog(`分片 ${i+1} 写入失败`); return false; } await new Promise(resolve => setTimeout(resolve, 20)); this.addDebugLog(`已打印分片 ${i + 1}/${chunks.length}`); } return true; } catch (error) { this.addDebugLog(`分片打印失败: ${error.message}`); return false; } } // 关闭蓝牙适配器 async closeBluetoothAdapter() { try { // 有缓存设备ID,关闭适配器 const cachedDeviceId = uni.getStorageSync('connectedDeviceId'); if (cachedDeviceId) { this.addDebugLog(`存在缓存设备ID: ${cachedDeviceId},关闭蓝牙适配器`); return; } await this.disconnect(); if (this.bluetoothAdapterOpened) { await new Promise((resolve, reject) => { uni.closeBluetoothAdapter({ success: resolve, fail: reject }); }); this.bluetoothAdapterOpened = false; this.addDebugLog('蓝牙适配器已关闭'); } } catch (error) { this.addDebugLog(`关闭蓝牙适配器失败: ${error.message}`); } } // 打印多行文本 async printMultiLine(texts, options = {}) { if (!Array.isArray(texts) || texts.length === 0) { this.addDebugLog('批量打印内容为空'); return false; } try { for (let i = 0; i < texts.length; i++) { const text = texts[i]; await this.printText(text, { ...options, cutPaper: false }); // 优化:最后一行等待 if (i < texts.length - 1) { await new Promise(resolve => setTimeout(resolve, 10)); } } if (options.cutPaper) { await new Promise(resolve => setTimeout(resolve, 50)); await this.writeRawData([0x1D, 0x56, 0x00]); } return true; } catch (error) { this.addDebugLog(`批量打印失败: ${error.message}`); return false; } } } // 导出单例实例 export default new BluetoothPrinter(); import iconv from 'iconv-lite'; // 必须安装:npm install iconv-lite class PrintCommands { constructor() { this.encoding = 'GBK'; // 与 BluetoothPrinter 编码保持一致 } // 设置编码(确保与打印机支持的编码匹配) setEncoding(encoding) { const validEncodings = ['GBK', 'GB2312', 'UTF-8', 'BIG5']; if (validEncodings.includes(encoding.toUpperCase())) { this.encoding = encoding.toUpperCase(); } else { console.warn(`支持的编码: ${encoding},默认使用 GBK`); this.encoding = 'GBK'; } } // 核心:文本编码转换(使用 iconv-lite,兼容 uni-app) encodeText(text) { try { const buffer = iconv.encode(text, this.encoding); return Array.from(buffer); } catch (error) { console.error('文本编码失败:', error); return Array.from(iconv.encode(text, 'GBK')); // 降级方案 } } // 初始化打印机 init() { return [0x1B, 0x40]; // ESC @ 指令:初始化打印机 } // 设置对齐方式(0:左 1:中 2:右) setAlign(align) { const validAlign = Math.min(Math.max(align, 0), 2); return [0x1B, 0x61, validAlign]; } // 设置字体大小(0:正常 1-7:放大) setFontSize(size) { const validSize = Math.min(Math.max(size, 0), 7); return [0x1D, 0x21, validSize]; } // 设置加粗(true:开启 false:关闭) setBold(enabled) { return [0x1B, 0x45, enabled ? 0x01 : 0x00]; } // 设置下划线(true:开启 false:关闭) setUnderline(enabled) { return [0x1B, 0x2D, enabled ? 0x01 : 0x00]; } // 打印文本(仅文本,无换行) printText(text) { return this.encodeText(text); } // 打印文本并换行 printTextLine(text) { return [...this.encodeText(text), 0x0D, 0x0A]; // CR+LF 强制换行 } // 换行(默认1行,可指定行数) lineFeed(lines = 1) { const commands = []; for (let i = 0; i < lines; i++) { commands.push(0x0D, 0x0A); } return commands; } // 切纸(切纸,适配大部分小票机) cutPaper() { return [0x1D, 0x56, 0x00]; // 兼容型切纸指令,速度更快 } // 蜂鸣器(部分打印机支持) beep() { return [0x1B, 0x42, 0x05, 0x05]; // 蜂鸣 5 次,每次 50ms } // 打印条码(CODE128 格式) printBarcode(data, type = 0x49) { const dataBytes = this.encodeText(data); return [ 0x1D, 0x48, 0x02, // HRI 文本在条码下方 0x1D, 0x77, 0x02, // 条码宽度(3点) 0x1D, 0x68, 0x40, // 条码高度(100点) 0x1D, 0x6B, type, // 条码类型(CODE128) dataBytes.length, // 数据长度 ...dataBytes ]; } // 打印二维码(ESC/POS 标准指令,优化大小) printQRCode(data, size = 5) { // 大小从6→5,提升速度 const dataBytes = this.encodeText(data); const length = dataBytes.length + 3; return [ // 1. 选择QR码模型(模型2) 0x1D, 0x28, 0x6B, 0x04, 0x00, 0x31, 0x41, 0x32, 0x00, // 2. 设置QR码大小(1-16) 0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x43, Math.min(Math.max(size, 1), 16), // 3. 设置纠错等级(H级,容错率30%) 0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x45, 0x30, // 4. 存储QR码数据 0x1D, 0x28, 0x6B, length, 0x00, 0x31, 0x50, 0x30, ...dataBytes, // 5. 打印QR码 0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x51, 0x30 ]; } // 生成测试小票 generateTestReceipt() { const commands = []; // 1. 初始化 + 标题 commands.push(...this.init()); commands.push(...this.setAlign(1), ...this.setBold(true)); commands.push(...this.printTextLine('=== 测试小票 ===')); commands.push(...this.setBold(false), ...this.lineFeed(1)); // 2. 订单信息 commands.push(...this.setAlign(0)); commands.push(...this.printTextLine(`订单号: ${this.generateOrderNumber()}`)); commands.push(...this.printTextLine(`时间: ${new Date().toLocaleString()}`)); commands.push(...this.printTextLine('----------------------------')); commands.push(...this.lineFeed(1)); // 3. 商品列表 commands.push(...this.printTextLine('商品1 ¥10.00')); commands.push(...this.printTextLine('商品2 ¥20.00')); commands.push(...this.printTextLine('商品3 ¥15.00')); commands.push(...this.printTextLine('----------------------------')); commands.push(...this.lineFeed(1)); // 4. 总计 commands.push(...this.setAlign(2), ...this.setBold(true)); commands.push(...this.printTextLine('总计: ¥45.00')); commands.push(...this.setBold(false), ...this.lineFeed(3)); // 5. 结束语 + 切纸 commands.push(...this.setAlign(1)); commands.push(...this.printTextLine('谢谢惠顾!')); commands.push(...this.lineFeed(2)); // 减少走纸行数 commands.push(...this.cutPaper()); return commands; } // 生成订单号(年月日时分秒) generateOrderNumber() { const now = new Date(); const year = now.getFullYear(); const month = String(now.getMonth() + 1).padStart(2, '0'); const day = String(now.getDate()).padStart(2, '0'); const hours = String(now.getHours()).padStart(2, '0'); const minutes = String(now.getMinutes()).padStart(2, '0'); const seconds = String(now.getSeconds()).padStart(2, '0'); return `${year}${month}${day}${hours}${minutes}${seconds}`; } // 生成带二维码的小票 generateQRReceipt(qrData = 'https://www.example.com') { const commands = []; commands.push(...this.init()); commands.push(...this.setAlign(1), ...this.setBold(true)); commands.push(...this.printTextLine('停车场')); commands.push(...this.setBold(false), ...this.lineFeed(2)); commands.push(...this.printQRCode(qrData)); commands.push(...this.lineFeed(2)); commands.push(...this.printTextLine('扫描上方二维码获取更多服务')); commands.push(...this.lineFeed(2)); commands.push(...this.cutPaper()); return commands; } // 打印自定义文本(支持格式配置) generateCustomText(lines) { const commands = []; commands.push(...this.init()); lines.forEach(line => { if (line.align !== undefined) commands.push(...this.setAlign(line.align)); if (line.bold !== undefined) commands.push(...this.setBold(line.bold)); if (line.fontSize !== undefined) commands.push(...this.setFontSize(line.fontSize)); if (line.text) commands.push(...this.printTextLine(line.text)); if (line.feed) commands.push(...this.lineFeed(line.feed)); }); commands.push(...this.lineFeed(2)); commands.push(...this.cutPaper()); return commands; } // 按宽度拆分文本(中文2字符,英文1字符) splitTextByWidth(text, maxChars = 32) { const lines = []; let currentLine = ''; let currentWidth = 0; for (let i = 0; i < text.length; i++) { const char = text[i]; const charWidth = /[\u4e00-\u9fa5\uff00-\uffef]/.test(char) ? 2 : 1; if (currentWidth + charWidth > maxChars) { lines.push(currentLine); currentLine = char; currentWidth = charWidth; } else { currentLine += char; currentWidth += charWidth; } } if (currentLine) lines.push(currentLine); return lines; } // 生成停车小票指令(正常小票,优化速度) generateParkingReceipt(data) { console.log(data); const printerWidth = 64; const commands = []; // 1. 初始化打印机(优化行间距) commands.push(...this.init()); commands.push(...this.setLineSpacing(4)); // 行间距4点,减少走纸时间 // 2. 标题(合并格式指令) commands.push(...this.setAlign(1), ...this.setBold(true)); commands.push(...this.printTextLine(`${data.parkName}`)); commands.push(...this.setBold(false), ...this.lineFeed(1)); // 3. 分割线 commands.push(...this.printTextLine('------------------------------')); commands.push(...this.lineFeed(1)); // 4. 核心信息(批量处理,减少冗余) commands.push(...this.setAlign(0)); const infoLines = [ `车牌号码: ${data.carNo}`, `入场时间: ${data.entryTime}`, `停车地点: ${data.parkLocation}`, `泊位号: ${data.spotNo}`, `停车管理员: ${data.manager}`, `管理员电话: ${data.managerTel}` ]; infoLines.forEach(line => { this.splitTextByWidth(line, printerWidth).forEach(subLine => { commands.push(...this.printTextLine(subLine)); }); }); commands.push(...this.lineFeed(1), ...this.printTextLine('------------------------------')); // 5. 二维码 + 提示语 commands.push(...this.setAlign(1)); commands.push(...this.printQRCode(data.qrUrl || 'https://www.example.com', 5)); commands.push(...this.printTextLine('请微信扫码支付停车费')); commands.push(...this.printTextLine('------------------------------'), ...this.lineFeed(1)); // 6. 欠费提示 if (data.oweNum) { commands.push(...this.setAlign(1), ...this.setBold(true)); commands.push(...this.printTextLine(`历史欠费${data.oweNum}条,合计${data.oweFee}`)); commands.push(...this.setBold(false)); const tipText = '温馨提示:先缴费、后离场、如遇小票或系统问题,避免延迟确认离场,请及时联系管理员终止计时。请自觉缴纳停车费,恶意欠费将依法追缴并纳入城市停车管理征信系统。本停车位为城市公共资源,临时停车点属于公开场合,请在离开前关好车门车窗,妥善保管您的个人物品,贵重物品请随身携带!您所缴纳的费用为城市道路资源占用费,本公司负责车辆挂擦和随车物品保管责任、后果一切自负。争创文明城市人人有责,请勿随地乱丢垃圾。'; this.splitTextByWidth(tipText, printerWidth).forEach(subLine => { commands.push(...this.printTextLine(subLine)); }); } // 7. 切纸(减少走纸行数) commands.push(...this.lineFeed(2)); commands.push(...this.cutPaper()); return commands; } // 生成欠费告知单(优化版) generateParkingReceipts(data) { console.log(data); const printerWidth = 64; const commands = []; // 1. 初始化打印机 commands.push(...this.init()); commands.push(...this.setLineSpacing(4)); // 2. 标题 commands.push(...this.setAlign(1), ...this.setBold(true)); commands.push(...this.printTextLine('停车欠费告知单')); commands.push(...this.setBold(false), ...this.lineFeed(0.5)); // 3. 二维码 + 分割线 commands.push(...this.printTextLine('------------------------------'), ...this.lineFeed(0.5)); commands.push(...this.printQRCode(data.qrUrl || 'https://www.example.com', 5)); commands.push(...this.lineFeed(0.5)); commands.push(...this.printTextLine('请微信扫码支付停车费')); commands.push(...this.printTextLine('------------------------------'), ...this.lineFeed(1)); // 4. 核心内容 commands.push(...this.setAlign(0), ...this.setBold(true)); commands.push(...this.printTextLine(`尊敬的${data.carNumber}车主您好!`)); commands.push(...this.setBold(false)); const oweText = ` 您的爱车已累计拖欠停车费累计订单:${data.oweNum}笔,合计欠费${data.oweFee}元。根据系统审核统计,您的车辆,存在多次停车未缴费记录,欠费金额超法定数额,将会被纳入司法追缴名单,一旦被执行法律流程,会对你个人的征信及生活带来必要的影响,为此请收到本催缴单7天内,尽快补缴相关停车费用。`; this.splitTextByWidth(oweText, printerWidth).forEach(subLine => { commands.push(...this.printTextLine(subLine)); }); // 5. 切纸 commands.push(...this.lineFeed(2)); commands.push(...this.cutPaper()); return commands; } // 设置行间距(0-255) setLineSpacing(spacing = 4) { const validSpacing = Math.min(Math.max(spacing, 0), 255); return [0x1B, 0x33, validSpacing]; } } export default new PrintCommands();
最新发布
11-16
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

C++程序员Carea

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值