2025年3月17日 星期一 甲辰(龙)年 月十六 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > Python

记录一些最近用过的编码转换

时间:09-17来源:作者:点击数:48

记录一些之前遇到的编码解码方法,当速查表。

如果谁有更好的方法欢迎留言告知。

编码解码(encode,decode)

8bit = 1byte(8比特=1字节)

1byte能表示的最大的整数就是255(二进制11111111=十进制255)

编码 字节(byte)
ASCII 1
UTF-8 英文1;汉字3;生僻字符4-6

encode、decode:

  • a = '我用Python'
  • # encode (编码)Unicode转化为其他
  • encode_utf8 = a.encode('utf-8')
  • print('utf-8:',encode_utf8)
  • encode_gbk = a.encode('gbk')
  • print(' gbk:',a.encode('gbk'))
  • # decode (解码)其他转化为Unicode
  • print('utf-8:',encode_utf8.decode('utf-8'))
  • print(' gbk:',encode_gbk.decode('gbk'))
  • utf-8: b'\xe6\x88\x91\xe7\x94\xa8Python'
  • gbk: b'\xce\xd2\xd3\xc3Python'
  • utf-8: 我用Python
  • gbk: 我用Python

URL编码

’%E4%BD%A0’【urllib.request.unquote(xxx)】

很常见,你用百度随便搜点什么,在看一下上面的地址栏里面,你要搜索的文字就是被转码成这种格式的。

解码:

  • >>> import urllib.request
  • >>> urllib.request.unquote('http%3a%2f%2fwww.baidu.com%2fs%3fwd%3d%E4%BD%A0%E5%A5%BD')
  • 'http://www.baidu.com/s?wd=你好'
  • #如果用的是requests库,则
  • >>> import requests
  • >>> requests.utils.unquote()

编码:

  • >>> import urllib.request
  • >>> urllib.request.quote('你好')
  • '%E4%BD%A0%E5%A5%BD'

题外话:

电脑里翻到不知道多少年前的通讯录备份文件(*.vcf),想看看都有啥。用文本编辑器打开发现姓名的编码都显示成=E5=8D=8E=E4=B8=BA这种格式。

  • vcf = '''BEGIN:VCARD
  • VERSION:2.1
  • N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;=E5=BE=AE=E6=98=9F=E5=94=AE=E5=90=8E=E7=BB=B4=E4=BF=AE=E7=AB=99;;;
  • FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=E5=BE=AE=E6=98=9F=E5=94=AE=E5=90=8E=E7=BB=B4=E4=BF=AE=E7=AB=99
  • TEL;WORK:010 6250 9074
  • ORG;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=E5=8C=97=E4=BA=AC=E4=B8=AD=E5=85=B3=E6=9D=91=20=E5=8C=97=E4=BA=AC=E5=
  • =B8=82=E6=B5=B7=E6=B7=80=E5=8C=BA=E4=B8=AD=E5=85=B3=E6=9D=91=E7=A7=91=
  • =E5=AD=A6=E9=99=A2=E5=8D=97=E8=B7=AF=35=35=E5=8F=B7=C2=A0
  • END:VCARD
  • BEGIN:VCARD
  • VERSION:2.1
  • N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=E5=8D=8E=E4=B8=BA;=E5=AE=A2=E6=9C=8D;;;
  • FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=E5=8D=8E=E4=B8=BA=E5=AE=A2=E6=9C=8D
  • TEL;CELL:4008308300
  • EMAIL;HOME:mobile@huawei.com
  • ADR;WORK;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;;=E6=B7=B1=E5=9C=B3=E5=B8=82=E9=BE=99=E5=B2=97=E5=8C=BA=E5=9D=82=E7=94=
  • =B0=E5=8D=8E=E4=B8=BA=E5=9F=BA=E5=9C=B0;;;;
  • ADR;WORK;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;;=E6=B7=B1=E5=9C=B3=E5=B8=82=E9=BE=99=E5=B2=97=E5=8C=BA=E5=9D=82=E7=94=
  • =B0=E5=8D=8E=E4=B8=BA=E5=9F=BA=E5=9C=B0;;;;
  • END:VCARD'''

看到CHARSET=UTF-8这种写法,果断试着把=替换成%,发现还真就是url编码。

写个小程序运行一下

  • import re
  • import urllib.request
  • vcf = vcf.replace('=\n=','=')
  • names = re.findall('(=[\w=]+)',vcf)
  • for name in names:
  • if name[3:4]=='=':
  • name_new = name.replace('=','%')
  • name_new = urllib.request.unquote(name_new)
  • vcf = vcf.replace(name,name_new,1)
  • print(vcf)

然后一目了然。

  • BEGIN:VCARD
  • VERSION:2.1
  • N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;微星售后维修站;;;
  • FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:微星售后维修站
  • TEL;WORK:010 6250 9074
  • ORG;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:北京中关村 北京市海淀区中关村科学院南路55
  • END:VCARD
  • BEGIN:VCARD
  • VERSION:2.1
  • N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:华为;客服;;;
  • FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:华为客服
  • TEL;CELL:4008308300
  • EMAIL;HOME:mobile@huawei.com
  • ADR;WORK;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;;深圳市龙岗区坂田华为基地;;;;
  • ADR;WORK;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;;深圳市龙岗区坂田华为基地;;;;
  • END:VCARD

Unicode编码

\u6ee1【xxx.encode().decode(‘unicode_escape’)】

有些时候你会遇到这样的文字

  • {"dataHtml":"<li …… 38371c4c411.jpg\" alt=\"\u6ee1\u6c49\u5168\u5e2d\"\/><\/a><p class=\" …… }

'\uxxxx’在实际生活遇到的很多都是中文的Unicode编码。一般用json模块加载的时候,可以自动转换处理的。如果不是在json中遇到的,可以这样解码:

  • >>> r'\u6ee1\u6c49\u5168\u5e2d'.encode().decode('unicode_escape')
  • 满汉全席

html转义字符

& amp ;【html.parser.HTMLParser().unescape(xxx)】

因为&等字符在html代码中是有特殊含义的符号,因此作为文字显示的时候,在源代码中用转义字符的方式显示。

解码方式如下:

  • >>> import html.parser
  • >>> html_parser = html.parser.HTMLParser()
  • >>> html_parser.unescape('~^&omega;^~')
  • '~^ω^~'

有的网页比较恶心,还会遇到二次转义的,比如&amp;omega;这种。对于需要处理多次的,直接写了个函数。

  • import html.parser
  • def html_unescapeEntities(string):
  • str_new = string
  • str_old = ''
  • while str_old != str_new:
  • str_old = str_new
  • str_new = html.parser.HTMLParser().unescape(str_old)
  • return str_new
  • >>> html_unescapeEntities('&amp;omega;')
  • 'ω'
  • # 成功得到字符ω

至于编码,试着用encode方法写出来了,实际使用中非常少用到:

只在2020年7月,用到过一次,是因为网页使用了爬虫反制,原网站生成随机字体ttf文件对页面数字进行了加密,导致你能看到数字,但是无法复制和抓取。

例如:

网页上显示46两个数字;

从网页源代码中看是'&#100154;&#100152;,且每次都不一样,每次刷新都随机生成一个ttf字体库,动态解析显示;

而在爬虫页面text文本中是𘜺𘜸这种方框。

不想从r.content中提取,又想直接使用pyquery提取𘜺𘜸转换回&#的形式,才用到了这种编码转换,代码如下。

(顺便一提的是,对于这种随机生成ttf库的加密,如果通过fontTools.ttLib.TTFont查看ttf转出的xml是明文的,则直接对应解密即可;如果不是明文的,可能就需要考虑ocr或px像素样式匹配之类的,通常这种加密都是用于加密数字,所以对应文字只有0-9和小数点这几个字而已)

  • >>> str('𘜺𘜸'.encode('ascii', 'xmlcharrefreplace'),'ascii')
  • '&#100154;&#100152;'

字节集

b’\xd5\xfd’【xxx.decode([encoding])】

先说一下怎么解码的

如果是在网页中遇到的,看一下网页源代码通常会有这样的东西:

<meta http-equiv="content-type" content="text/html;charset=gb2312">

其中charset就是编码格式,参照下面的写法解码就行了。

  • >>> b'\xd5\xfd'.decode('gb2312')
  • '正'

说几句废话。

偶尔碰到有人问\xbb\xbb这个怎么解码,我觉得最好是先明确这么一个概念:

b’\xd5\xfd’ 是纯粹计算机的数据格式。

你要说它是数字,也可以。它就是16进制的 d5fd,二进制的 1101010111111101。文字要经过编码才能存到计算机里,我想你一定还记得电脑只能存’01010101’这回事吧。

那磁盘或内存上的’0101’是怎么转换为文字呢?

看过谍战片的都知道,电视剧里情报员通过电台听到“3379 3728”之类密码后,拿着一本书按照数字指定的页数行数把密码翻译出来。

解码也一样,数字“d5fd”就是密码,只有当知道解码的书是《GBK》的时候,你才能正确的解出来。

  • >>> b'\xd5\xfd'.decode('gbk')
  • '正'

否则……

  • >>> b'\xd5\xfd'.decode('big5')
  • '淏'
  • >>> b'\xd5\xfd'.decode('IBM1026')
  • 'NÙ'
  • >>> b'\xd5\xfd'.decode('EUC-KR')
  • '攣'
  • >>> b'\xd5\xfd'.decode('EUC_JP')
  • '屎'

所以问”\xd5\xfd是什么?“,就好比在问“第213页15行第13字是什么?”,这么点信息鬼知道是神马啊!屎啊?(╯╰_╯)╯╧╧

但就日常而言,虽然编码的种类非常多,但我们接触到的很有限。如果不知道编码,建议你可以GBK、UTF-8、BIG5挨个试试 (ΦωΦ)。

或者用第三方库,比如chardet来判断编码类型。

  • >>> import chardet
  • >>> phase = '使用 chardet 可以很方便的实现字符串/文件的编码'
  • >>> aaa = phase.encode('gbk')
  • >>> bbb = phase.encode('utf-8')
  • >>> chardet.detect(aaa)
  • {'encoding': 'GB2312', 'confidence': 0.99}
  • >>> chardet.detect(bbb)
  • {'encoding': 'utf-8', 'confidence': 0.99}

看到结论了吧,chardet会很谦虚的对你说"(๑>◡<๑) 我99%肯定这是XX编码的"。

那么chardet就一定对吗?答案是否定的。

chardet从来不100%confidence是什么编码,意思就是告诉你还是有可能存在意外的,使用的时候还需注意。比如:

  • >>> import chardet
  • >>> phase = '使用 chardet 可以很方便的實現字符串'.encode('gbk')
  • >>> print(chardet.detect(phase))
  • {'confidence': 0.99, 'encoding': 'GB2312'}

很明显这是错的,因为gb2312是解不了繁体字的。

这里按时间顺序讲一下GB2312、GBK、CP936、ANSI、Unicode、UTF-8, 来帮助你理解这些常见的编码名称代表什么

  • 首先, 世界上不是只有英文,
  • 1980年中国国家标准总局发布了GB2312(GB是GuoBiao国标的拼音缩写), 包含六千多汉字和一些字符, 以满足汉字显示存储等需求
  • 1987年出现了CodePage(内码表), 由IBM最早用在MS-DOS中, 用于兼容各个国家和地区的不同编码, 后来微软等公司也纷纷制定了自己的内码表
  • 1991年出现了大名鼎鼎的万国字符集Unicode, 注意这个是字符集而非编码方法, 只是为每一个「字符」分配一个唯一的 ID(学名:码位)
  • 1992年出现的UTF-8才是编码方法, 顾名思义UTF-8是一套以 8 位为一个编码单位的可变长编码。会将一个码位编码为1~4个字节, 实现数据存储单位「字节」与实际「字符」之间转换
  • 1995年出现了Windows95简体中文版默认编码CP936,即微软内码页936(Code Page 936), CP936是微软以GB2312为基础制作的内码页
  • 1995年出现了GBK, 是一份技术性指导文件, 而非标准, 就现在的日常使用而言, 如果你是在windows上用CP936编码的文件, 基本上用GBK解码没有太多问题, 两者相差不大
  • 至于ANSI具体代表什么编码取决于CodePage, 比如你在简中和繁中的Windows上分别保存一个ANSI编码的文档, 则其编码格式分别为 CP936(可以用GBK解码) 和 CP950(可以用BIG5解码), 因为两种操作系统的默认CodePage值分别为936和950

跑码小程序

最后写个小程序,以后遇到不知道编码的字节集,大可放进去跑跑试试。

  • #!/usr/bin/env python3
  • # -*- coding: utf-8 -*-
  • import chardet
  • def decodetest1(byte):
  • aaa='''IBM037|IBM437|IBM500|ASMO-708|DOS-720|ibm737|ibm775|ibm850|ibm852|IBM855|ibm857|IBM00858|IBM860|ibm861|DOS-862|IBM863|IBM864|IBM865|cp866|ibm869|IBM870|windows-874|cp875|shift_jis|ks_c_5601-1987|big5|IBM1026|IBM01047|IBM01140|IBM01141|IBM01142|IBM01143|IBM01144|IBM01145|IBM01146|IBM01147|IBM01148|IBM01149|utf-16|unicodeFFFE|windows-1250|windows-1251|windows-1252|windows-1253|windows-1254|windows-1255|windows-1256|windows-1257|windows-1258|Johab|macintosh|x-mac-japanese|x-mac-chinesetrad|x-mac-korean|x-mac-arabic|x-mac-hebrew|x-mac-greek|x-mac-cyrillic|x-mac-chinesesimp|x-mac-romanian|x-mac-ukrainian|x-mac-thai|x-mac-ce|x-mac-icelandic|x-mac-turkish|x-mac-croatian|utf-32|utf-32BE|x-Chinese_CNS|x-cp20001|x_Chinese-Eten|x-cp20003|x-cp20004|x-cp20005|x-IA5|x-IA5-German|x-IA5-Swedish|x-IA5-Norwegian|us-ascii|x-cp20261|x-cp20269|IBM273|IBM277|IBM278|IBM280|IBM284|IBM285|IBM290|IBM297|IBM420|IBM423|IBM424|x-EBCDIC-KoreanExtended|IBM-Thai|koi8-r|IBM871|IBM880|IBM905|IBM00924|EUC-JP|x-cp20936|x-cp20949|cp1025|koi8-u|iso-8859-1|iso-8859-2|iso-8859-3|iso-8859-4|iso-8859-5|iso-8859-6|iso-8859-7|iso-8859-8|iso-8859-9|iso-8859-13|iso-8859-15|x-Europa|iso-8859-8-i|iso-2022-jp|csISO2022JP|iso-2022-jp|iso-2022-kr|x-cp50227|euc-jp|EUC-CN|euc-kr|hz-gb-2312|gb2312|gbk|GB18030|x-iscii-de|x-iscii-be|x-iscii-ta|x-iscii-te|x-iscii-as|x-iscii-or|x-iscii-ka|x-iscii-ma|x-iscii-gu|x-iscii-pa|utf-7|utf-8'''
  • for encoding in aaa.split('|'):
  • try:
  • sss = byte.decode(encoding)
  • print('%15s %s'% (encoding,sss))
  • except:
  • pass
  • print(chardet.detect(byte))
  • def decodetest2(text):
  • code_arr='''shift_jis|koi8-r|euc-kr|euc-jp|ISO-2022-JP|big5|GB18030|utf-8|utf-16|utf-32'''.split('|')
  • for c1 in code_arr:
  • for c2 in code_arr:
  • if c1!=c2:
  • try:
  • sss = text.encode(c1).decode(c2)
  • print('%10s %10s %s'% (c1,c2,sss))
  • except:
  • pass
  • if __name__ == '__main__':
  • decodetest1(b'\xba\xf3\xc7\xda\xb9\xdc\xc0\xed')
  • #decodetest2('鍏抽棴')

结果如下:

  • …… ……
  • macintosh ∫Û«⁄π‹¿Ì
  • x-mac-korean 빈핸밗잿
  • IBM273 ¬3G¹¾}äÒ
  • koi8-r ╨Сгз╧эюМ
  • EUC-JP 朔輩砿尖
  • koi8-u ╨Сгз╧эюМ
  • iso-8859-1 ºóÇÚ¹ÜÀí
  • iso-8859-2 şóÇÚšÜŔí
  • iso-8859-3 şóÇÚıÜÀí
  • iso-8859-4 ēķĮÚšÜĀí
  • iso-8859-5 КѓЧкЙмРэ
  • iso-8859-7 ΊσΗΪΉάΐν
  • iso-8859-9 ºóÇÚ¹ÜÀí
  • iso-8859-13 ŗóĒŚ¹ÜĄķ
  • iso-8859-15 ºóÇÚ¹ÜÀí
  • euc-jp 朔輩砿尖
  • EUC-CN 后勤管理
  • euc-kr 빈핸밗잿
  • gb2312 后勤管理
  • gbk 后勤管理
  • GB18030 后勤管理
  • {'confidence': 0.0, 'encoding': None}
  • [Finished in 0.2s]
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门