You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

指定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系统会混用该编码与CP1251
  • utf-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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 11:05:00