欢迎各位兄弟 发布技术文章
这里的技术是共享的
自己亲自做的代码,见 /node-admin/22695
#!/usr/bin/python3 import os import ctypes import tkinter as tk from tkinter import messagebox def add_hosts_entries(): # 定义要添加的hosts条目 new_entries = [ "192.168.1.100 mmmm.aaa.com", "192.168.1.101 wwww3aaa.com", "192.168.1.102 mmmmm.aaa.com", "192.168.1.103 nnnnnn.aaa.com", "192.168.1.104 r.aaa3static.com.com" ] # 获取hosts文件路径 hosts_path = r"C:\Windows\System32\drivers\etc\hosts" try: # 检查管理员权限 if not ctypes.windll.shell32.IsUserAnAdmin(): messagebox.showerror("权限错误", "请以管理员身份运行此程序") return 0 # 检查条目是否已存在 existing_entries = set() if os.path.exists(hosts_path): with open(hosts_path, 'r') as f: for line in f: line = line.strip() if line and not line.startswith('#'): existing_entries.add(line.split('#')[0].strip()) # 添加新条目(跳过已存在的) added = -1 with open(hosts_path, 'a') as f: for entry in new_entries: if entry not in existing_entries: f.write("\n" + entry) added = 1 return added except Exception as e: messagebox.showerror("错误", f"操作失败: {str(e)}") return False if __name__ == "__main__": # 隐藏主Tkinter窗口 root = tk.Tk() root.withdraw() # 执行添加操作 result = add_hosts_entries() # 显示结果弹窗 if result == 1: messagebox.showinfo("完成", "hosts文件更新成功!") elif result == 0: messagebox.showinfo("完成", "未更改hosts文件(无权限)!") elif result == -1: messagebox.showinfo("完成", "未修改hosts文件(条目已存在)") # 其他错误情况已在函数内处理 操作失败: 'gbk' codec can't decode byte 0xbf in position 2: ilegal multibyte sequence
您的代码在PyCharm中运行时遇到的错误是由于文件编码问题导致的。错误信息 'gbk' codec can't decode byte 0xbf
表明Python在尝试用GBK编码读取文件时遇到了非法字节序列。这通常发生在读取非GBK编码的文件时。
以下是修复后的完整代码:( 下面的代码并不完美 ,因为未考虑到hosts文件是GBK编码的情况 )
#!/usr/bin/python3 import os import ctypes import tkinter as tk from tkinter import messagebox def add_hosts_entries(): # 定义要添加的hosts条目 new_entries = [ "192.168.1.100 mmmm.aaa.com", "192.168.1.101 wwww3aaa.com", "192.168.1.102 mmmmm.aaa.com", "192.168.1.103 nnnnnn.aaa.com", "192.168.1.104 r.aaa3static.com.com" ] # 获取hosts文件路径 hosts_path = r"C:\Windows\System32\drivers\etc\hosts" try: # 检查管理员权限 if not ctypes.windll.shell32.IsUserAnAdmin(): messagebox.showerror("权限错误", "请以管理员身份运行此程序") return 0 # 检查条目是否已存在 existing_entries = set() if os.path.exists(hosts_path): # 使用UTF-8编码打开文件 with open(hosts_path, 'r', encoding='utf-8') as f: for line in f: line = line.strip() if line and not line.startswith('#'): # 去除行内注释 clean_line = line.split('#')[0].strip() existing_entries.add(clean_line) # 添加新条目(跳过已存在的) added = -1 # 使用UTF-8编码追加写入 with open(hosts_path, 'a', encoding='utf-8') as f: for entry in new_entries: if entry not in existing_entries: f.write("\n" + entry) added = 1 return added except Exception as e: messagebox.showerror("错误", f"操作失败: {str(e)}") return False if __name__ == "__main__": # 隐藏主Tkinter窗口 root = tk.Tk() root.withdraw() # 执行添加操作 result = add_hosts_entries() # 显示结果弹窗 if result == 1: messagebox.showinfo("完成", "hosts文件更新成功!") elif result == 0: messagebox.showinfo("完成", "未更改hosts文件(无权限)!") elif result == -1: messagebox.showinfo("完成", "未修改hosts文件(条目已存在)") # 其他错误情况已在函数内处理
指定文件编码:
with open(hosts_path, 'r', encoding='utf-8') as f: with open(hosts_path, 'a', encoding='utf-8') as f:
明确使用UTF-8编码打开文件,避免GBK解码错误。
修复条目检查逻辑:
确保正确清理注释内容:
clean_line = line.split('#')[0].strip()
将此代码保存为.py文件
必须右键选择"以管理员身份运行",否则会提示权限错误
程序会自动执行添加操作,并根据结果弹出提示框
如果您的hosts文件使用的是其他编码(如ANSI),可以尝试以下替代方案:
# 替代编码方案 try: with open(hosts_path, 'r', encoding='utf-8') as f: # 读取内容 except UnicodeDecodeError: # 如果UTF-8失败,尝试使用系统默认编码 with open(hosts_path, 'r') as f: # 读取内容
或者更稳健的方式:
# 自动检测编码 import chardet def detect_encoding(file_path): with open(file_path, 'rb') as f: raw_data = f.read() result = chardet.detect(raw_data) return result['encoding'] or 'utf-8' encoding = detect_encoding(hosts_path) with open(hosts_path, 'r', encoding=encoding) as f: # 读取内容
但请注意,对于hosts文件,通常使用UTF-8或ANSI编码,直接使用UTF-8在大多数情况下都能正常工作。
下面是使用替代编码方案的完整实现代码,该代码可以智能处理不同编码的 hosts 文件,并提供更健壮的条目管理功能:
#!/usr/bin/python3 import os import ctypes import tkinter as tk from tkinter import messagebox, ttk, scrolledtext def add_hosts_entries(): # 定义要添加的hosts条目 new_entries = [ "192.168.1.100 mmmm.aaa.com", "192.168.1.101 wwww3aaa.com", "192.168.1.102 mmmmm.aaa.com", "192.168.1.103 nnnnnn.aaa.com", "192.168.1.104 r.aaa3static.com.com" ] # 获取hosts文件路径 hosts_path = r"C:\Windows\System32\drivers\etc\hosts" result = {"added": 0, "skipped": 0, "errors": []} encoding_used = "utf-8" # 默认使用UTF-8编码 try: # 检查管理员权限 if not ctypes.windll.shell32.IsUserAnAdmin(): messagebox.showerror("权限错误", "请以管理员身份运行此程序") return {"status": "error", "message": "权限不足"} # 检查条目是否已存在 existing_entries = set() file_content = [] if os.path.exists(hosts_path): # 替代编码方案:先尝试UTF-8,失败则使用系统默认编码 try: with open(hosts_path, 'r', encoding='utf-8') as f: file_content = f.readlines() encoding_used = "utf-8" except UnicodeDecodeError: try: with open(hosts_path, 'r') as f: # 使用系统默认编码 file_content = f.readlines() encoding_used = "system_default" except Exception as e: result["errors"].append(f"读取文件失败: {str(e)}") return result except Exception as e: result["errors"].append(f"读取文件失败: {str(e)}") return result # 处理每一行 for line in file_content: line = line.strip() if line and not line.lstrip().startswith('#'): # 忽略注释行 # 去除行内注释 clean_line = line.split('#')[0].strip() # 规范化空格(将多个空格替换为单个空格) normalized_line = ' '.join(clean_line.split()) existing_entries.add(normalized_line.lower()) # 不区分大小写 # 添加新条目(跳过已存在的) entries_to_add = [] for entry in new_entries: # 规范化条目 normalized_entry = ' '.join(entry.split()) if normalized_entry.lower() in existing_entries: result["skipped"] += 1 else: entries_to_add.append(normalized_entry) result["added"] += 1 # 如果有需要添加的条目 if entries_to_add: try: # 使用确定的编码追加写入 if encoding_used == "utf-8": with open(hosts_path, 'a', encoding='utf-8') as f: f.write("\n# Added by Hosts Editor\n") for entry in entries_to_add: f.write(entry + "\n") else: with open(hosts_path, 'a') as f: # 使用系统默认编码 f.write("\n# Added by Hosts Editor\n") for entry in entries_to_add: f.write(entry + "\n") except Exception as e: result["errors"].append(f"写入文件失败: {str(e)}") return result except Exception as e: result["errors"].append(f"操作失败: {str(e)}") return result def show_hosts_file(): hosts_path = r"C:\Windows\System32\drivers\etc\hosts" content = "" try: # 替代编码方案 try: with open(hosts_path, 'r', encoding='utf-8') as f: content = f.read() except UnicodeDecodeError: with open(hosts_path, 'r') as f: # 使用系统默认编码 content = f.read() except Exception as e: content = f"读取文件失败: {str(e)}" except Exception: content = "无法读取hosts文件" # 创建查看窗口 view_window = tk.Toplevel(root) view_window.title("Hosts 文件内容") view_window.geometry("600x400") # 添加滚动文本框 text_area = scrolledtext.ScrolledText(view_window, wrap=tk.WORD) text_area.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) text_area.insert(tk.INSERT, content) text_area.configure(state='disabled') # 设置为只读 # 添加关闭按钮 close_btn = ttk.Button(view_window, text="关闭", command=view_window.destroy) close_btn.pack(pady=10) def on_add_click(): result = add_hosts_entries() if result.get("errors"): error_msg = "\n".join(result["errors"]) messagebox.showerror("错误", f"操作过程中发生错误:\n{error_msg}") else: added = result.get("added", 0) skipped = result.get("skipped", 0) if added > 0: msg = f"成功添加 {added} 个条目" if skipped > 0: msg += f"\n跳过 {skipped} 个已存在的条目" messagebox.showinfo("成功", msg) else: if skipped > 0: messagebox.showinfo("完成", f"所有条目已存在,未进行更改") else: messagebox.showinfo("完成", "没有需要添加的条目") # 创建主窗口 if __name__ == "__main__": root = tk.Tk() root.title("Hosts 文件编辑器") root.geometry("450x300") # 设置样式 style = ttk.Style() style.configure("TButton", padding=6, font=('Arial', 10)) style.configure("Header.TLabel", font=('Arial', 14, 'bold')) # 创建界面元素 header = ttk.Label(root, text="Hosts 文件编辑器", style="Header.TLabel") header.pack(pady=20) info_label = ttk.Label(root, text="此工具用于添加华为相关服务的hosts条目", wraplength=400) info_label.pack(pady=10) # 按钮框架 btn_frame = ttk.Frame(root) btn_frame.pack(pady=20) add_btn = ttk.Button(btn_frame, text="添加条目", command=on_add_click) add_btn.pack(side=tk.LEFT, padx=10) view_btn = ttk.Button(btn_frame, text="查看Hosts文件", command=show_hosts_file) view_btn.pack(side=tk.LEFT, padx=10) # 状态栏 status_bar = ttk.Label(root, text="请以管理员身份运行此程序", relief=tk.SUNKEN, anchor=tk.W) status_bar.pack(side=tk.BOTTOM, fill=tk.X) # 检查管理员权限 try: is_admin = ctypes.windll.shell32.IsUserAnAdmin() if is_admin: status_bar.config(text="管理员权限已获取 - 可以修改hosts文件") else: status_bar.config(text="警告:未以管理员身份运行 - 可能无法修改hosts文件") except: status_bar.config(text="无法确定管理员权限状态") root.mainloop()
# 替代编码方案 try: with open(hosts_path, 'r', encoding='utf-8') as f: file_content = f.readlines() encoding_used = "utf-8" except UnicodeDecodeError: try: with open(hosts_path, 'r') as f: # 使用系统默认编码 file_content = f.readlines() encoding_used = "system_default" except Exception as e: # 错误处理
这个方案首先尝试使用 UTF-8 编码打开文件,如果失败(遇到编码错误),则回退到系统默认编码。这样可以处理大多数中文 Windows 系统中的 hosts 文件。
# 处理每一行 for line in file_content: line = line.strip() if line and not line.lstrip().startswith('#'): # 忽略注释行 # 去除行内注释 clean_line = line.split('#')[0].strip() # 规范化空格(将多个空格替换为单个空格) normalized_line = ' '.join(clean_line.split()) existing_entries.add(normalized_line.lower()) # 不区分大小写
这里做了三个关键改进:
使用 lstrip()
确保正确识别以空格开头的注释行
使用 ' '.join(clean_line.split())
规范化空格,避免因空格数量不同导致的误判
使用 lower()
进行大小写不敏感的匹配
程序包含以下界面元素:
添加条目按钮:执行添加操作
查看Hosts文件按钮:在新窗口中显示当前hosts文件内容
状态栏:显示管理员权限状态
响应式布局:使用 ttk 主题控件,更美观
result = {"added": 0, "skipped": 0, "errors": []} # 在操作过程中更新结果 if normalized_entry.lower() in existing_entries: result["skipped"] += 1 else: entries_to_add.append(normalized_entry) result["added"] += 1
这种结构可以记录添加了多少条目、跳过了多少条目,以及遇到的任何错误,为用户提供更详细的反馈。
def show_hosts_file(): # ...读取文件内容... # 创建查看窗口 view_window = tk.Toplevel(root) # 添加滚动文本框显示内容
这个功能允许用户在不离开程序的情况下查看当前 hosts 文件内容,方便验证修改结果。
保存:将代码保存为 hosts_editor.py
运行:右键选择"以管理员身份运行"
界面:
点击"添加条目"添加预定义的华为服务条目
点击"查看Hosts文件"查看当前hosts内容
状态栏显示当前权限状态
结果:
成功添加会显示添加数量
条目已存在会显示跳过数量
错误会显示详细错误信息
这个增强版解决了编码问题,提供了更友好的界面,并增强了错误处理和用户反馈功能。