指定cp1251编码仍遇UnicodeDecodeError:Discord机器人日志读取故障
问题:Discord机器人读取CP1251编码日志时的Unicode解码错误
使用disnake框架开发的Discord机器人,功能是监控server_chat_log.txt日志文件并将新增内容发送到指定频道。已知日志采用CP1251编码,但指定encoding='cp1251'后仍触发解码错误:
line 22, in on_ready last_line = file2.readlines()[-1].strip() # Получаем последнюю строку из файла и удаляем символы переноса строки File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\lib\encodings\cp1251.py", line 23, in decode return codecs.charmap_decode(input,self.errors,decoding_table)[0] UnicodeDecodeError: 'charmap' codec can't decode byte 0x98 in position 4711: character maps to <undefined>
机器人代码:
import disnake from disnake.ext import commands import time from disnake import Intents bot = commands.Bot(command_prefix="*", intents=disnake.Intents.all()) @bot.event async def on_ready(): print(f'{bot.user.name} has connected to Discord!') channel1 = bot.get_channel(1105450189688414208) channel2 = bot.get_channel(1105452174546321498) last_line_file1 = '' last_line_file2 = '' while True: # with open("C:\\Users\\______\\____\\_____\\______\\_____\\_____\\server_log.txt", 'r', encoding='cp1251') as file1: # last_line = file1.readlines()[-1].strip() символы переноса строки # if last_line != last_line_file1: # await channel1.send(f'```\n{last_line}\n```') # last_line_file1 = last_line with open('"C:\\Users\\______\\____\\_____\\______\\_____\\_____\\server_chat_log.txt"', 'r', encoding='cp1251') as file2: last_line = file2.readlines()[-1].strip() if last_line != last_line_file2: await channel2.send(last_line) last_line_file2 = last_line time.sleep(0.5) bot.run('---')
日志文件server_chat_log.txt示例内容:
[00:15:59]: [Say] (KU_LnRtDiLl) groznyjsasison: у меня норм лут теперь [00:16:56]: [Say] (KU_l2ws0ArT) Коннор RK800 (????): скок у тебя камня? [00:17:05]: [Say] (KU_LnRtDiLl) groznyjsasison: камня 14 [00:17:06]: [Join Announcement] Aleksey Meatballs in DST [00:17:09]: [Say] (KU_LnRtDiLl) groznyjsasison: кремня 24 [00:17:13]: [Say] (KU_l2ws0ArT) Коннор RK800 (????): добудь 40 хотя бы [00:17:17]: [Say] (KU_LnRtDiLl) groznyjsasison: знаю [00:17:18]: [Say] (KU_l2ws0ArT) Коннор RK800 (????): кремня итак у меня 40 [00:19:42]: [Say] (KU_LnRtDiLl) groznyjsasison: давай я угля добуду [00:19:46]: [Say] (KU_LnRtDiLl) groznyjsasison: для казана [00:19:49]: [Say] (KU_l2ws0ArT) Коннор RK800 (????): го [00:19:59]: [Say] (KU_l2ws0ArT) Коннор RK800 (????): я золото добыл [00:20:09]: [Say] (KU_LnRtDiLl) groznyjsasison: у меня 2 золота [00:20:24]: [Say] (KU_l2ws0ArT) Коннор RK800 (????): добавь меня в др [00:20:31]: [Say] (KU_l2ws0ArT) Коннор RK800 (????): нах ето говорить в общий чат [00:20:38]: [Say] (KU_LnRtDiLl) groznyjsasison: как [00:20:40]: [Say] (KU_LnRtDiLl) groznyjsasison: в др [00:20:41]: [Say] (KU_l2ws0ArT) Коннор RK800 (????): в стиме [00:20:47]: [Say] (KU_LnRtDiLl) groznyjsasison: я не могу [00:20:52]: [Say] (KU_LnRtDiLl) groznyjsasison: не донил [00:20:52]: [Say] (KU_l2ws0ArT) Коннор RK800 (????): я тебе заявку кину
解决方案
1. 添加错误处理参数跳过无效字符
CP1251编码不包含0x98对应的字符,添加errors='ignore'或errors='replace'参数可避免解码崩溃:
errors='ignore':直接跳过无法解码的字节errors='replace':用�替换无法解码的字符
修改后的文件打开代码:
with open("C:\\Users\\______\\____\\_____\\______\\_____\\_____\\server_chat_log.txt", 'r', encoding='cp1251', errors='ignore') as file2:
2. 验证日志文件实际编码
日志可能并非纯CP1251编码,可尝试测试以下常见西里尔字符编码:
windows-1252:部分Windows系统会混用该编码与CP1251utf-8:可能存在编码混合情况koi8-r:另一种常见西里尔编码
测试代码:
# 遍历编码测试读取情况 for enc in ['cp1251', 'windows-1252', 'utf-8', 'koi8-r']: try: with open("C:\\Users\\______\\____\\_____\\______\\_____\\_____\\server_chat_log.txt", 'r', encoding=enc) as f: print(f"编码{enc}读取成功:{f.readline()}") except Exception as e: print(f"编码{enc}读取失败:{e}")
3. 修复文件路径错误
当前代码中文件路径被多余双引号包裹('"C:\\Users\\..."'),会导致Python找不到文件,必须去掉:
错误写法:
with open('"C:\\Users\\______\\____\\_____\\______\\_____\\_____\\server_chat_log.txt"', 'r', encoding='cp1251') as file2:
正确写法(可选原始字符串避免转义):
with open(r"C:\Users\______\____\_____\______\_____\_____\server_chat_log.txt", 'r', encoding='cp1251', errors='ignore') as file2:
4. 优化日志读取逻辑(可选)
当前每次循环读取整个文件效率较低,可改为记录文件指针位置,只读取新增内容,同时替换time.sleep为asyncio.sleep避免阻塞机器人事件循环:
import disnake from disnake.ext import commands import asyncio from disnake import Intents bot = commands.Bot(command_prefix="*", intents=disnake.Intents.all()) @bot.event async def on_ready(): print(f'{bot.user.name} has connected to Discord!') channel2 = bot.get_channel(1105452174546321498) log_path = r"C:\Users\______\____\_____\______\_____\_____\server_chat_log.txt" # 初始定位到文件末尾 with open(log_path, 'r', encoding='cp1251', errors='ignore') as f: f.seek(0, 2) file_position = f.tell() while True: with open(log_path, 'r', encoding='cp1251', errors='ignore') as f: f.seek(file_position) new_lines = f.readlines() if new_lines: for line in new_lines: stripped_line = line.strip() if stripped_line: await channel2.send(stripped_line) file_position = f.tell() await asyncio.sleep(0.5) bot.run('---')
内容的提问来源于stack exchange,提问作者Fanky
相关产品推荐
相关产品推荐

