8

Tôi có một chuỗi unicode được lấy từ một webservice sử dụng requests module, chứa các byte của tài liệu nhị phân (PCL, khi nó xảy ra). Một trong những byte có giá trị 248, và cố gắng base64 mã hóa nó dẫn đến các lỗi sau:base64 mã hóa các chuỗi unicode trong python 2.7

In [68]: base64.b64encode(response_dict['content']+'\n') 
--------------------------------------------------------------------------- 
UnicodeEncodeError      Traceback (most recent call last) 
C:\...\<ipython-input-68-8c1f1913eb52> in <module>() 
----> 1 base64.b64encode(response_dict['content']+'\n') 

C:\Python27\Lib\base64.pyc in b64encode(s, altchars) 
    51  """ 
    52  # Strip off the trailing newline 
---> 53  encoded = binascii.b2a_base64(s)[:-1] 
    54  if altchars is not None: 
    55   return _translate(encoded, {'+': altchars[0], '/': altchars[1]}) 

UnicodeEncodeError: 'ascii' codec can't encode character u'\xf8' in position 272: ordinal not in range(128) 

In [69]: response_dict['content'].encode('base64') 
--------------------------------------------------------------------------- 
UnicodeEncodeError      Traceback (most recent call last) 
C:\...\<ipython-input-69-7fd349f35f04> in <module>() 
----> 1 response_dict['content'].encode('base64') 

C:\...\base64_codec.pyc in base64_encode(input, errors) 
    22  """ 
    23  assert errors == 'strict' 
---> 24  output = base64.encodestring(input) 
    25  return (output, len(input)) 
    26 

C:\Python27\Lib\base64.pyc in encodestring(s) 
    313  for i in range(0, len(s), MAXBINSIZE): 
    314   chunk = s[i : i + MAXBINSIZE] 
--> 315   pieces.append(binascii.b2a_base64(chunk)) 
    316  return "".join(pieces) 
    317 

UnicodeEncodeError: 'ascii' codec can't encode character u'\xf8' in position 44: ordinal not in range(128) 

Tôi tìm thấy điều này hơi ngạc nhiên, bởi vì 248 nằm trong phạm vi của một byte unsigned (và có thể được tổ chức tại một chuỗi byte), nhưng câu hỏi thực sự của tôi là: cách tốt nhất hoặc đúng để mã hóa chuỗi này là gì?

công việc xung quanh hiện tại của tôi là thế này:

In [74]: byte_string = ''.join(map(compose(chr, ord), response_dict['content'])) 

In [75]: byte_string[272] 
Out[75]: '\xf8' 

Điều này dường như làm việc một cách chính xác, và kết quả byte_string có khả năng được mã hóa base64, nhưng có vẻ như không nên có một cách tốt hơn. Lanhung?

+1

248 có thể nằm trong phạm vi byte chưa ký, nhưng không nằm trong phạm vi chuẩn ASCII [0-127]. – Cameron

+0

@Cameron: Một điểm đúng và tốt, nhưng nó vẫn không giải thích được vấn đề, vì giá trị giống hệt nhau, khi được giữ trong một chuỗi byte không dẫn đến lỗi đó. – Marcin

+0

Xem câu trả lời của tôi :-) Những gì bạn đã làm là lấy các điểm mã của chuỗi 'unicode' và coi chúng là byte. Điều này là ... cá tốt nhất, vì bạn không có đảm bảo codepoints ngay cả trong phạm vi 0-255. Thậm chí còn tệ hơn nữa là không ai khác sẽ biết cách giải thích chuỗi byte sau này, vì nó nằm trong một mã hóa tùy chỉnh, không xác định. – Cameron

Trả lời

2

Vì bạn đang làm việc với dữ liệu nhị phân, tôi không chắc rằng bạn nên sử dụng mã hóa utf-8. Tôi đoán nó phụ thuộc vào cách bạn định sử dụng biểu diễn mã hóa base64. Tôi nghĩ rằng nó có lẽ sẽ tốt hơn nếu bạn có thể lấy dữ liệu dưới dạng chuỗi byte chứ không phải chuỗi unicode. Tôi chưa bao giờ sử dụng thư viện yêu cầu, nhưng việc duyệt tài liệu cho thấy điều đó là có thể. Có phần nói về "Nội dung phản hồi nhị phân" và "Nội dung phản hồi thô".

+0

Cảm ơn! Nó chỉ ra rằng mã hóa như latin-1 sản lượng chính xác cùng một chuỗi byte như workaround của tôi. – Marcin

+1

@Marcin: Bạn cần đảm bảo rằng mô-đun yêu cầu đã không giả định rằng bạn đang làm việc với văn bản, áp dụng mã hóa mặc định và giải mã dữ liệu nhị phân của bạn thành unicode. Nếu đó là trường hợp bạn gặp rắc rối. Bạn có thể xác minh rằng nội dung là những gì bạn mong đợi? –

+2

Đã chú ý hơn một chút đến tài liệu, nó chỉ ra rằng các yêu cầu cũng cho tôi biết mã hóa được sử dụng để giải mã phản hồi thành unicode, vì vậy tôi có thể luôn mã hóa lại với mã đó (và một lần nữa mang lại cùng một giá trị byte). – Marcin

5

tôi sẽ đề nghị đầu tiên mã hóa nó để cái gì đó như UTF-8 trước khi mã hóa base64:

In [12]: my_unicode = u'\xf8' 

In [13]: my_utf8 = my_unicode.encode('utf-8') 

In [15]: base64.b64encode(my_utf8) 
Out[15]: 'w7g=' 
+0

* mã hóa thành UTF-8 * không có ý nghĩa. hoặc bạn mã hóa từ UTF-8 thành byte/ascii hoặc bạn giải mã từ ascii sang UTF-8. đó là cách khác. – sebix

14

Bạn có một chuỗi unicode mà bạn muốn mã hóa base64. Vấn đề là b64encode() chỉ hoạt động trên byte, không phải ký tự. Vì vậy, bạn cần phải chuyển đổi chuỗi unicode của bạn (là một chuỗi các điểm mã Unicode trừu tượng) thành một chuỗi byte.

Ánh xạ chuỗi Unicode trừu tượng thành một chuỗi byte cụ thể được gọi là mã hóa . Python hỗ trợ một số mã hóa; Tôi đề nghị các rộng rãi sử dụng mã hóa UTF-8:

byte_string = response_dict['content'].encode('utf-8') 

Lưu ý rằng bất cứ ai được giải mã byte sẽ cũng cần phải biết được mã hóa được sử dụng để lấy lại một chuỗi unicode thông qua bổ sung decode() chức năng:

# Decode 
decoded = byte_string.decode('utf-8') 

Điểm khởi đầu tốt để tìm hiểu thêm về Unicode và mã hóa là Python docsthis article bởi Joel Spolsky.

+0

Để rõ ràng: nội dung của chuỗi unicode của tôi là dữ liệu nhị phân. Tôi không thể thay đổi chúng thành một số byte khác nhau. Có mã hóa danh tính không? – Marcin

+1

@Marcin: Bạn không thể có chuỗi 'unicode' chứa dữ liệu nhị phân. Đó là một mâu thuẫn về mặt! Nếu các byte của chuỗi 'unicode' được cho là đại diện cho dữ liệu nhị phân (như là trường hợp ở đây), thì nó không nên được lưu trữ trong một đối tượng' unicode' vì nó không thực sự là Unicode! – Cameron

+0

Vâng, đó là vấn đề của tôi. – Marcin

1

Có thể nhận phản hồi dưới dạng byte nhị phân và bỏ qua hoàn toàn các bước giải mã và mã hóa. Luôn có khả năng là requests sẽ chọn mã hóa mất một số dữ liệu hoặc lỗi trong chuyến đi khứ hồi.

Phần tài liệu này được gọi là "Binary Response Content" dường như phù hợp với vấn đề của bạn một cách hoàn hảo.

0

Nếu đó là dữ liệu nhị phân ... tại sao mã hóa/giải mã? Đặc biệt là phần "base64.encodestring".Dưới đây là cách tôi mã hóa hình ảnh thành base64 để thêm trực tiếp vào mã python của tôi thay vì có thêm tệp. 2.7.2 btw

import base64 
iconfile = open("blah.icon","rb") 
icondata = iconfile.read() 
icondata = base64.b64encode(icondata)