如何将 python3 中 os.popen()的默认编码由 ascii 修改为 ‘utf-8’ ?

2018-05-29 09:27:03 +08:00
 wulala1234

遇到一个编码问题,问题简化如下:

在当前 desktop 目录下,有如下内容:

desktop $ ls
client.py	server.py	中文测试
arcpy.txt	codetest.py	test.py

如上所示有一个中文命名的文件 ----> 中文测试

# -*- coding:utf-8 -*-
# python3.5.1

import os,sys
print (sys.getdefaultencoding()) #系统默认编码

dir_list  = os.listdir()
for li in dir_list:
	print (li)

输出如下:

utf-8
arcpy.txt
client.py
codetest.py
server.py
test.py
中文测试

可以看出默认编码为 utf-8,os.listdir()命令可以正常输出中文字符。

在使用 os.popen()时:

# -*- coding:utf-8 -*-
# python3.5.1

import os,sys
print (sys.getdefaultencoding()) #系统默认编码

dir_list = os.popen('ls','r').read()
for li in dir_list:
	print (li)

报错如下:

utf-8
Traceback (most recent call last):
  File "Desktop/codetest.py", line 8, in <module>
    dir_list = os.popen('ls','r').read()
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 76: ordinal not in range(128)

既虽然默认编码为‘ utf-8',但 popen()读取目录时,依旧采用 ascii 编码,无法输出中文。 请问该如何解决?

10100 次点击
所在节点    Python
12 条回复
zhjits
2018-05-29 09:30:32 +08:00
os.popen('ls', 'rb).read()
出来一个 bytes 对象,然后 decode 一下
wulala1234
2018-05-29 09:47:43 +08:00
@zhjits 查看 help(os.popen) 信息如下:
```
Help on function popen in module os:

popen(cmd, mode='r', buffering=-1)
# Supply os.popen()
```
查询到的信息:参数 mode 只支持‘ r'(默认),'w',两种模式。。。

代入‘ rb ’,报错如下:
```
raise ValueError("invalid mode %r" % mode)
ValueError: invalid mode 'rb'
```
hicdn
2018-05-29 10:19:12 +08:00
3.6.5 在 MAC OSX 下没有问题
est
2018-05-29 10:27:23 +08:00
io 模块里有个可以 wrap 一下的。
Sylv
2018-05-29 10:49:21 +08:00
os.popen 相当于 subprocess.Popen + io.TextIOWrapper,io.TextIOWrapper 默认使用的编码是 locale.getpreferredencoding(False)。

class io.TextIOWrapper(buffer, encoding=None, errors=None, newline=None, line_buffering=False, write_through=False)
A buffered text stream over a BufferedIOBase binary stream. It inherits TextIOBase.
encoding gives the name of the encoding that the stream will be decoded or encoded with. It defaults to locale.getpreferredencoding(False).

locale.getpreferredencoding(do_setlocale=True)
Return the encoding used for text data, according to user preferences. User preferences are expressed differently on different systems, and might not be available programmatically on some systems, so this function only returns a guess.

这个编码应该是由语言环境变量判断来的,在我这终端下( Mac OS )这个编码是 'UTF-8',所以你的代码在我这可以正常运行。在你的运行环境中这个编码是 'ascii',于是就出错了。

解决办法 1:改用 subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE),然后手动 decode('utf-8')。
解决办法 2:尝试修改你的语言环境变量,使得 locale.getpreferredencoding 返回 'UTF-8' 编码。
alvin666
2018-05-29 10:52:25 +08:00
五楼正解,popen 出来的通道是和你的 shell 的编码一致的,要么 bytes 然后 decode.encode,要么就改 shell 的编码
wulala1234
2018-05-29 11:01:54 +08:00
@hicdn 终端下( Mac OS X ) python3.5.1 和 python3.6.4 同运行正常。但在 sublime Text 3 中结果就是解码错误。
Sylv
2018-05-29 11:21:11 +08:00
@wulala1234
Sublime Text 的 Build System 也是用 subprocess.Popen 来运行用户脚本的,在这种情况下因为不存在 LANG 环境变量,所以 locale.getpreferredencoding 返回的是 'US-ASCII' 编码。

可以在 Build System 的配置文件 Python.sublime-build 中添加 LANG 变量来使其变为 'UTF-8':
"env": {"PYTHONIOENCODING": "utf-8", "LANG": "en_US.UTF-8"}

参考:
https://stackoverflow.com/questions/42101759/how-to-change-the-preferred-encoding-in-sublime-text-3-for-macos
wulala1234
2018-05-29 12:33:56 +08:00
@Sylv 非常感谢您的回复。
在终端里
$locale
LANG="zh_CN.UTF-8"
LC_COLLATE="zh_CN.UTF-8"
LC_CTYPE="zh_CN.UTF-8"
LC_MESSAGES="zh_CN.UTF-8"
LC_MONETARY="zh_CN.UTF-8"
LC_NUMERIC="zh_CN.UTF-8"
LC_TIME="zh_CN.UTF-8"
LC_ALL=

LC_ALL 无默认值

$ python 3.5
>>> import locale
>>> locale.getpreferredencoding()
'UTF-8'

而在 sublime Text3 中
# python3.5
>>>import locale
>>>print (locale.getpreferredencoding())
US-ASCII

>>>print (locale.getdefaultlocale())
(None, None)

因为编码不同产生了错误。

您上面的回复是一种解决方式

还可以在代码中临时设置语言环境

>>>locale.setlocale(locale.LC_ALL,'zh_CN.UTF-8')

解决问题。

不过您的解决方式是一劳永逸的。

参考:
https://docs.python.org/2/library/locale.html#locale.getdefaultlocale

接下来去查查 sys.getdefaultencoding()与 locale.getdefaultlocale()的区别了~~~
LEORChn
2019-11-29 02:37:50 +08:00
os.popen(cmd).buffer.read().decode(encoding='utf8')
LEORChn
2019-11-29 02:53:48 +08:00
# 不好意思,刚刚以为代码简化一行之后就没事了,后来发现还是图样
def exec(cmd:str):
pip = os.popen(cmd) # 这个地方不能合并一行写,会出错说 read of closed file
return pip.buffer.read().decode(encoding='utf8')
mouyong
2020-02-23 12:31:32 +08:00
#11 代码有帮助

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/458543

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX