V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
wulala1234
V2EX  ›  Python

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

  •  
  •   wulala1234 · 2018-05-29 09:27:03 +08:00 · 10271 次点击
    这是一个创建于 2398 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    在当前 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 编码,无法输出中文。 请问该如何解决?

    12 条回复    2020-02-23 12:31:32 +08:00
    zhjits
        1
    zhjits  
       2018-05-29 09:30:32 +08:00
    os.popen('ls', 'rb).read()
    出来一个 bytes 对象,然后 decode 一下
    wulala1234
        2
    wulala1234  
    OP
       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
        3
    hicdn  
       2018-05-29 10:19:12 +08:00
    3.6.5 在 MAC OSX 下没有问题
    est
        4
    est  
       2018-05-29 10:27:23 +08:00
    io 模块里有个可以 wrap 一下的。
    Sylv
        5
    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
        6
    alvin666  
       2018-05-29 10:52:25 +08:00 via Android
    五楼正解,popen 出来的通道是和你的 shell 的编码一致的,要么 bytes 然后 decode.encode,要么就改 shell 的编码
    wulala1234
        7
    wulala1234  
    OP
       2018-05-29 11:01:54 +08:00
    @hicdn 终端下( Mac OS X ) python3.5.1 和 python3.6.4 同运行正常。但在 sublime Text 3 中结果就是解码错误。
    Sylv
        8
    Sylv  
       2018-05-29 11:21:11 +08:00   ❤️ 1
    @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
        9
    wulala1234  
    OP
       2018-05-29 12:33:56 +08:00   ❤️ 1
    @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
        10
    LEORChn  
       2019-11-29 02:37:50 +08:00
    os.popen(cmd).buffer.read().decode(encoding='utf8')
    LEORChn
        11
    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
        12
    mouyong  
       2020-02-23 12:31:32 +08:00
    #11 代码有帮助
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2835 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 07:45 · PVG 15:45 · LAX 23:45 · JFK 02:45
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.