Преобразовать байты в строку?

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

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

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

>>> command_stdout
b'total 0n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file1n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file2n'

однако я хотел бы работать с выводом в качестве обычной строки 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 0n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file1n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file2n'

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

16 ответов


вам нужно декодировать объект bytes для создания строки:

>>> 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 = [112, 52, 52]
"".join(map(chr, bytes))
>> p44

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

b'hello'.decode(encoding)

или

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. Смотрите недостающие точки в Макет Кодовой Страницы - это где Python задыхается с печально известным 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: я решил реализовать slash escaping decode, который работает как для 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]

пока @Аарон мяэнпяа: дизайн для всех это!--5--> работает, а недавно попросил

есть ли более простой способ? 'fhand.читать.)(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'':

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

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

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

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

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

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


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

import os
import subprocess

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

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

если вы передадите затем 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() (который использует Windows API для Юникода), например, '\xb6' можно заменить на '\x14'-в Python cp437 кодек карты b'\x14' для управления символом U + 0014 вместо U + 00B6 (¶). Для поддержки имен файлов с произвольными символами Юникода см. декодировать вывод poweshell, возможно, содержащий символы unicode, отличные от 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():

AttributeError: 'str' object has no attribute 'decode'

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

>>> my_byte_str
b'Hello World'

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

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

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

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

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

почему? Попробуйте это с многострочным входом.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)

будет реплицировать исходный файл.


для Python 3 это намного безопаснее и весть подход для преобразования из byte to 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))

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

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