Преобразовать байты в строку?
Я использую этот код для получения стандартного вывода из внешней программы:
>>> 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 все было в порядке.
26 ответов
Вам нужно декодировать объект 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'
Вам нужно декодировать строку байтов и превратить ее в символьную строку (юникод).
b'hello'.decode(encoding)
или на питоне 3
str(b'hello', encoding)
Я думаю, что так легко
bytes = [112, 52, 52]
"".join(map(chr, bytes))
>> p44
В Python 3 кодировкой по умолчанию является "utf-8"
, так что вы можете использовать напрямую:
b'hello'.decode()
что эквивалентно
b'hello'.decode(encoding="utf-8")
С другой стороны, в Python 2 кодировка по умолчанию соответствует строковому кодированию по умолчанию. Таким образом, вы должны использовать:
b'hello'.decode(encoding)
где encoding
это кодировка, которую вы хотите.
Примечание: поддержка аргументов ключевых слов была добавлена в Python 2.7.
Если вы не знаете кодировку, то для чтения двоичного ввода в строку в 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 Layout - это где 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 для получения подробной информации.
ОБНОВЛЕНИЕ 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'))
Я думаю, что вы действительно хотите это:
>>> 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 (или прочитать ее здесь).
Поскольку этот вопрос на самом деле задает вопрос о 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é'
Установите для 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'/'
и ноль 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()
а также surrogateescape
обработчик ошибок в Unix):
import os
import subprocess
output = os.fsdecode(subprocess.check_output('ls'))
Чтобы получить оригинальные байты, вы можете использовать os.fsencode()
,
Если вы пройдете universal_newlines=True
параметр тогда subprocess
использования locale.getpreferredencoding(False)
декодировать байты, например, это может быть cp1252
на винде.
Чтобы декодировать поток байтов на лету, io.TextIOWrapper()
может быть использован: пример.
Различные команды могут использовать разные кодировки для вывода, например: dir
внутренняя команда (cmd
) может использовать cp437. Чтобы декодировать его вывод, вы можете явно передать кодировку (Python 3.6+):
output = subprocess.check_output('dir', shell=True, encoding='cp437')
Имена файлов могут отличаться от os.listdir()
(который использует Windows
Unicode API), например, '\xb6'
можно заменить на '\x14'
- Карты кодеков Python cp437 b'\x14'
управлять символом U+0014 вместо U+00B6 (¶). Чтобы поддержать имена файлов с произвольными символами Unicode, см. Декодирование вывода Poweshell, возможно, содержащее символы Unicode, отличные от ascii, в строку Python
Хотя ответ @Aaron Maenpaa просто работает, недавно пользователь спросил
Есть ли более простой способ? 'fhand.read().decode("ASCII")' [...] Это так долго!
Ты можешь использовать
command_stdout.decode()
decode()
имеет стандартный аргумент
codecs.decode(obj, encoding='utf-8', errors='strict')
If you have had this error:
'utf-8 codec can't decode byte 0x8a'
, then it is better to use the following code to convert bytes to a string:
bytes = b"abcdefg"
string = bytes.decode("utf-8", "ignore")
Enjoy!
Если вы должны получить следующее, пытаясь decode()
:
AttributeError: 'str' object has no attribute 'decode'
Вы также можете указать тип кодировки прямо в приведении:
>>> my_byte_str
b'Hello World'
>>> str(my_byte_str, 'utf-8')
'Hello World'
Байты
m=b'This is bytes'
Преобразование в строку
Способ 1
m.decode("utf-8")
или
m.decode()
Способ 2
import codecs
codecs.decode(m,encoding="utf-8")
или
import codecs
codecs.decode(m)
Способ 3
str(m,encoding="utf-8")
или
str(m)[2:-1]
Результат
'This is bytes'
Мы можем декодировать объект bytes для создания строки, используя
bytes.decode(encoding='utf-8', errors='strict')
Для документации. Нажмите здесь
Python3
пример:
byte_value = b"abcde"
print("Initial value = {}".format(byte_value))
print("Initial value type = {}".format(type(byte_value)))
string_value = byte_value.decode("utf-8")
# utf-8 is used here because it is a very common encoding, but you need to use the encoding your data is actually in.
print("------------")
print("Converted value = {}".format(string_value))
print("Converted value type = {}".format(type(string_value)))
Выход:
Initial value = b'abcde'
Initial value type = <class 'bytes'>
------------
Converted value = abcde
Converted value type = <class 'str'>
ПРИМЕЧАНИЕ. В Python3 тип кодировки по умолчанию —
utf-8
. Так,
<byte_string>.decode("utf-8")
можно также записать как
<byte_string>.decode()
Для 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
Я сделал функцию для очистки списка
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")
Зачем? Попробуйте это с многострочным 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)
будет копировать ваш оригинальный файл.
С http://docs.python.org/3/library/sys.html,
Для записи или чтения двоичных данных из / в стандартные потоки используйте базовый двоичный буфер. Например, чтобы записать байты в stdout, используйте sys.stdout.buffer.write(b'abc').
В вашем конкретном случае "запустить команду оболочки и получить ее вывод в виде текста вместо байтов" в Python 3.7 следует использовать subprocess.run
и пройти в text=True
(так же как capture_output=True
для захвата вывода)
command_result = subprocess.run(["ls", "-l"], capture_output=True, text=True)
command_result.stdout # is a `str` containing your program's stdout
text
раньше назывался universal_newlines
, и был изменен (ну, псевдоним) в Python 3.7. Если вы хотите поддерживать версии Python до 3.7, передайтеuniversal_newlines=True
вместо того text=True
Расшифровать с помощью
.decode()
. Это расшифрует строку. Пройти в
'utf-8'
) как ценность внутри.
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 МБ.
Попробуйте использовать этот, эта функция проигнорирует все несимвольные наборы (например,
utf-8
) двоичные файлы и вернуть чистую строку. проверено на
python 3.6
и выше.
def bin2str(text, encoding = 'utf-8'):
"""Converts a binary to Unicode string by removing all non Unicode char
text: binary string to work on
encoding: output encoding *utf-8"""
return text.decode(encoding,'ignore')
Здесь функция принимает двоичный файл и декодирует его (преобразует двоичные данные в символы с использованием предопределенного набора символов Python и
ignore
аргумент игнорирует все данные, не являющиеся набором символов, и, наконец, возвращает желаемый
string
значение.
если вы не уверены в кодировке, используйте
sys.getdefaultencoding()
чтобы получить кодировку вашего устройства по умолчанию.
Один из лучших способов преобразования в строку, не заботясь о каком-либо типе кодировки, заключается в следующем:
import json
b_string = b'test string'
string = b_string.decode(
json.detect_encoding(b_string) # detect_encoding - used to detect encoding
)
print(string)
Здесь мы использовалиjson.detect_encoding
метод определения кодировки.
Возможный ответ:
#input string
istring = b'pomegranite'
# output string
ostring = str(istring)