2011-07-16 48 views
21

Tôi đang làm việc với tải lên S3 của Amazon và đang gặp sự cố với các tên khóa quá dài. S3 giới hạn chiều dài của khóa theo byte, chứ không phải ký tự.Làm cách nào để xác định độ dài byte của chuỗi được mã hóa utf-8 bằng Python?

Từ các tài liệu:

Tên cho một chìa khóa là một chuỗi các ký tự Unicode có mã UTF-8 là độ dài không quá 1024 byte.

Tôi cũng cố gắng nhúng siêu dữ liệu vào tên tệp, vì vậy tôi cần tính toán độ dài byte hiện tại của chuỗi bằng Python để đảm bảo siêu dữ liệu không tạo khóa quá dài (trong trường hợp này Tôi sẽ phải sử dụng một tệp siêu dữ liệu riêng biệt).

Làm cách nào để xác định độ dài byte của chuỗi được mã hóa utf-8? Một lần nữa, tôi không quan tâm đến chiều dài ký tự ... thay vì độ dài byte thực tế được sử dụng để lưu trữ chuỗi.

Trả lời

36
def utf8len(s): 
    return len(s.encode('utf-8')) 

trình tốt trong Python 2 và 3.

+1

Cảm ơn. Tôi cũng tìm thấy một trang web cho bạn biết cách thực hiện bằng nhiều ngôn ngữ tại đây: http://rosettacode.org/wiki/String_length#Byte_Length_49 – user319862

8

Sử dụng chuỗi 'mã hóa' phương pháp để chuyển đổi từ một nhân vật-chuỗi để một byte-chuỗi, sau đó sử dụng len() như bình thường:

>>> s = u"¡Hola, mundo!"              
>>> len(s)                  
13 # characters                    
>>> len(s.encode('utf-8')) 
14 # bytes 
+0

Được đánh giá cao – user319862

+6

Vui lòng không sử dụng 'str' làm tên biến! Nó sẽ không gây ra kết thúc đau buồn. –

4

Mã hóa chuỗi và sử dụng len trên kết quả hoạt động tốt, như các câu trả lời khác đã được hiển thị. Nó không cần phải xây dựng một bản sao của chuỗi - nếu bạn đang làm việc với các chuỗi rất lớn, điều này có thể không tối ưu (tôi không xem xét 1024 byte là lớn). Cấu trúc của UTF-8 cho phép bạn có được độ dài của mỗi ký tự rất dễ dàng mà không cần mã hóa nó, mặc dù nó vẫn có thể dễ dàng mã hóa một ký tự đơn. Tôi trình bày cả hai phương pháp ở đây, họ nên cho kết quả tương tự.

def utf8_char_len_1(c): 
    codepoint = ord(c) 
    if codepoint <= 0x7f: 
     return 1 
    if codepoint <= 0x7ff: 
     return 2 
    if codepoint <= 0xffff: 
     return 3 
    if codepoint <= 0x10ffff: 
     return 4 
    raise ValueError('Invalid Unicode character: ' + hex(codepoint)) 

def utf8_char_len_2(c): 
    return len(c.encode('utf-8')) 

utf8_char_len = utf8_char_len_1 

def utf8len(s): 
    return sum(utf8_char_len(c) for c in s) 
+1

Lưu ý rằng để đổi lấy việc không tạo bản sao, nó mất khoảng 180x miễn là 'len (s.encode ('utf-8'))', ít nhất là trên trăn của tôi 3.3.2 trên một chuỗi 1000 ký tự utf8 [được tạo từ mã ở đây] (http://stackoverflow.com/a/1477572/344821). (Nó sẽ là tốc độ tương đương nếu bạn viết cùng một thuật toán trong C, có lẽ.) – Dougal

+0

@Dougal, cảm ơn bạn đã chạy thử nghiệm. Đó là thông tin hữu ích, cần thiết để đánh giá các giải pháp khả thi. Tôi có cảm giác nó có thể chậm hơn, nhưng không biết độ lớn. Bạn đã thử cả hai phiên bản? –

+1

Phiên bản có 'utf8_char_len_2' chậm hơn khoảng 1,5 lần so với' utf8_char_len_1'. Tất nhiên, chúng tôi đang nói về dưới một phần nghìn giây trong mọi trường hợp, vì vậy nếu bạn chỉ làm điều đó một vài lần nó không quan trọng ở tất cả: 2 µs/375 µs/600 µs. Điều đó nói rằng, sao chép 1kb bộ nhớ cũng không có vấn đề gì cả. :) – Dougal

Các vấn đề liên quan