2014-12-31 16 views
5

Chúng tôi đang chuẩn bị chuyển sang Python 3.4 và thêm unicode_literals. Mã của chúng tôi dựa hoàn toàn vào đường ống đến/từ các tiện ích bên ngoài bằng cách sử dụng mô-đun subprocess. Đoạn mã sau hoạt động tốt trên Python 2,7 đến ống UTF 8-strings để một tiểu trình:Làm thế nào để sửa một quy trình con chuyển mã Python sang unicode_literals?

kw = {} 
kw[u'stdin'] = subprocess.PIPE 
kw[u'stdout'] = subprocess.PIPE 
kw[u'stderr'] = subprocess.PIPE 
kw[u'executable'] = u'/path/to/binary/utility' 
args = [u'', u'-l', u'nl'] 

line = u'¡Basta Ya!' 

popen = subprocess.Popen(args,**kw) 
popen.stdin.write('%s\n' % line.encode(u'utf-8')) 
...blah blah... 

Những thay đổi sau đây ném lỗi này:

from __future__ import unicode_literals 

kw = {} 
kw[u'stdin'] = subprocess.PIPE 
kw[u'stdout'] = subprocess.PIPE 
kw[u'stderr'] = subprocess.PIPE 
kw[u'executable'] = u'/path/to/binary/utility' 
args = [u'', u'-l', u'nl'] 

line = u'¡Basta Ya!' 

popen = subprocess.Popen(args,**kw) 
popen.stdin.write('%s\n' % line.encode(u'utf-8')) 
Traceback (most recent call last): 
    File "test.py", line 138, in <module> 
    exitcode = main() 
    File "test.py", line 57, in main 
    popen.stdin.write('%s\n' % line.encode('utf-8')) 
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 0: ordinal not in range(128) 

Mọi góp ý để vượt qua UTF-8 thông qua ống?

+0

cảm ơn điều này, tôi đã có chính xác tình hình & vấn đề và thread này là một trợ giúp rất lớn. Tôi cũng thấy rằng trong một số trường hợp đơn giản, 'sh.py' (có sẵn tại đây: https://github.com/amoffat/sh) là một sự thay thế dễ dàng, di động và tương thích cho một số' quy trình con' điên cuồng. – user5359531

Trả lời

5

'%s\n' là một chuỗi unicode khi bạn sử dụng unicode_literals:

>>> line = u'¡Basta Ya!' 
>>> '%s\n' % line.encode(u'utf-8') 
'\xc2\xa1Basta Ya!\n' 
>>> u'%s\n' % line.encode(u'utf-8') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 0: ordinal not in range(128) 

gì xảy ra là giá trị được mã hóa line của bạn đang được giải mã để suy vào unicode '%s\n' chuỗi.

Bạn sẽ phải sử dụng một chuỗi byte thay thế; tiền tố chuỗi với b:

>>> from __future__ import unicode_literals 
>>> line = u'¡Basta Ya!' 
>>> b'%s\n' % line.encode(u'utf-8') 
'\xc2\xa1Basta Ya!\n' 

hoặc mã hóa sau suy:

>>> line = u'¡Basta Ya!' 
>>> ('%s\n' % line).encode(u'utf-8') 
'\xc2\xa1Basta Ya!\n' 

Trong Python 3, bạn sẽ phải viết bytestrings để ống anyway.

+0

Tôi đã xóa tùy chọn 'TextIOWrapper()' một lần nữa vì nó sẽ không hoạt động trong Python 2; các đối tượng đường ống không dựa trên các lớp cơ sở trừu tượng 'io' và gói không thành công. –

+0

Tuyệt vời! Nó hoạt động. Nó cũng tạo ra một cảnh báo ở một nơi khác. Tôi sẽ giải quyết điều đó trong một câu hỏi khác. Cảm ơn! – tahoar

+0

nếu 'universal_newlines = True' thì bạn có thể viết văn bản trên Python 3. – jfs

4

Nếu utf-8 đứng để mã hóa địa phương của bạn sau đó để giao tiếp sử dụng chuỗi Unicode, bạn có thể sử dụng universal_newlines=True trên Python 3:

#!/usr/bin/env python3 
from subprocess import Popen, PIPE 

p = Popen(['/path/to/binary/utility', '-l', 'nl'], 
      stdin=PIPE, stdout=PIPE, stderr=PIPE, 
      universal_newlines=True) 
out, err = p.communicate('¡Basta Ya!') 

Mã này hoạt động ngay cả khi mã hóa của miền địa phương không phải là utf-8. Đầu vào/đầu ra là các chuỗi Unicode ở đây (loại str).

Nếu subprocess đòi hỏi bất cứ điều gì utf-8 miền địa phương hiện tại sau đó được giao sử dụng bytestrings thay vì (pass/đọc byte):

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 
from __future__ import unicode_literals 
import os 
from subprocess import Popen, PIPE 

p = Popen(['/path/to/binary/utility', '-l', 'nl'], 
      stdin=PIPE, stdout=PIPE, stderr=PIPE) 
out, err = map(lambda b: b.decode('utf-8').replace(os.linesep, '\n'), 
       p.communicate((u'¡Basta Ya!' + os.linesep).encode('utf-8'))) 

Mã này hoạt động giống nhau trên cả hai Python 2 và 3.

Các vấn đề liên quan