python字符串前缀与字符编码设置的使用小技巧

这一周事情有点多。(开学后这段时间的摆烂造成了严重后果,组会后的心情十分低落。)

周末没有抽出太多时间探索新的知识,谨在此po一篇去年整理的旧文。

一、python字符串前缀使用小技巧

1、字符串前加u

例子: u"字符串中有中文"

含义:

前缀u表示该字符串是unicode编码,主要用于在Python2中,用在含有中文字符的字符串前,防止因为编码问题,导致中文出现乱码,另外一般要在文件开关标明编码方式采用utf8( #coding=utf-8 )。
由于在Python3中,所有字符串默认都是unicode字符串,这一前缀也就失去了作用。现在我们编写python代码基本用不上这个前缀了,但有时候翻看前人发表的模型或软件,偶尔可能会碰上。

1
2
3
4
str1 = u"字符串中有中文"
print(str1)
# 输出效果:
# 字符串中有中文

2、字符串前加r

例子: r"\n\t"

含义:

在普通字符串中,反斜线是转义符,代表一些特殊的内容,如换行符\n。前缀r表示该字符串是原始字符串,即\不是转义符,只是单纯的一个符号。常用于特殊的字符如换行符、正则表达式、文件路径。
注意不能在原始字符串结尾输入反斜线,否则Python不知道这是一个字符还是换行符(字符串最后用\表示换行),会报错:

1
2
3
4
>>> str21 = r'C:\Program File\my\path\' 
File "<stdin>", line 1
str21 = r'C:\Program File\my\path\'
SyntaxError: EOL while scanning string literal

如果一个字符串确实是以\结尾(如文件夹路径),可以再接一个转义\的字符串以规避上面的报错。

1
2
3
4
5
6
7
8
9
10
11
str21 = r'C:\Program File\my\path''\\'
print(str21)
str22 = r'\d+\.d*'
print(str22)
# 输出效果:
# C:\Program File\my\path\
# \d+\.d*
str23 = r'C:\Program File\my\path\\' # 这是第一个字符串的另一种写法,但输出结果会变得不一样(末尾变成两个`\`)。如果这个字符串仅用于储存文件夹路径的话,影响到也不大。
# 输出效果:
# C:\Program File\my\path\\

3、字符串前加b

例子: b'<h1>Hello World!</h1>'

含义:

前缀b表示该字符串是bytes类型。主要用在Python3中,因为Python3里默认的str是unicode类。Python2的str本身就是bytes类,所以可不用。
常用在如网络编程中,服务器和浏览器只认bytes类型数据。
如:send 函数的参数和 recv 函数的返回值都是bytes类型。
在Python3中,bytes和str的互相转换方式是

1
2
str.encode('utf-8')   # str转为bytes
bytes.decode('utf-8') # bytes转为str

示例:

1
2
3
4
str3 = b'<h1>Hello World!</h1>'
print(str3)
# 输出效果:
# b'<h1>Hello World!</h1>'

4、字符串前加f

例子:

1
2
3
>>> name = 'python'
>>> f'我的编程语言是:{name}'
'我的编程语言是:python'

含义:

前缀f是python3.6新加特性,用来格式化字符串。可以看出f前缀可以更方便的格式化字符串, 比format()方法可读性高且使用方便。而且加上f前缀后, 支持在大括号内, 运行Python表达式。你还可以用fr前缀来表示原生字符串。

1
2
3
4
5
6
7
8
9
code_name = 'python'
str4 = f'我的编程语言是:{code_name}'
# 等价于 `str4 = '我的编程语言是:{}'.format(code_name)`
print(str4)
import time
print(f"现在时间为{time.asctime()}")
# 输出效果:
# 我的编程语言是:python
# 现在时间为Sun Mar 10 18:44:43 2024

二、python在使用open()读取文件时的字符串编码问题

在windows平台下使用python内置函数 open() 时发现,当不传递encoding参数时,会自动采用gbk(cp936)编码打开文件,而当下很大部分文件的编码都是UTF-8,因此可能会出现文件打开失败的情况。

我们当然可以通过每次给open()函数手动传参encoding='utf-8',但是略显冗余,而且有很多外国的第三方包,里面调用的内置open()函数并没有提供接口让我们指定encoding,这就会导致这些包在windows平台上使用时,常会出现如 "UnicodeDecodeError: 'gbk' codec can't decode byte 0x91 in position 209: illegal multibyte sequence" 的报错

通过查看python文档分析原因:

if encoding is not specified the encoding used is platform dependent: locale.getpreferredencoding(False) is called to get the current locale encoding. (For reading and writing raw bytes use binary mode and leave encoding unspecified.)

可以发现当open()不传递encoding参数时,是默认调用locale.getpreferredencoding()方法来获取当前平台的“默认编码类型”,继续查看相关文档,发现有两种方法可以指定windows平台下Python运行时的“默认编码类型”。

1. 指定sys.flags.utf8_mode

通过运行python脚本时添加命令行参数 -X utf8(注意是跟在python.exe后面的interpreter option,不是跟在要运行脚本后面的parameters!)
指定sys.flags.utf8_mode参数之后,Python运行时会在很多场景下自动使用utf-8编码,而不是win默认的gbk(cp936)编码。

2. 直接重写_locale

1
2
3
4
5
6
import _locale
print(_locale._getdefaultlocale())
# 输出 `('zh_CN', 'cp936')` ,表明此时的locale设定依然是中文cp936
_locale._getdefaultlocale = (lambda*args: ['en_US', 'utf8'])
print(_locale._getdefaultlocale())
# 输出 `['en_US', 'utf8']` ,表明locale设定已经变为了utf-8

python解释器会取_getdefaultlocale()[1]作为默认编码类型,重写后,会改变当前运行环境下的所有模块的默认编码。

如果在windows下, 还可以在Python安装目录下的Lib/site-packages目录中,新建一个sitecustomize.py文件:

1
2
import _locale 
_locale._getdefaultlocale = (lambda *args: ['en_US', 'utf8'])

这样,可以让locale设置在每次python启动时都生效。

总之,使用以上两种方法后,windows平台下,open()函数会默认用utf-8编码打开文件。其实不止open()方法,跨模块、全局改变python解释器的默认编码为utf-8,会带来很多使用上的便利,而不需要被gbk编码报错的噩梦所纠缠。

参考文献: