字符串和编码

字符串和编码

标签(空格分隔): Python


1、历史

1.1 字符串

字符串:是一种数据类型,但是字符串比较特殊的还有一个就是编码问题。

1.2 bit和byte

bit是比特,byte是字节

\[8 bit = 1 byte \]

\[1024 byte = 1 KB \]

因为计算机只能处理数字,要处理文本,就必须把文本装换成数字才能处理,最早的计算机载机设计时采用\(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个字节)。现代操作系统和大多数编程语言都直接支持UnicodeASCII用的是一个字节,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.

  • pythonbytes类型的数据用带'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

在操作字符串时,我们经常遇到strbytes的互相转换。为了避免乱码问题,应当始终坚持使用UTF-8编码对strbytes进行转换。

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

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

第一行注释是为了告诉Linux/OS X系统,这是一个Python可执行程序,Windows系统会忽略这个注释;Windows是通过文件名后缀辨识文件类型的,例如exetxt。 但是基于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());
}

主要参考:

  • 文中可点击链接

  • 廖雪峰网站

    其实我认为生活是痛苦的,绝大多数时间是痛苦的,痛苦来自于各方各面,无时无刻不提醒着你。
    痛苦更来自于我见过那些美好的事物,如果我没有见过阳光,我就不会知道我活在黑暗里面。

    痛苦和希望一直在正反两面反复的催促着我,让我穿越更多的痛苦,逐渐麻痹,去寻找希望,去变得更快乐。

posted @ 2021-05-26 00:44  X-POWER  阅读(356)  评论(0编辑  收藏  举报