Вопрос

Я использую этот код для получения стандартного вывода из внешней программы:

>>> from subprocess import *
>>> command_stdout = Popen(['ls', '-l'], stdout=PIPE).communicate()[0]

Метод communication() возвращает массив байтов:

>>> command_stdout
b'total 0\n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file1\n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file2\n'

Однако я хотел бы работать с выводом как с обычной строкой Python.Чтобы я мог распечатать это так:

>>> print(command_stdout)
-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file1
-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file2

Я думал, что это то, что binascii.b2a_qp() метод предназначен для, но когда я попробовал его, я снова получил тот же массив байтов:

>>> binascii.b2a_qp(command_stdout)
b'total 0\n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file1\n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file2\n'

Кто-нибудь знает, как преобразовать значение байтов обратно в строку?Я имею в виду использование «батарейок» вместо того, чтобы делать это вручную.И я бы хотел, чтобы с Python 3 все было в порядке.

Это было полезно?

Решение

Вам нужно декодировать объект байтов, чтобы создать строку:

>>> b"abcde"
b'abcde'

# utf-8 is used here because it is a very common encoding, but you
# need to use the encoding your data is actually in.
>>> b"abcde".decode("utf-8") 
'abcde'

Другие советы

Я думаю, что этот способ прост:

bytes_data = [112, 52, 52]
"".join(map(chr, bytes_data))
>> p44

Вам нужно декодировать строку байтов и преобразовать ее в строку символов (Юникод).

На Python 2

encoding = 'utf-8'
b'hello'.decode(encoding)

На Python 3

encoding = 'utf-8'
str(b'hello', encoding)

Если вы не знаете кодировку, то для чтения двоичного ввода в строку способом, совместимым с Python 3 и Python 2, используйте древнюю MS-DOS. cp437 кодировка:

PY3K = sys.version_info >= (3, 0)

lines = []
for line in stream:
    if not PY3K:
        lines.append(line)
    else:
        lines.append(line.decode('cp437'))

Поскольку кодировка неизвестна, ожидайте, что неанглийские символы будут переведены в символы cp437 (Английские символы не переводятся, поскольку они совпадают с большинством однобайтовых кодировок и UTF-8).

Декодирование произвольного двоичного ввода в UTF-8 небезопасно, поскольку вы можете получить следующее:

>>> b'\x00\x01\xffsd'.decode('utf-8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 2: invalid
start byte

То же самое относится и к latin-1, который был популярен (по умолчанию?) для Python 2.Посмотрите недостающие пункты в Макет кодовой страницы - именно здесь Питон задыхается от печально известных ordinal not in range.

ОБНОВЛЕНИЕ 20150604:Ходят слухи, что Python 3 имеет surrogateescape стратегия ошибок для кодирования данных в двоичные данные без потери данных и сбоев, но требует тестов преобразования [binary] -> [str] -> [binary] для проверки производительности и надежности.

ОБНОВЛЕНИЕ 20170116:Благодаря комментарию Nearoo - также есть возможность сократить все неизвестные байты с помощью backslashreplace обработчик ошибок.Это работает только для Python 3, поэтому даже при использовании этого обходного пути вы все равно будете получать непоследовательный вывод из разных версий Python:

PY3K = sys.version_info >= (3, 0)

lines = []
for line in stream:
    if not PY3K:
        lines.append(line)
    else:
        lines.append(line.decode('utf-8', 'backslashreplace'))

Видеть https://docs.python.org/3/howto/unicode.html#python-s-unicode-support для получения подробной информации.

ОБНОВЛЕНИЕ 20170119:Я решил реализовать декодирование с экранированием косой черты, которое работает как для Python 2, так и для Python 3.Это должно быть медленнее, что cp437 решение, но оно должно давать идентичные результаты в каждой версии Python.

# --- preparation

import codecs

def slashescape(err):
    """ codecs error handler. err is UnicodeDecode instance. return
    a tuple with a replacement for the unencodable part of the input
    and a position where encoding should continue"""
    #print err, dir(err), err.start, err.end, err.object[:err.start]
    thebyte = err.object[err.start:err.end]
    repl = u'\\x'+hex(ord(thebyte))[2:]
    return (repl, err.end)

codecs.register_error('slashescape', slashescape)

# --- processing

stream = [b'\x80abc']

lines = []
for line in stream:
    lines.append(line.decode('utf-8', 'slashescape'))

В Python 3, кодировка по умолчанию "utf-8", поэтому вы можете использовать напрямую:

b'hello'.decode()

что эквивалентно

b'hello'.decode(encoding="utf-8")

С другой стороны, в Python 2, кодировка по умолчанию соответствует кодировке строки по умолчанию.Таким образом, вам следует использовать:

b'hello'.decode(encoding)

где encoding это кодировка, которую вы хотите.

Примечание: поддержка аргументов ключевых слов была добавлена ​​в Python 2.7.

Я думаю, что вы на самом деле хотите этого:

>>> from subprocess import *
>>> command_stdout = Popen(['ls', '-l'], stdout=PIPE).communicate()[0]
>>> command_text = command_stdout.decode(encoding='windows-1252')

Ответ Аарона был правильным, за исключением того, что вам нужно знать, КАКУЮ кодировку использовать.И я считаю, что Windows использует «windows-1252».Это будет иметь значение только в том случае, если в вашем контенте есть какие-то необычные (не ASCII) символы, но тогда это будет иметь значение.

Кстати, тот факт, что это ДЕЙСТВИТЕЛЬНО имеет значение, является причиной того, что Python перешел к использованию двух разных типов для двоичных и текстовых данных:он не может волшебным образом конвертировать между ними, потому что не знает кодировку, пока вы ей не сообщите!Единственный способ узнать это — прочитать документацию Windows (или прочитать ее здесь).

Установите для Universal_newlines значение True, т.е.

command_stdout = Popen(['ls', '-l'], stdout=PIPE, universal_newlines=True).communicate()[0]

Пока Ответ @Аарона Маенпаа просто работает, пользователь недавно спросил:

Есть ли более простой способ?'fhand.read().decode("ASCII")' [...] Это так долго!

Вы можете использовать:

command_stdout.decode()

decode() имеет стандартный аргумент:

codecs.decode(obj, encoding='utf-8', errors='strict')

Чтобы интерпретировать последовательность байта как текст, вы должны знать соответствующее кодирование символов:

unicode_text = bytestring.decode(character_encoding)

Пример:

>>> b'\xc2\xb5'.decode('utf-8')
'µ'

ls Команда может выдать вывод, который нельзя интерпретировать как текст.Имена файлов на Unix может быть любой последовательности байтов, кроме Slash b'/' и нольb'\0':

>>> open(bytes(range(0x100)).translate(None, b'\0/'), 'w').close()

Попытка декодировать такой байтовый суп с использованием кодировки utf-8 поднимает UnicodeDecodeError.

Может быть и хуже.Декодирование может завершиться незаметно и привести к моджибакеесли вы используете неправильную несовместимую кодировку:

>>> '—'.encode('utf-8').decode('cp1252')
'—'

Данные повреждены, но ваша программа не знает, что произошел сбой.

В общем, какую кодировку символов использовать, не заложено в самой последовательности байтов.Вы должны передать эту информацию вне диапазона.Некоторые исходы более вероятны, чем другие, и поэтому chardet существует модуль, который может предполагать кодировка символов.Один скрипт Python может использовать несколько кодировок символов в разных местах.


ls вывод можно преобразовать в строку Python, используя os.fsdecode()функция, которая успешна даже для Необратимые имена файлов (оно используетsys.getfilesystemencoding() и surrogateescape обработчик ошибок на UNIX):

import os
import subprocess

output = os.fsdecode(subprocess.check_output('ls'))

Чтобы получить исходные байты, вы можете использовать os.fsencode().

Если ты пройдешь universal_newlines=True параметр тогда subprocess используетlocale.getpreferredencoding(False) для декодирования байтов, например, это может бытьcp1252 в Windows.

Чтобы декодировать поток байтов на лету,io.TextIOWrapper()может быть использован: пример.

Различные команды могут использовать разные кодировки символов для их вывода, например, dir внутренняя команда (cmd) может использовать cp437.Чтобы декодировать его выход, вы можете явно передать кодирование (Python 3.6+):

output = subprocess.check_output('dir', shell=True, encoding='cp437')

Имена файлов могут отличаться от os.listdir() (который использует API Windows Unicode), например, '\xb6' можно заменить на '\x14'–Python's CP437 Codec Maps b'\x14' контролировать символ U+0014 вместо U+00B6 (¶).Чтобы поддерживать имена файлов с произвольными символами Юникода, см. Декодировать выходные данные poweshell, возможно, содержащие символы Юникода, отличные от ascii, в строку Python

Поскольку этот вопрос на самом деле задается о subprocess вывод, у вас есть более прямой подход, поскольку Popen принимает кодирование ключевое слово (в Python 3.6+):

>>> from subprocess import Popen, PIPE
>>> text = Popen(['ls', '-l'], stdout=PIPE, encoding='utf-8').communicate()[0]
>>> type(text)
str
>>> print(text)
total 0
-rw-r--r-- 1 wim badger 0 May 31 12:45 some_file.txt

Общий ответ для других пользователей: декодировать байты в текст:

>>> b'abcde'.decode()
'abcde'

Без всяких аргументов, sys.getdefaultencoding() будет использован.Если ваши данные не sys.getdefaultencoding(), то вы должны явно указать кодировку в decode вызов:

>>> b'caf\xe9'.decode('cp1250')
'café'

Если вы должны получить следующее, попробовав decode():

Ошибка атрибута:Объект 'str' не имеет атрибута 'decode'

Вы также можете указать тип кодировки прямо в приведении:

>>> my_byte_str
b'Hello World'

>>> str(my_byte_str, 'utf-8')
'Hello World'

При работе с данными из систем Windows (с \r\n окончания строк), мой ответ:

String = Bytes.decode("utf-8").replace("\r\n", "\n")

Почему?Попробуйте это с помощью многострочного файла Input.txt:

Bytes = open("Input.txt", "rb").read()
String = Bytes.decode("utf-8")
open("Output.txt", "w").write(String)

Все окончания строк будут удвоены (до \r\r\n), что приводит к появлению дополнительных пустых строк.Функции чтения текста Python обычно нормализуют окончания строк, чтобы в строках использовались только \n.Если вы получаете двоичные данные из системы Windows, у Python нет возможности сделать это.Таким образом,

Bytes = open("Input.txt", "rb").read()
String = Bytes.decode("utf-8").replace("\r\n", "\n")
open("Output.txt", "w").write(String)

скопирует ваш исходный файл.

Я сделал функцию для очистки списка

def cleanLists(self, lista):
    lista = [x.strip() for x in lista]
    lista = [x.replace('\n', '') for x in lista]
    lista = [x.replace('\b', '') for x in lista]
    lista = [x.encode('utf8') for x in lista]
    lista = [x.decode('utf8') for x in lista]

    return lista

Для Python 3 это гораздо более безопасный и Пифонический подход к преобразованию из byte к string:

def byte_to_str(bytes_or_str):
    if isinstance(bytes_or_str, bytes): #check if its in bytes
        print(bytes_or_str.decode('utf-8'))
    else:
        print("Object not of byte type")

byte_to_str(b'total 0\n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file1\n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file2\n')

Выход:

total 0
-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file1
-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file2
def toString(string):    
    try:
        return v.decode("utf-8")
    except ValueError:
        return string

b = b'97.080.500'
s = '97.080.500'
print(toString(b))
print(toString(s))

Если вы хотите преобразовать любые байты, а не только строку, преобразованную в байты:

with open("bytesfile", "rb") as infile:
    str = base64.b85encode(imageFile.read())

with open("bytesfile", "rb") as infile:
    str2 = json.dumps(list(infile.read()))

Однако это не очень эффективно.Он превратит картинку размером 2 Мб в 9 Мб.

От http://docs.python.org/3/library/sys.html,

Чтобы записать или прочитать двоичные данные из/в стандартные потоки, используйте базовый двоичный буфер.Например, чтобы записать байты в стандартный вывод, используйте sys.stdout.buffer.write(b'abc').

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top