为何将记录器编码设置为UTF-8写入带有UNIX行尾的文件?

我创建了一个写入文本文件的记录器:

import logging

logger_dbg = logging.getLogger("dbg")
logger_dbg.setLevel(logging.DEBUG)
fh_dbg_log = logging.FileHandler('debug.log', mode='w', encoding='utf-8')
fh_dbg_log.setLevel(logging.DEBUG)

# Print time, logger-level and the call's location in a source file.
formatter = logging.Formatter(
    '%(asctime)s-%(levelname)s(%(module)s:%(lineno)d)  %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S')
fh_dbg_log.setFormatter(formatter)

logger_dbg.addHandler(fh_dbg_log)
logger_dbg.propagate = False

然后,当我想记录一些信息时,我将此记录器称为:

logger_dbg.debug("Closing port...")
logger_dbg.debug("Port closed.")

问题是尽管我在Windows 7(64位)上运行此程序,但书面日志文件debug.log使用单个换行(LF)字符作为换行符:

2015-11-30 12:39:08-DEBUG(SerialThread:196)  Closing port...  2015-11-30 12:39:08-DEBUG(SerialThread:198)  Port closed.

奇怪的是,如果我改为设置记录器的文件句柄而不使用encoding =’utf-8’参数,则换行符正确地写为CR / LF.

为什么将编码设置为UTF-8会导致Python使用不正确的换行符?

解决方法:

指定编码时,将使用codecs.open()代替常规的open()调用.此函数始终以二进制模式打开文件,并在此之上实现编码.这样,它可以保证任何编解码器都能正常工作,而不仅仅是基于ASCII的编解码器.此选择的副作用是,在Windows上,换行符不再转换为平台约定!

您可以提出一个错误来解决此问题,一个更好的解决方案是使用io.open();否则,您可以使用它. io模块是新的Python 3 I / O框架,该框架反向移植到Python 2,它可以更好地处理文本模式,包括在Windows上正确处理换行符.

您可以修补logging.FileHandler._open方法以在本地修复此问题:

import io
from logging import FileHandler

_orig_open = FileHander._open
_orig_emit = FileHandler.emit

def filehandler_open_patch(self):
    if self.encoding is not None:
        return io.open(self.baseFilename, self.mode, encoding=self.encoding)
    return _orig_open(self)

def filehandler_emit_patch(self, record):
    if not self.encoding:
        return _orig_emit(self, record)
    try:
        msg = self.format(record)
        stream = self.stream
        fs = u"%s\n"
        if not isinstance(msg, unicode):
            msg = msg.decode('ASCII', 'replace')
        ufs = u'%s\n'
        stream.write(ufs % msg)
        self.flush()
    except (KeyboardInterrupt, SystemExit):
        raise
    except:
        self.handleError(record)

FileHandler._open = filehandler_open_patch
FileHandler.emit = filehandler_emit_patch

还需要修补FileHandler.emit()方法,因为否则Unicode消息首先会被编码为UTF-8,但是io.open()文件对象仅接受Unicode对象.

上一篇:PHP-为什么在以下情况下回声后没有插入换行符?


下一篇:如何在Java FileOutputStream中编写新行