2014-12-31 1 views
5

Wir bereiten den Wechsel zu Python 3.4 vor und fügen unicode_literals hinzu. Unser Code stützt sich weitgehend auf das Piping zu/von externen Dienstprogrammen mit Hilfe des Unterprozessmoduls. Der folgende Codeausschnitt funktioniert auf Python 2.7 Rohr UTF-8-Strings zu einem Teilprozess fein:Wie behebt man eine Codierung, die den Python-Subprozess auf Unicode_literals migriert?

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... 

Die folgenden Änderungen werfen diese Fehler:

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) 

Irgendwelche Vorschläge UTF-8 durchlaufen das Rohr?

+0

Dank für Dies hatte ich genau die gleiche Situation und Problem und dieser Thread war eine große Hilfe. Ich fand auch, dass 'sh.py' (verfügbar hier: https://github.com/amoffat/sh) in einigen einfachen Fällen ein einfacher, tragbarer und kompatibler Ersatz für einige" Unterprozesse "war Verrücktheit. – user5359531

Antwort

5

'%s\n' ist ein Unicode-String, wenn Sie unicode_literals verwenden:

>>> 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) 

Was passiert, ist, dass Ihr codierten line Wert decodiert wird in den Unicode-String '%s\n' zu interpolieren.

Sie müssen stattdessen eine Byte-Zeichenfolge verwenden; Präfix die Zeichenfolge mit b:

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

oder kodieren nach Interpolation:

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

In Python 3, werden Sie bytestrings auf Rohre sowieso schreiben.

+0

Ich habe die Option 'TextIOWrapper()' wieder entfernt, da sie in Python 2 nicht funktioniert. Die Pipe-Objekte basieren nicht auf den abstrakten Basisklassen und das Wrapping schlägt fehl. –

+0

Ausgezeichnet! Es klappt. Es erzeugte auch eine Warnung an einem anderen Ort. Ich werde das in einer anderen Frage ansprechen. Vielen Dank! – tahoar

+0

Wenn 'universal_newlines = True', dann könnten Sie Text auf Python 3 schreiben. – jfs

4

Wenn utf-8 steht für Ihre Locale dann Unicode-Zeichenfolgen mit zu kommunizieren, könnten Sie universal_newlines=True auf Python 3 verwenden:

#!/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!') 

Der Code funktioniert auch, wenn die Codierung Gebietsschema ist nicht utf-8. Eingabe/Ausgabe sind Unicode-Zeichenfolgen hier (str Typ).

Wenn die subprocess erfordert utf-8 unabhängig von der aktuellen Locale dann statt mit bytestrings kommunizieren (Pass/Lese Bytes):

#!/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'))) 

Der Code funktioniert das gleiche auf beiden Python 2 und 3.

Verwandte Themen