Python基础语法

u参数

有的时候在运行Python的时候,会遇到python -u xx.py,这是什么意思呢?

背景

python中标准错误(std.err)和标准输出(std.out)的输出规则:标准输出默认需要缓存后再输出到屏幕,而标准错误则直接打印到屏幕。如在test.py中有如下内容:

1
2
3
4
5
6
import sys

sys.stdout.write("Stdout1")
sys.stderr.write("Stderr1")
sys.stdout.write("Stdout2")
sys.stderr.write("Stderr2")

其中sys.stdout.write()和sys.stderr.write()均是向屏幕打印的语句。其实python中的print语句就是调用了sys.stdout.write(),例如在打印对象调用print obj 时,事实上是调用了 sys.stdout.write(obj+’\n’)。

预想的结果是:

1
Stdout1Stderr1Stdout2Stderr2

实际的结果为:

1
Stderr1Stderr2Stdout1Stdout2

原因:是python缓存机制,虽然stderr和stdout默认都是指向屏幕的,但是stderr是无缓存的,程序往stderr输出一个字符,就会在屏幕上显示一个;而stdout是有缓存的,只有遇到换行或者积累到一定的大小,才会显示出来。这就是为什么上面的会最先显示两个stderr的原因。

注意要使用python test.py才能验证,不要在ipython中。

-u参数的作用

python命令加上-u(unbuffered)参数后会强制其标准输出也同标准错误一样不通过缓存直接打印到屏幕。

运行结果:

1
Stdout1Stderr1Stdout2Stderr2

字符串前u,r,b

u/U:表示unicode字符串。不是仅仅是针对中文, 可以针对任何的字符串,代表是对字符串进行unicode编码。

r/R:非转义的原始字符串。与普通字符相比,其他相对特殊的字符,其中可能包含转义字符,即那些,反斜杠加上对应字母,表示对应的特殊含义的,比如最常见的”\n”表示换行,”\t”表示Tab等。而如果是以r开头,那么说明后面的字符,都是普通的字符了,即如果是“\n”那么表示一个反斜杠字符,一个字母n,而不是表示换行了。以r开头的字符,常用于正则表达式,对应着re模块。变量前面可以添加repr

b:bytes。

字符编码

字符在内存中的编码

字符在内存里的表示是unicode,如果要存盘或者发到网络就经过编码,具体为使用encode函数将其转为bytes形式,然后对端收到依次解码,具体为使用decode函数将其转为str形式。

Python 3里面,str在内存里是unicode表示的,所以’中文’ == ‘\u4e2d\u6587’,类型都是str。

1
2
3
4
5
6
7
8
9
10
11
>>> '\u4e2d\u6587'
'中文'
# 1个汉字用unicode表示,是2个byte,这里\u4e2d是十六进制的写法。4e是0100 1110,2d是0010 1101,合起来16位2bytes。

# encode默认参数是'utf-8'
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
>>> '\u4e2d\u6587'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'

# '\u4e2d'是unicode表示的字符,unicode只是表示它的一个形式,但本质上被表示的对象还是字符,是str而不是bytes

对str编码,本质上还是对str表示的字符编码,可以用ascii(如果字符属于ascii字符集的话),也可以用utf-8,也可以用gb2312(中文),都行。但是注意并没有unicode这个encode形式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>>> ('\u0041').encode('ascii')
b'A'
>>> 'A'.encode('ascii')
b'A'
>>> 'A'.encode('utf-8')
b'A'
>>> '中文'.encode('gb2312')
b'\xd6\xd0\xce\xc4'

# 错误的方式
>>> '中文'.encode('ascii')
---------------------------------------------------------------------------
UnicodeEncodeError Traceback (most recent call last)
<ipython-input-19-76f41cd8dafa> in <module>
----> 1 '中文'.encode('ascii')

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

# 错误的方式
>>> '中文'.encode('unicode')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
LookupError: unknown encoding: unicode

编码后是bytes,俗称的01010101,如果这个010101不在ascii的表示范围内,就会显示成\x(010101的十六进制形式)。

这就是说,像汉字编码成bytes以后,去查看这个bytes肯定只能看到\x系列,因为这个bytes的内容肯定不在ascii范围内;但如果换英文,就可以看到对应的英文字母,不过不要误会,本质上它还是没有含义的010101而不是字符。

1
2
3
4
5
6
7
8
9
10
11
>>> "abc".encode('utf-8')
b'abc'
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
# 1个汉字,按utf-8编码,一般是3个bytes,\xe4是十六进制表示的1个byte。

>>> 'A'.encode('ascii')
b'A'
# 注意区分b'A'和'A',虽然编码后看到的结果是b'A',但这个结果跟'A'没有关系。
# 这个结果b'A'就是一串0101,具体说就是0100 0001这样一个1个byte,是表示'A'还是其他符号,要看解编码形式。
# b'A'已经是内存里的形式,占1个byte;而'A',由于我们说python 3在内存里是按unicode形式表示字符,所以占的是2个byte。

由于对同一个英文字符,ascii编码和utf-8编码的结果是一致的,所以用一个编码然后再另一个解码,是可以成功还原的。不过一般是不会这么做的。

1
2
>>>'abc'.encode('ascii').decode('utf-8')
'abc'

爬虫若拿到的是形如0101的bytes,首先会指定一个编码做decode,这时候可能会碰到部分不符合出错,可以加上ignore参数试试。

1
2
>>> b'\xe4\xb8\xad\xff'.decode('utf-8', errors='ignore')
'中'

ord函数获取字符的整数表示和chr数把编码转换为对应的字符

1
2
3
4
5
6
7
8
 ord('A')
65
ord('中')
20013
chr(66)
'B'
chr(25991)
'文'

另外,对str和对bytes用len,意义是不同的。

len(str)统计字符数,len(bytes)统计bytes数,即——这串010101一共是多少个bits,除以8就是bytes。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> len('中文')
2
# len(str)统计字符数

>>> byte1 = '中文'.encode('gb2312')
>>> byte2 = '中文'.encode('utf-8')
>>> byte1
b'\xd6\xd0\xce\xc4'
>>> byte2
b'\xe4\xb8\xad\xe6\x96\x87'
>>> len(byte1)
4
>>> len(byte2)
6
# len(bytes)统计bytes数。

如何判断字符串编码方式

我们经常遇到的编码其实主要的就只有三种:utf-8,gbk,unicode

  • unicode一般是 \u 带头的,然后后面跟四位数字或字母,例如 \u6d4b\u8bd5 ,一个 \u 对应一个汉字
  • utf-8一般是 \x 带头的,后面跟两位字母或数字,例如 \xe6\xb5\x8b\xe8\xaf\x95\xe5\x95\x8a ,三个 \x 代表一个汉字
  • gbk一般是 \x 带头的,后面跟两位字母或数字,例如 \xb2\xe2\xca\xd4\xb0\xa1 ,两个 \x 代表一个汉字。

除了粗略的根据是否乱码看编码方式外,还可以用chardet模块猜测、

1
2
3
4
5
import chardet

raw = u'我是一只小小鸟'
print(chardet.detect(raw.encode('utf-8')))
print(chardet.detect(raw.encode('gbk')))

输出:

1
2
{'encoding': 'utf-8', 'confidence': 0.99, 'language': ''}
{'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'}

chardet模块可以计算这个字符串是某个编码的概率,基本对于99%的应用场景,这个模块都够用了。

\x字符串转码

在使用Python的时候,经常遇到类似于\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xb8\x96\xe7\x95\x8c的字符串,其实它是utf-8编码,但数据类型是字符串类型,而不是bytes类型的utf-8编码。如果我们需要将\x开头的字符串编码转换中文。

方法一:先将字符串编码指定为unicode_escape,

1
2
3
4
5
s = '\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xb8\x96\xe7\x95\x8c'
s = s.encode('unicode_escape')

# 得到bytes类型数据(单斜杠变成双斜杠)
b'\\xe4\\xbd\\xa0\\xe5\\xa5\\xbd\\xe4\\xb8\\x96\\xe7\\x95\\x8c'

接着再对bytes类型进行utf-8解码,得到字符串,将字符串中的 “ \x “ 替换为 “ % “,

1
2
3
4
5
6
# bytes to string
s.decode('utf-8')

ss = s.decode('utf-8').replace('\\x', '%')
# 替换作用就是将字符串改为url的utf-8编码格式
%e4%bd%a0%e5%a5%bd%e4%b8%96%e7%95%8c

最后利用urllib中的unquote方法将url编码解码,得到中文

1
2
3
import urllib.parse
un = urllib.parse.unquote(ss)
# 你好世界

方法二:

1
2
3
4
5
6
7
s = '\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xb8\x96\xe7\x95\x8c'

s.encode('raw_unicode_escape')
# b'\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xb8\x96\xe7\x95\x8c'

s.encode('raw_unicode_escape').decode()
# '你好世界'

\u字符串转码

在使用Python的时候,经常遇到类似于'\u5403\u9e21\u6218\u573a'的字符串,其实它是Unicode编码,可以直接解码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
In [63]: s = '\u5403\u9e21\u6218\u573a'

In [64]: s
Out[64]: '吃鸡战场'

In [74]: a = '\u5403\u9e21\u6218\u573a'

In [75]: a
Out[75]: '吃鸡战场'

In [76]: b = a.encode('unicode-escape')

In [77]: b
Out[77]: b'\\u5403\\u9e21\\u6218\\u573a'

In [78]: c = b.decode('utf-8')

In [79]: c
Out[79]: '\\u5403\\u9e21\\u6218\\u573a'

In [80]: c.encode()
Out[80]: b'\\u5403\\u9e21\\u6218\\u573a'

In [81]: c.encode().decode('unicode-escape')
Out[81]: '吃鸡战场'

In [82]: b.decode('unicode-escape')
Out[82]: '吃鸡战场'

In [95]: c.encode() == b
Out[95]: True

In [103]: b'\u5403\u9e21\u6218\u573a'
Out[103]: b'\\u5403\\u9e21\\u6218\\u573a'

关于unicode-escape以及raw-unicode-escape,可以参考codecs —- 编解码器注册和相关基类

参考

解析python 命令的-u参数
关于\x开头的字符串编码转换中文解决方法
【Python】笔记:关于\u和\x
python中的编码和解码及\x和\u问题
python学习:字符串前面添加u,r,b的含义
python对变量的字符串不转义 变量如何加r
https://blog.csdn.net/mijichui2153/article/details/105516152
不得不知道的Python字符串编码相关的知识

------ 本文结束------
坚持原创技术分享,您的支持将鼓励我继续创作!

欢迎关注我的其它发布渠道