字符串和编码
字符串和编码
标签(空格分隔): Python
1、历史
1.1 字符串
字符串:是一种数据类型,但是字符串比较特殊的还有一个就是编码问题。
1.2 bit和byte
bit
是比特,byte
是字节
因为计算机只能处理数字,要处理文本,就必须把文本装换成数字才能处理,最早的计算机载机设计时采用\(8\)个\(bit\)作为一个字节,所以,一个字节能表示的最大整数就是\(255\),其基本单位是字节,所以需要表示更大的数据的时候就需要两个字节,也就是\(65535\),4个字节的话表示的最大整数是\(4294967295\)。这个时候就有小精灵鬼提出疑问了,我记得C里面int是四个字节,但是最大能表示的数据大概是\(21\)亿呀,这个机组有讲第一位作为标识符,有正负标准,所以最大是\(21\)亿,负数最大是负\(21\)亿。(应该是这样表达的吧? 提前说了是负数的话 大指的是单纯的数据大小)
- 存储和网速的单位,无论是B还是b,代表的都是
Byte
。 - 带宽的单位,无论是B还是b,代表的都是比特
bit
。
但是我们在实际应用中更偏向于第一种计量单位,所以办完宽带之后咱们总感觉网速没有那么快,实际上的网速都是\(\frac{1}{8}\)。
2 解决方案
2.1 各种奇葩编码
由于计算机是美国人发明的,因此我们了解的ASCII
编码只有\(127\)个字符被收录进去,也就是大小写字母、数字、符号。
但是如果要处理中文的话是远远不够的,《现代汉语常用字表1988年版》就有3500个汉字而且不能和ASCII
冲突,并且根据上面说的最小计量单位为byte
的话,我们至少需要两个字节,所以中国制定了GB2313
编码,用来将中文编入计算机。
但是世界上有很多语言,各国都以这种方法编码的话,机会不可避免的产生冲突,结果就是在多语言混合文本中显示出来会有乱码的情况(学过C的都应该见过 锟斤拷 烫烫烫, 俗称 手持两把锟斤拷 口中直呼烫烫烫)。详情链接
由此Unicode
字符集应运而生,他将所有的语言统一到一套编码里面,这样就不会有乱码问题了。其中最常见的是UCS-16
编码,用两个字节表示一个字符(如果要用到非常偏僻的字符,就需要4个字节)。现代操作系统和大多数编程语言都直接支持Unicode
。ASCII
用的是一个字节,Unicode
一般情况下是两个字节,特别生僻的词会用到四个字节。
ASCII
中字符A
的编码是十进制的65,二进制的0100 0001
Unicode
中字符A
的二进制编码是0000 0000 0100 0001
由上可得:如果统一为Unicode
编码的话 会造成很大的内存空间浪费(以前计算机内存很小程序要精打细算,魂斗罗128k,需要实现那么多的剧情和场景,这一块可以联系享元设计模式一块看),这样的话就需要新的解决方法了。
2.2 解决方案
新的风暴已经出现,怎么能够停滞不前。新的方案出来了也就是门外汉也听过的UTF-8
编码(希望你们可以去看一下Mysql字符串度设置为多长合适,提示: Mysql5
是个分界线.)
UTF-8
是可变长编码,他将Unicode
字符根据不同的数字代销分为1-6个字节,常用的英文字符为1个字节,汉字是三个字节,只有很偏僻的字符才是4-6个字节、 好的,这个时候就开始担心 如果这样操作的话会不会时间复杂度比较高? 这个我有想过,你们也可以去搜一下,这种疑问的习惯很不错,但是小心陷入局部技术陷阱
。
由上可得:大量只支持ASCII编码的历史遗留软件可以在UTF-8
编码格式下正常解析。
在目前计算机内存当中,统一使用Unicode
编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8
编码。
在用记事本进行编辑的时候,从文件中读取的UTF-8
字符被转换为Unicode
放到内存当中,编辑完毕之后,保存的时候再把Unicode
转换为UTF-8
进行保存。
2.3 闲扯
其实在工作当中一般情况下遇不到这种编码问题,只有在写爬虫的时候可能会遇到这种问题。
但是对于Python程序员来说特别是以前的2, 这就很难受了。
在最新的Python3
中,字符串是以Unicode
编码的,也就是说,Python的字符串支持多种语言,并且现在基本上也没那么多编码问题了。
对于单个字符的编码,Python
提供了ord()
函数来获取字符的整数表示,chr()
函数把编码转换为对应的字符:
>>> ord('A')
65
>>> ord('中')
20013
>>> chr(66)
'B'
>>> chr(25991)
'文'
知道中文的整数编码,还可以用十六进制这样编写str
:
>>> '\u4e2d\u6587'
'中文'
Python
中字符串类型是str
,在内存中用Unicode
表示,一个字符对应若干个字节,如果要在网络上传输,或者保存到磁盘上就需要str
变为字节为单位的bytes
.
python
对bytes
类型的数据用带'b'的前缀表示。
a = "asd"
a
Out[41]: 'asd'
b = b"asd"
b
Out[43]: b'asd'
要注意的是两者虽然表示的内容一样,但是第二个每个字符只占用一个字节。
以Unicode
表示的str
通过encode()
方法可以编码为制定的bytes
。
>>> 'ABC'.encode('ascii')
b'ABC'
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
>>> '中文'.encode('ascii')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
纯英文的str
可以用ASCII
编码为bytes
,内容是一样的,因为英文字符ASCII
也就只占一个字符,只有在被编码为Unicode
的时候要占两个字节,前面需要补0。
含有中文的str
可以用UTF-8
编码会bytes
。当然中文无法转ASCII
。
当然我们在磁盘或者网络中获取字节流之后,读取到的数据是bytes
,要把bytes
变为str
,就要用decode()
方法:
>>> b'ABC'.decode('ascii')
'ABC'
>>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
'中文'
如果bytes
中包含无法解码的字节,decode()
方法会报错。
>>> b'\xe4\xb8\xad\xff'.decode('utf-8')
Traceback (most recent call last):
...
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 3: invalid start byte
如果bytes
中只有一部分无效的字节,可以传入errors='ignore'
忽略错误的字节
>>> b'\xe4\xb8\xad\xff'.decode('utf-8', errors='ignore')
'中'
len()
方法用于计算str
包含多少个字符
>>> len('ABC')
3
>>> len('中文')
2
如果将其转化为bytes
的话,len()
函数就计算字节数
>>> len(b'ABC')
3
>>> len(b'\xe4\xb8\xad\xe6\x96\x87')
6
>>> len('中文'.encode('utf-8'))
6
在操作字符串时,我们经常遇到str
和bytes
的互相转换。为了避免乱码问题,应当始终坚持使用UTF-8
编码对str
和bytes
进行转换。
由于Python
源代码也是一个文本文件,所以,当你的源代码中包含中文的时候,在保存源代码时,就需要务必指定保存为UTF-8
编码。当Python
解释器读取源代码时,为了让它按UTF-8
编码读取,我们通常在文件开头写上这两行:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
第一行注释是为了告诉Linux/OS X
系统,这是一个Python
可执行程序,Windows
系统会忽略这个注释;Windows
是通过文件名后缀辨识文件类型的,例如exe
,txt
。 但是基于Unix
的系统是通过文件内容辨识文件类型的。所以需要加上第一行的代码。
第二行注释是为了告诉Python
解释器,按照UTF-8
编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。但是在编译器中也需要设置为UTF-8
编码
申明了UTF-8编码并不意味着你的.py文件就是UTF-8编码的,必须并且要确保文本编辑器正在使用UTF-8 without BOM编码:
2.4
Java 解码
在解码之前,先把你们的idea编码格式设置一下。 设置方法在这里。
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(getMethod.getResponseBodyAsStream(), StandardCharsets.ISO_8859_1));
String tmp = null;
StringBuilder htmlRet = new StringBuilder();
while ((tmp = reader.readLine()) != null) {
htmlRet.append(tmp).append("\r\n");
}
System.out.println(new String(htmlRet.toString().getBytes(StandardCharsets.ISO_8859_1), "GB2312")); // 还是乱码的话,可以将"GB2312" 改成"UTF-8试试"
} catch (IOException e) {
e.printStackTrace();
} finally {
getMethod.releaseConnection();
System.out.println(getMethod.getResponseCharSet());
}
主要参考:
-
文中可点击链接
-
廖雪峰网站
其实我认为生活是痛苦的,绝大多数时间是痛苦的,痛苦来自于各方各面,无时无刻不提醒着你。
痛苦更来自于我见过那些美好的事物,如果我没有见过阳光,我就不会知道我活在黑暗里面。痛苦和希望一直在正反两面反复的催促着我,让我穿越更多的痛苦,逐渐麻痹,去寻找希望,去变得更快乐。