为什么将记录器编码设置为 UTF-8 会写入带有 UNIX 行结尾的文件?
Why setting logger encoding to UTF-8 writes file with UNIX line-endings?
我创建了一个写入文本文件的记录器:
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.")
问题是写入的日志文件 debug.log
使用单个换行符 (LF) 作为换行符,尽管我 运行 这个程序在 Windows 7 (64-位):
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 = FileHandler._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 对象。
我创建了一个写入文本文件的记录器:
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.")
问题是写入的日志文件 debug.log
使用单个换行符 (LF) 作为换行符,尽管我 运行 这个程序在 Windows 7 (64-位):
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 = FileHandler._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 对象。