在 Python3 中对 print() 使用编解码器错误处理程序?
Use codec error handler for print() in Python3?
众所周知,encode()
有一个 error
参数用于编解码器错误处理,例如:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# "发" and "财" are not available in 'big5' encoding
text = "发财了".encode('big5', errors='replace')
但是,print()
没有 errors
参数,如果我们简单地写:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
print("发财了")
如果在使用 big5 编码的命令提示符中 运行,则会引发 UnicodeEncodeError
异常(例如,在繁体中文版本 Windows 中)。
有没有办法让 print()
像 encode()
一样接受更多的处理程序,例如 replace
、backslashreplace
或 xmlcharrefreplace
,所以字符串可以安全地打印而不会引发异常吗?
用 try .. except 块包装您的打印函数,并捕获 UnicodeEncodeError 异常:
try:
print("发财了")
except UnicodeEncodeError:
handle_encode_error()
您可以决定如何转换异常块中的字符串。
这是我目前的解决方案:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
def safeprint(*args, errors='backslashreplace', **kargs):
"""
Print safely and skips error decode.
Acts like print() with an additional "errors" argument to determine the
error handler for codec errors and accepts non-str-or-None types for the
"sep" and "end" arguments.
"""
e = (kargs['file'] if 'file' in kargs else sys.stdout).encoding
args = [str(x) for x in args]
sep = str(kargs['sep']) if 'sep' in kargs and kargs['sep'] is not None else " "
end = str(kargs['end']) if 'end' in kargs and kargs['end'] is not None else "\n"
text = sep.join(args) + end
kargs['sep'] = ""
kargs['end'] = ""
print(text.encode(e, errors).decode(e, errors), **kargs)
if __name__ == "__main__":
safeprint("Hello World!", "你好世界!", "ハローワールド", "हैलो वर्ल्ड")
safeprint("Hello World!", "你好世界!", "ハローワールド", "हैलो वर्ल्ड", errors="ignore")
safeprint("Hello World!", "你好世界!", "ハローワールド", "हैलो वर्ल्ड", errors="replace")
safeprint("Hello World!", "你好世界!", "ハローワールド", "हैलो वर्ल्ड", errors="backslashreplace")
safeprint("Hello World!", "你好世界!", "ハローワールド", "हैलो वर्ल्ड", errors="xmlcharrefreplace")
safeprint("Hello World!", "你好世界!", "ハローワールド", "हैलो वर्ल्ड", sep=None, end=str)
safeprint("Hello World!", "你好世界!", "ハローワールド", "हैलो वर्ल्ड", sep=" -发- ", end=" -财- \n")
with open("safeprint_big5.log", "w", encoding="big5") as f:
safeprint("Hello World!", "你好世界!", "ハローワールド", "हैलो वर्ल्ड", file=f)
with open("safeprint_gbk.log", "w", encoding="gbk") as f:
safeprint("Hello World!", "你好世界!", "ハローワールド", "हैलो वर्ल्ड", file=f)
with open("safeprint_utf8.log", "w", encoding="utf-8") as f:
safeprint("Hello World!", "你好世界!", "ハローワールド", "हैलो वर्ल्ड", file=f)
此方法使自定义函数 safeprint()
的行为类似于本机 print()
,但存在以下差异:
有一个额外的 errors
参数来确定如何处理编解码器错误(默认为 backslashreplace
)。
sep
和 end
参数接受 str 或 None 以外的类型。
safeprint()
检查本机 print()
应该写入的输出文件的编码,并预先对所有文本参数进行编码,因此所有可打印字符都按原样打印,所有不可打印字符打印为已转换。
尽管对所有正在打印的文本进行预先编码和解码似乎效率不高,但原生 encode()
和 decode()
是基于 C 的,并且 运行 非常快。在一次测试中,我在 utf8 控制台中打印了一些带有 utf8 兼容纯文本的文章 5000 次,本机 print()
占用 0:00:02.366799
而 safeprint()
占用 0:00:02.915871
。证明性能下降几乎可以忽略不计
以上脚本可以保存为模块脚本,比如safeprint.py
。其他脚本可以使用 from safeprint import safeprint
和 safeprint()
,甚至可以使用 from safeprint import safeprint as print
覆盖原生 print()
,这样 print()
就可以像 safeprint()
一样工作] 确实如此。
众所周知,encode()
有一个 error
参数用于编解码器错误处理,例如:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# "发" and "财" are not available in 'big5' encoding
text = "发财了".encode('big5', errors='replace')
但是,print()
没有 errors
参数,如果我们简单地写:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
print("发财了")
如果在使用 big5 编码的命令提示符中 运行,则会引发 UnicodeEncodeError
异常(例如,在繁体中文版本 Windows 中)。
有没有办法让 print()
像 encode()
一样接受更多的处理程序,例如 replace
、backslashreplace
或 xmlcharrefreplace
,所以字符串可以安全地打印而不会引发异常吗?
用 try .. except 块包装您的打印函数,并捕获 UnicodeEncodeError 异常:
try:
print("发财了")
except UnicodeEncodeError:
handle_encode_error()
您可以决定如何转换异常块中的字符串。
这是我目前的解决方案:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
def safeprint(*args, errors='backslashreplace', **kargs):
"""
Print safely and skips error decode.
Acts like print() with an additional "errors" argument to determine the
error handler for codec errors and accepts non-str-or-None types for the
"sep" and "end" arguments.
"""
e = (kargs['file'] if 'file' in kargs else sys.stdout).encoding
args = [str(x) for x in args]
sep = str(kargs['sep']) if 'sep' in kargs and kargs['sep'] is not None else " "
end = str(kargs['end']) if 'end' in kargs and kargs['end'] is not None else "\n"
text = sep.join(args) + end
kargs['sep'] = ""
kargs['end'] = ""
print(text.encode(e, errors).decode(e, errors), **kargs)
if __name__ == "__main__":
safeprint("Hello World!", "你好世界!", "ハローワールド", "हैलो वर्ल्ड")
safeprint("Hello World!", "你好世界!", "ハローワールド", "हैलो वर्ल्ड", errors="ignore")
safeprint("Hello World!", "你好世界!", "ハローワールド", "हैलो वर्ल्ड", errors="replace")
safeprint("Hello World!", "你好世界!", "ハローワールド", "हैलो वर्ल्ड", errors="backslashreplace")
safeprint("Hello World!", "你好世界!", "ハローワールド", "हैलो वर्ल्ड", errors="xmlcharrefreplace")
safeprint("Hello World!", "你好世界!", "ハローワールド", "हैलो वर्ल्ड", sep=None, end=str)
safeprint("Hello World!", "你好世界!", "ハローワールド", "हैलो वर्ल्ड", sep=" -发- ", end=" -财- \n")
with open("safeprint_big5.log", "w", encoding="big5") as f:
safeprint("Hello World!", "你好世界!", "ハローワールド", "हैलो वर्ल्ड", file=f)
with open("safeprint_gbk.log", "w", encoding="gbk") as f:
safeprint("Hello World!", "你好世界!", "ハローワールド", "हैलो वर्ल्ड", file=f)
with open("safeprint_utf8.log", "w", encoding="utf-8") as f:
safeprint("Hello World!", "你好世界!", "ハローワールド", "हैलो वर्ल्ड", file=f)
此方法使自定义函数 safeprint()
的行为类似于本机 print()
,但存在以下差异:
有一个额外的
errors
参数来确定如何处理编解码器错误(默认为backslashreplace
)。sep
和end
参数接受 str 或 None 以外的类型。
safeprint()
检查本机 print()
应该写入的输出文件的编码,并预先对所有文本参数进行编码,因此所有可打印字符都按原样打印,所有不可打印字符打印为已转换。
尽管对所有正在打印的文本进行预先编码和解码似乎效率不高,但原生 encode()
和 decode()
是基于 C 的,并且 运行 非常快。在一次测试中,我在 utf8 控制台中打印了一些带有 utf8 兼容纯文本的文章 5000 次,本机 print()
占用 0:00:02.366799
而 safeprint()
占用 0:00:02.915871
。证明性能下降几乎可以忽略不计
以上脚本可以保存为模块脚本,比如safeprint.py
。其他脚本可以使用 from safeprint import safeprint
和 safeprint()
,甚至可以使用 from safeprint import safeprint as print
覆盖原生 print()
,这样 print()
就可以像 safeprint()
一样工作] 确实如此。