Câu hỏi Chuyển đổi byte thành chuỗi?


Tôi đang sử dụng mã này để có được đầu ra tiêu chuẩn từ một chương trình bên ngoài:

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

Phương thức communication () trả về một mảng các byte:

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

Tuy nhiên, tôi muốn làm việc với đầu ra như một chuỗi Python bình thường. Để tôi có thể in nó như thế này:

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

Tôi nghĩ đó là những gì binascii.b2a_qp () phương pháp là, nhưng khi tôi thử nó, tôi đã nhận được cùng một mảng byte một lần nữa:

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

Có ai biết làm thế nào để chuyển đổi các giá trị byte trở lại chuỗi? Ý tôi là, sử dụng "pin" thay vì làm bằng tay. Và tôi muốn nó được ok với Python 3.


1243
2018-03-03 12:23


gốc




Các câu trả lời:


Bạn cần giải mã đối tượng byte để tạo chuỗi:

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

2050
2018-03-03 12:26



'Giải pháp' này đặc biệt khó tìm (ít nhất là đối với tôi) xem xét nó là một vấn đề đơn giản ... Tôi muốn đặt một dòng ở đâu đó tài liệu về quy trình con này vì tôi đặt cược một phần lớn người mới như tôi sẽ nhấn snag này khi sử dụng tiến trình con. Bất kỳ ai biết về việc đóng góp vào tài liệu python? - mathtick
Sử dụng "windows-1252" không đáng tin cậy (ví dụ: đối với các phiên bản ngôn ngữ khác của Windows), không phải là cách tốt nhất để sử dụng sys.stdout.encoding? - nikow
Đây là lần thứ hai tôi quên mất điều này và nó vẫn không được tìm thấy trong tài liệu, ngay cả trong phần unicode. Xấu hổ làm sao. - Profpatsch
Có lẽ điều này sẽ giúp ai đó hơn nữa: Đôi khi bạn sử dụng mảng byte cho e.x. Giao tiếp TCP. Nếu bạn muốn chuyển đổi mảng byte thành chuỗi cắt các ký tự '\ x00', câu trả lời sau là không đủ. Sử dụng b'example \ x00 \ x00'.decode ('utf-8'). Strip ('\ x00') sau đó. - Wookie88
Trong Python 2.7.6 không xử lý b"\x80\x02\x03".decode("utf-8") -> UnicodeDecodeError: 'utf8' codec can't decode byte 0x80 in position 0: invalid start byte. - martineau


Tôi nghĩ cách này rất dễ dàng:

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

120
2017-08-22 12:57



Cảm ơn bạn, phương pháp của bạn đã làm việc cho tôi khi không có ai khác làm. Tôi đã có một mảng byte không được mã hóa mà tôi cần phải biến thành một chuỗi. Đã cố gắng tìm cách để mã hóa lại nó để tôi có thể giải mã nó thành một chuỗi. Phương pháp này hoạt động hoàn hảo! - leetNightshade
@leetNightshade: nhưng nó là cực kỳ không hiệu quả. Nếu bạn có một mảng byte, bạn chỉ cần giải mã. - Martijn Pieters♦
@Martijn Pieters Tôi vừa làm một điểm chuẩn đơn giản với các câu trả lời khác, chạy nhiều 10.000 lượt chạy stackoverflow.com/a/3646405/353094 Và giải pháp trên thực sự nhanh hơn nhiều lần. Đối với 10.000 lượt chạy trong Python 2.7.7, nó mất 8ms, so với những người khác ở 12ms và 18ms. Cấp có thể có một số biến thể tùy thuộc vào đầu vào, phiên bản Python, vv Không có vẻ quá chậm với tôi. - leetNightshade
@Martijn Pieters Có. Vì vậy, với điểm đó, đây không phải là câu trả lời tốt nhất cho cơ thể của câu hỏi được hỏi. Và tiêu đề là gây hiểu lầm, phải không? Anh / cô ấy muốn chuyển đổi một chuỗi byte thành một chuỗi thông thường, không phải là một mảng byte thành một chuỗi. Câu trả lời này hoạt động tốt cho tiêu đề của câu hỏi đã được hỏi. - leetNightshade
Đối với python 3, giá trị này phải tương đương với bytes([112, 52, 52]) - btw bytes là tên không hợp lệ cho biến cục bộ chính xác vì nó là nội dung p3 - Mr_and_Mrs_D


Bạn cần phải giải mã chuỗi byte và biến nó thành chuỗi ký tự (unicode).

b'hello'.decode(encoding)

hoặc là

str(b'hello', encoding)

99
2018-03-03 12:28



Lưu ý rằng str chức năng trong Python 2 (ít nhất là 2.7.5 tôi đang chạy) không hỗ trợ tham số mã hóa thứ hai, vì vậy tốt hơn là nên sử dụng decode nếu bạn muốn mã của bạn hoạt động trên Python 2 và 3. - metakermit
@dF. : Điều này không hoạt động với python3. - user2284570
@ user2284570 str (s, 'utf-8') đã hoạt động đối với tôi trong Python3 - Kat


Nếu bạn không biết mã hóa, sau đó đọc đầu vào nhị phân thành chuỗi theo Python 3 và Python 2 theo cách tương thích, sử dụng MS-DOS cổ cp437 mã hóa:

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

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

Bởi vì mã hóa không xác định, mong đợi các biểu tượng không phải tiếng Anh dịch sang các ký tự của cp437 (Các ký tự tiếng Anh không được dịch, vì chúng khớp với hầu hết các mã hóa byte đơn và UTF-8).

Giải mã đầu vào nhị phân tùy ý cho UTF-8 là không an toàn, bởi vì bạn có thể nhận được điều này:

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

Ứng dụng tương tự latin-1, phổ biến (mặc định?) cho Python 2. Xem các điểm bị thiếu trong Bố cục mã - đó là nơi mà cuộn cảm của Python nổi tiếng ordinal not in range.

CẬP NHẬT 20150604: Có những tin đồn rằng Python 3 có surrogateescape chiến lược lỗi để mã hóa nội dung thành dữ liệu nhị phân mà không mất dữ liệu và sự cố, nhưng nó cần kiểm tra chuyển đổi [binary] -> [str] -> [binary] để xác thực cả hiệu suất và độ tin cậy.

CẬP NHẬT 20170116: Nhờ nhận xét của Nearoo - đó cũng là một khả năng để cắt giảm tất cả các byte không rõ với backslashreplace xử lý lỗi. Điều đó chỉ làm việc với Python 3, vì vậy ngay cả với cách giải quyết này, bạn vẫn sẽ nhận được kết quả không nhất quán từ các phiên bản Python khác nhau:

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

Xem https://docs.python.org/3/howto/unicode.html#python-s-unicode-support để biết chi tiết.

CẬP NHẬT 20170119: Tôi quyết định triển khai giải mã thoát slash hoạt động cho cả Python 2 và Python 3. Nó sẽ chậm hơn cp437 giải pháp, nhưng nó sẽ sản xuất kết quả giống hệt nhau trên mọi phiên bản 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'))

57
2017-12-17 14:23



Tôi thực sự cảm thấy như Python nên cung cấp một cơ chế để thay thế các biểu tượng còn thiếu và tiếp tục. - anatoly techtonik
Rực rỡ! Điều này là nhanh hơn nhiều so với phương pháp @ Sisso cho một tập tin 256 MB! - wallyk
@techtonik: Điều này sẽ không hoạt động trên một mảng như nó hoạt động trong python2. - user2284570
@ user2284570 bạn có nghĩa là danh sách? Và tại sao nó nên hoạt động trên mảng? Đặc biệt là mảng nổi .. - anatoly techtonik
@anatolytechtonik Có khả năng để thoát khỏi chuỗi thoát trong chuỗi và di chuyển trên: b'\x80abc'.decode("utf-8", "backslashreplace") sẽ cho kết quả '\\x80abc'. Thông tin này được lấy từ trang tài liệu unicode dường như đã được cập nhật kể từ khi viết câu trả lời này. - Nearoo


Tôi nghĩ những gì bạn thực sự muốn là:

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

Câu trả lời của Aaron là đúng, ngoại trừ việc bạn cần biết mã hóa WHICH để sử dụng. Và tôi tin rằng Windows sử dụng 'windows-1252'. Nó sẽ chỉ quan trọng nếu bạn có một số ký tự khác thường (không phải ascii) trong nội dung của bạn, nhưng sau đó nó sẽ tạo sự khác biệt.

Nhân tiện, thực tế là nó DO vấn đề là lý do mà Python chuyển sang sử dụng hai loại khác nhau cho dữ liệu nhị phân và văn bản: nó không thể chuyển đổi kỳ diệu giữa chúng bởi vì nó không biết mã hóa trừ khi bạn nói nó! Cách duy nhất bạn biết là đọc tài liệu Windows (hoặc đọc nó ở đây).


33
2017-07-18 19:51



open() chức năng cho luồng văn bản hoặc Popen() nếu bạn vượt qua nó universal_newlines=True thực sự quyết định mã hóa ký tự cho bạn (locale.getpreferredencoding(False) trong Python 3.3+). - jfs
'latin-1' là mã hóa nguyên bản với tất cả các điểm mã được thiết lập, vì vậy bạn có thể sử dụng để đọc chuỗi byte hiệu quả vào bất kỳ kiểu chuỗi nào mà Python của bạn hỗ trợ (do đó nguyên văn trên Python 2, thành Unicode cho Python 3). - tripleee


Trong Python 3, mã hóa mặc định là "utf-8", vì vậy bạn có thể sử dụng trực tiếp:

b'hello'.decode()

tương đương với

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

Mặt khác, bằng Python 2, mã hóa mặc định thành mã hóa chuỗi mặc định. Vì vậy, bạn nên sử dụng:

b'hello'.decode(encoding)

Ở đâu encoding là mã hóa bạn muốn.

Chú thích: hỗ trợ cho các đối số từ khóa đã được thêm vào trong Python 2.7.


31
2018-06-29 14:21



@Artyer, bằng Python 3 mã hóa mặc định, theo liên kết bạn đã cung cấp, là Default encoding is 'utf-8'. Tại sao câu trả lời của tôi sai, nếu tôi nói mã hóa mặc định là utf-8 ý tôi là nó luôn luôn utf-8. - lmiguelvargasf
@Artyer, tôi thấy quan điểm của bạn. Tôi có nghĩa là bạn có thể kiểm tra mã hóa mặc định nói chung không chỉ cho python 3, đó là lý do tại sao tôi đã không đặt giá trị bạn nhận được khi bạn chạy sys.getdefaultencoding(). - lmiguelvargasf
@Artyer, tôi đã cập nhật câu trả lời của tôi, cảm ơn cho ý kiến ​​của bạn. - lmiguelvargasf


Đặt universal_newlines thành True, tức là

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

26
2018-01-21 15:31



Tôi đã sử dụng phương pháp này và nó hoạt động. Mặc dù, nó chỉ là đoán tại mã hóa dựa trên sở thích của người dùng trên hệ thống của bạn, do đó, nó không phải là mạnh mẽ như một số tùy chọn khác. Đây là những gì nó đang làm, tham khảo docs.python.org/3.4/library/subprocess.html: "Nếu universal_newlines là True, [stdin, stdout và stderr] sẽ được mở dưới dạng luồng văn bản trong chế độ dòng mới phổ dụng bằng cách sử dụng bảng mã được trả về bởi ngôn ngữ .getpreferredencoding (Sai). " - twasbrillig