Python中的unicode与str

unicode and str in python

Posted by wykxwyc on December 6, 2022

目录


问题的出现

一个日志的函数出现了报错,报错内容如下:

[LOG] ERROR [findAvatarNameByNid] nid(%s), name(%s), ret(%s) (4093364685, u'\u8fd9\u662f\u6635\u79f0id~', "{'aid': u'041101', '_id': u'YlP2NQgI0GG9D97W', 'nid': 4093364685L, 'name': u'\\u8fd9\\u662f\\u6635\\u79f0id~', 'username': u'', 'login_channel': u'', 'app_channel': u'app_channel', 'alpha_expire_ts': ''}")

其实是日志打印出错了,日志函数是这样定义的:

def _log_with_timestamp(level, msg, args, **kwargs):
    try:
        ct = time.time()
        lt = time.localtime(ct)
        print "%04d-%02d-%02d %02d:%02d:%02d.%03d\t%-12.12s\t%5d\t%-5.5s\t%-16.16s\t%s %s" % (
                lt.tm_year, lt.tm_mon, lt.tm_mday, lt.tm_hour, lt.tm_min, lt.tm_sec, (ct - int(ct)) * 1000,
                TAG, PID, level, NAME, msg % args, kwargs and kwargs or "")
    except Exception:
        print '[LOG] ERROR', msg, args, kwargs and kwargs or ""

出错的Exception内容为:

UnicodeEncodeError('ascii', u"[findAvatarNameByNid] nid(4093364685), name(\u8fd9\u662f\u6635\u79f0id~), ret({'aid': u'041101', '_id': u'YlP2NQgI0GG9D97W', 'nid': 4093364685L, 'name': u'\\u8fd9\\u662f\\u6635\\u79f0id~', 'username': u'', 'login_channel': u'', 'app_channel': u'app_channel', 'alpha_expire_ts': ''})", 44, 48, 'ordinal not in range(128)')

因为print函数中, msg % args 会把参数转义,而name是unicode,所以转义之后 msg % args整体就变成了一个unicode,接下来需要把这个整体转义到%s里面。此处转义会直接使用str()函数,但是其中的name(\u8fd9\u662f\u6635\u79f0id~)部分使用str函数,因为ascii无法解码导致出错了,因此有这个报错。

ascii, unicode与utf-8

Python的诞生比Unicode标准发布的时间还要早,所以最早的Python只支持ASCII编码,Python在后来添加了对Unicode的支持,以Unicode表示的字符串用u’…‘表示。
unicode:加入多国字符,一般是2个字节表示一个字符,偏僻字用4个字节。缺点:浪费存储空间。 ascii:最早的,容量最小的编码方式。1个字节表示一个字符。
utf-8:为了解决浪费空间的问题,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。

保存:Python源代码也是一个文本文件,所以,当你的源代码中包含中文的时候,在保存源代码时,就需要务必指定保存为UTF-8编码。
读取:当Python解释器读取源代码时,为了让它按UTF-8编码读取,我们通常在文件开头(第一行或第二行)写上这一行行# -*- coding: utf-8 *-

一个unicode编码与utf-8编码之间的转换:

# -*- coding: utf-8 -*-
import re
import traceback
import sys
import time
import os


def unicode2utf8():
	print repr(u'abc'.encode('utf-8'))
	print repr(u'中文'.encode('utf-8'))

def utf82unicode():
	print repr('abc'.decode('utf-8'))
	print repr('\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8'))
	print repr('中文'.decode('utf-8'))


if __name__ == "__main__":
	print "========================="
	print sys.getdefaultencoding()
	print "========================="
	unicode2utf8()
	print "========================="
	utf82unicode()
	print "========================="

输出的结果为:

=========================
ascii
=========================
'abc'
'\xe4\xb8\xad\xe6\x96\x87'
=========================
u'abc'
u'\u4e2d\u6587'
u'\u4e2d\u6587'
=========================

Python3中的bytes与unicode的转换

python发展过程中,最初设计使用ASCII编码,后来扩充到Latin-1, 再后来扩展到unicode。
unicode首先使用2字节代表一个字符,再后来扩展到4字节代表一个字符,空间有浪费,所以有了utf-8这种变长的编码方式。
bytes – decode –> unicode
bytes <– encode – unicode

判断unicode是不是控制字符

判断字符串s中是否有控制字符, s为unicode字符串
通过unicodedata.category对unicode字符进行分类,对于控制字符,分类信息以’C’开头

import unicodedata
s = u'\0'
for ch in s:
    if unicodedata.category(ch)[0] == "C":
        print True

参考文献

1.https://pyformat.info/
2.unicode控制字符串判断
3.python文档string-format
4.python2 编码问题详解