строка - байтовые строки python




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

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

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


В то время как ответ @Aaron Maenpaa работает, пользователь недавно спросил

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

Ты можешь использовать

command_stdout.decode()

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

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


Вам необходимо декодировать строку байтов и включить ее в строку символов (юникод).

b'hello'.decode(encoding)

или же

str(b'hello', encoding)

Для Python 3 это гораздо более безопасный и Pythonic подход к преобразованию из 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

Если вы не знаете кодировку, то для чтения двоичного ввода в строку в 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. См. Недостающие точки в макете Codepage - это то, где Python задыхается с позорным ordinal not in range .

UPDATE 20150604 : Ходят слухи, что Python 3 имеет стратегию ошибок surrogateescape для кодирования данных в двоичные данные без потери данных и сбоев, но для проверки эффективности и надежности нужны тесты преобразования [binary] -> [str] -> [binary] .

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

UPDATE 20170119 : Я решил реализовать декодирование слэш-экранированием, которое работает как для Python 2, так и для Python 3. Это должно быть медленнее, cp437 решение 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'))

Из http://docs.python.org/3/library/sys.html ,

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


Поскольку этот вопрос действительно спрашивает о выходе subprocess , у вас есть более прямой подход, доступный, поскольку Popen принимает ключевое слово для encoding (в 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é'

Установите universal_newlines в True, т.е.

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

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

unicode_text = bytestring.decode(character_encoding)

Пример:

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

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

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

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

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

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

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

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

Вывод ls может быть преобразован в строку Python с использованием функции os.fsdecode() которая преуспевает даже для недоказуемых имен файлов (она использует sys.getfilesystemencoding() и обработчик ошибок sys.getfilesystemencoding() в 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() : example .

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

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

Имена файлов могут отличаться от os.listdir() (который использует Windows Unicode API), например, '\xb6' может быть заменен на '\x14' -кодек cp437-кодека cp437 b'\x14' для управления символом U + 0014 вместо U + 00B6 (¶). Чтобы поддерживать имена файлов с произвольными символами Unicode, см. Раздел «Декодирование вывода poweshell, возможно содержащее символы un-ascii unicode в строку python


Я думаю, что так легко:

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

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

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

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






python-3.x