Konwertuj bajty na ciąg znaków

Używam tego kodu, aby uzyskać standardowe wyjście z zewnętrznego programu:

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

Metoda communicate() zwraca tablicę bajtów:

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

Chciałbym jednak pracować z wyjściem jako zwykły łańcuch Pythona. Żebym mógł to wydrukować w ten sposób:

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

Myślałem, że to jest to binascii.metoda b2a_qp() jest dla, Ale kiedy próbowałem, znowu dostałem tę samą tablicę bajtów:

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

Jak skonwertować wartość bajtów z powrotem na łańcuch znaków? I znaczy, używając "baterii" zamiast robić to ręcznie. A ja bym chciał żeby było OK z Pythonem 3.

Author: Peter Mortensen, 2009-03-03

21 answers

Musisz zdekodować obiekt bajtów, aby wytworzyć łańcuch znaków:

>>> 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'
 4292
Author: Aaron Maenpaa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-24 18:14:12

Musisz zdekodować łańcuch bajtów i przekształcić go w łańcuch znaków (Unicode).

On Python 2

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

Lub

unicode('hello', encoding)

On Python 3

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

Lub

str(b'hello', encoding)
 270
Author: dF.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-28 10:52:57

Myślę, że ta droga jest łatwa:

>>> bytes_data = [112, 52, 52]
>>> "".join(map(chr, bytes_data))
'p44'
 206
Author: Sisso,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-14 18:31:44

Jeśli nie znasz kodowania, to aby odczytać dane binarne do ciągu znaków w sposób zgodny z Pythonem 3 i Pythonem 2, Użyj starożytnego MS-DOS CP437 kodowanie:

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

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

Ponieważ kodowanie jest nieznane, należy oczekiwać, że symbole nie-angielskie będą tłumaczone na znaki cp437 (angielskie znaki nie są tłumaczone, ponieważ pasują do większości kodowań jednobajtowych i UTF-8).

Dekodowanie dowolnego wejścia binarnego do UTF-8 jest niebezpieczne, ponieważ możesz uzyskać to:

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

The to samo dotyczy latin-1, który był popularny(domyślnie?) dla Pythona 2. Zobacz brakujące punkty w układ strony kodowej - to tutaj Python dławi się infamous ordinal not in range.

UPDATE 20150604: istnieją pogłoski, że Python 3 ma strategię błędów surrogateescape do kodowania rzeczy do danych binarnych bez utraty danych i awarii, ale potrzebuje testów konwersji, [binary] -> [str] -> [binary], aby zweryfikować zarówno wydajność, jak i niezawodność.

UPDATE 20170116 : Thanks to comment by Nearoo - istnieje również możliwość ukośnika escape wszystkich nieznanych bajtów za pomocą obsługi błędów backslashreplace. To działa tylko w Pythonie 3, więc nawet z tym obejściem nadal będziesz otrzymywał niespójne wyniki z różnych wersji Pythona:

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

Zobacz obsługa Unicode Pythona Po szczegóły.

UPDATE 20170119 : zdecydowałem się zaimplementować dekodowanie ukośnika, które działa zarówno dla Pythona 2, jak i Pythona 3. Powinno być wolniejsze niż rozwiązanie cp437, ale powinno twórz identyczne wyniki dla każdej wersji Pythona.

# --- 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'))
 106
Author: anatoly techtonik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-28 10:58:53

W Pythonie 3 domyślnym kodowaniem jest "utf-8", więc możesz bezpośrednio użyć:

b'hello'.decode()

Co jest równoważne

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

Z drugiej strony, w Pythonie 2 , encoding defaults to the default string encoding. Dlatego należy użyć:

b'hello'.decode(encoding)

Gdzie encoding jest kodowaniem, które chcesz.

Uwaga: wsparcie dla argumentów słów kluczowych zostało dodane w Pythonie 2.7.

 95
Author: lmiguelvargasf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-28 10:59:43

Myślę, że naprawdę tego chcesz:

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

ODPOWIEDŹ Aarona była poprawna, tyle że musisz wiedzieć Jakie kodowanie użyć. A ja uważam, że Windows używa 'windows-1252'. Będzie to miało znaczenie tylko wtedy, gdy masz jakieś nietypowe (Nie-ASCII) znaki w treści, ale to będzie miało znaczenie.

Nawiasem mówiąc, fakt, że robi ma znaczenie, jest powodem, dla którego Python przeniósł się do używania dwóch różnych typów danych binarnych i tekstowych: nie może konwertować magicznie między nimi, bo nie zna kodowania, chyba że mu powiesz! Jedynym sposobem jest przeczytanie dokumentacji systemu Windows(lub przeczytanie jej tutaj).

 43
Author: mcherm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-28 10:54:06

Ustaw universal_newlines na True, tzn.

command_stdout = Popen(['ls', '-l'], stdout=PIPE, universal_newlines=True).communicate()[0]
 34
Author: ContextSwitch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-21 15:47:48

Aby zinterpretować sekwencję bajtów jako tekst, musisz znać odpowiednie kodowanie znaków:

unicode_text = bytestring.decode(character_encoding)

Przykład:

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

ls polecenie może generować dane wyjściowe, które nie mogą być interpretowane jako tekst. Nazwy plików na Unixie może być dowolna sekwencja bajtów z wyjątkiem slash b'/' i zero b'\0':

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

Próba dekodowania takiego bajtu za pomocą kodowania utf-8 podnosi UnicodeDecodeError.

Może być gorzej. W przeciwieństwie do innych gier, w których nie można grać, nie jest to gra logiczna.]} w przypadku stosowania wrong incompatible encoding:
>>> '—'.encode('utf-8').decode('cp1252')
'—'

Dane są uszkodzone, ale twój program pozostaje nieświadomy, że awaria wystąpił.

Ogólnie rzecz biorąc, kodowanie znaków nie jest osadzone w samej sekwencji bajtów. Musisz przekazać te informacje poza pasmem. Niektóre wyniki są bardziej prawdopodobne niż inne i dlatego istnieje chardet moduł, który może odgadnąć kodowanie znaków. Pojedynczy skrypt Pythona może używać wielu kodowań znaków w różnych miejsca.


ls Dane wyjściowe można przekonwertować na ciąg Pythona za pomocą os.fsdecode() funkcja, która powiedzie się nawet dla undekodable nazwy plików (używa sys.getfilesystemencoding() i surrogateescape obsługa błędów na Unix):

import os
import subprocess

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

Aby uzyskać oryginalne bajty, możesz użyć os.fsencode().

If you pass universal_newlines=True parameter then subprocess uses locale.getpreferredencoding(False) aby dekodować bajty np. można cp1252 w systemie Windows.

Aby dekodować strumień bajtów w locie, io.TextIOWrapper() może być używany: przykład .

Różne komendy mogą używać różnych kodowań znaków dla swoich wyjście np. dir wewnętrzne polecenie (cmd) może używać cp437. Aby rozszyfrować jego wyjście, można przekazać kodowanie jawnie (Python 3.6+):

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

Nazwy plików mogą różnić się od os.listdir() (który używa systemu Windows Unicode API) np. '\xb6' można zastąpić '\x14'-Python ' s cp437 kodek mapuje b'\x14' do kontrolowania znaku U + 0014 zamiast U + 00B6 (¶). Do obsługi nazw plików z dowolnymi Znaki Unicode, zobacz dekodowanie wyjścia PowerShell prawdopodobnie zawierającego znaki Unicode inne niż ASCII do łańcucha Pythona

 29
Author: jfs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-04 20:19:44

Ponieważ to pytanie jest właściwie pytaniem o wyjście subprocess, masz bardziej bezpośrednie podejście dostępne. Najnowocześniejsze byłoby wykorzystanie subprocess.check_output i przekazywanie text=True (Python 3.7+) do automatycznego dekodowania stdout przy użyciu domyślnego kodowania systemu:

text = subprocess.check_output(["ls", "-l"], text=True)

Dla Pythona 3.6, Popen przyjmuje kodowanie słowo kluczowe:

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

Ogólną odpowiedzią na pytanie w tytule, jeśli nie masz do czynienia z podprocesowym wyjściem, jest dekodowanie bajtów do tekst:

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

Bez argumentu, sys.getdefaultencoding() zostaną wykorzystane. Jeśli Twoje dane nie są sys.getdefaultencoding(), to musisz jawnie określić kodowanie wdecode wywołanie:

>>> b'caf\xe9'.decode('cp1250')
'café'
 26
Author: wim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-09-04 18:35:32

While @ Aaron Maenpaa ' s answer just works, a user recently asked :

Czy jest jakiś prosty sposób? "fhand.read ().decode("ASCII")" [... Tak długo!

Możesz użyć:

command_stdout.decode()

decode() mA standardowy argument :

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

 24
Author: serv-inc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-06-10 16:04:48

Jeśli powinieneś uzyskać następujące, próbując decode():

AttributeError:' str 'obiekt nie ma atrybutu 'decode'

Możesz również określić typ kodowania prosto w obsadzie:

>>> my_byte_str
b'Hello World'

>>> str(my_byte_str, 'utf-8')
'Hello World'
 13
Author: Broper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-06-10 16:03:10

I made a function to clean a list

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
 6
Author: eafloresf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-09 10:03:11

Podczas pracy z danymi z systemów Windows (z zakończeniami linii \r\n), moja odpowiedź brzmi

String = Bytes.decode("utf-8").replace("\r\n", "\n")
Dlaczego? Spróbuj tego z wejściem wielowierszowym.txt:
Bytes = open("Input.txt", "rb").read()
String = Bytes.decode("utf-8")
open("Output.txt", "w").write(String)

Wszystkie zakończenia linii zostaną podwojone( do \r\r\n), co doprowadzi do dodatkowych pustych linii. Funkcje odczytu tekstu Pythona Zwykle normalizują zakończenia linii, tak że ciągi używają tylko \n. Jeśli otrzymujesz dane binarne z systemu Windows, Python nie ma na to szans. Zatem

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

Powieli oryginalny plik.

 6
Author: bers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-16 13:28:25

Dla Pythona 3 jest to znacznie bezpieczniejsze i Pythoniczne podejście do konwersji z byte na string:

def byte_to_str(bytes_or_str):
    if isinstance(bytes_or_str, bytes): # Check if it's 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')

Wyjście:

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
 6
Author: Inconnu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-28 11:11:58

Dla Twojego konkretnego przypadku "uruchom polecenie powłoki i uzyskaj jego wynik jako tekst zamiast bajtów", w Pythonie 3.7 powinieneś użyć subprocess.run i przekazać text=True (jak również capture_output=True, aby przechwycić wyjście)

command_result = subprocess.run(["ls", "-l"], capture_output=True, text=True)
command_result.stdout  # is a `str` containing your program's stdout

text był nazywany universal_newlines i został zmieniony (dobrze, aliased) w Pythonie 3.7. Jeśli chcesz obsługiwać wersje Pythona przed 3.7, podaj universal_newlines=True zamiast text=True

 4
Author: Boris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-08-07 19:51:33

Od sys-parametry i funkcje specyficzne dla Systemu:

Aby zapisać lub odczytać dane binarne z / do standardowych strumieni, użyj bazowego bufora binarnego. Na przykład, aby zapisać bajty na stdout, użyj sys.stdout.buffer.write(b'abc').

 4
Author: Zhichang Yu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-28 10:54:56
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))
 2
Author: Leonardo Filipe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-03 22:44:45

Jeśli chcesz przekonwertować dowolne bajty, a nie tylko łańcuch skonwertowany na bajty:

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

with open("bytesfile", "rb") as infile:
    str2 = json.dumps(list(infile.read()))
Nie jest to jednak zbyt efektywne. Zamieni obraz z 2 MB na 9 MB.
 2
Author: HCLivess,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-28 11:14:40

Spróbuj tego

bytes.fromhex('c3a9').decode('utf-8') 
 2
Author: Victor Choy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-19 08:19:02

Możesz to zrobić:

bstr=b'your string'

print(bstr.decode())

Wyjście:

your string
 1
Author: yourson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-01-29 09:00:00

Możesz po prostu zrobić:

print(command_stdout.decode('utf-8'))
 0
Author: Matteo Bianchi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-01-22 14:18:18