2011-07-16 29 views
21

Estoy trabajando con las cargas de Amazon S3 y estoy teniendo problemas con los nombres clave que son demasiado largos. S3 limita la longitud de la clave por bytes, no por caracteres.¿Cómo puedo determinar la longitud de bytes de una cadena codificada en utf-8 en Python?

A partir de los documentos:

El nombre de una clave es una secuencia de caracteres Unicode cuya codificación UTF-8 es como máximo 1024 bytes de longitud.

también intento para incrustar metadatos en el nombre del archivo, así que tengo que ser capaz de calcular la longitud de bytes actual de la cadena usando Python para asegurarse de que los metadatos no hace que la tecla demasiado tiempo (en cuyo caso Tendría que usar un archivo de metadatos por separado).

¿Cómo puedo determinar la longitud de bytes de la cadena codificada en utf-8? De nuevo, no estoy interesado en la longitud del carácter ... sino en la longitud real del byte utilizada para almacenar la cadena.

Respuesta

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

funciona bien en Python 2 y 3.

+1

Gracias. También encontré un sitio web que le muestra cómo hacerlo en varios idiomas aquí: http://rosettacode.org/wiki/String_length#Byte_Length_49 – user319862

8

Utilice el método de cadena 'codificar' convertir de una cadena de caracteres de un byte de cuerdas, a continuación, utilizar len() como normal:

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

Muy apreciado – user319862

+6

¡No use 'str' como nombre de variable! Causará un final de dolor. –

4

La codificación de la cadena y el uso de len en el resultado funciona muy bien, ya que otras respuestas han demostrado. Necesita construir una copia descartable de la cadena; si está trabajando con cadenas muy grandes, esto podría no ser óptimo (no creo que 1024 bytes sean grande). La estructura de UTF-8 le permite obtener la longitud de cada carácter muy fácilmente sin siquiera codificarlo, aunque aún podría ser más fácil codificar un solo carácter. Presento ambos métodos aquí, deberían dar el mismo resultado.

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

Tenga en cuenta que a cambio de no hacer una copia, esto toma aproximadamente 180x siempre que 'len (s.encode ('utf-8'))', al menos en mi pitón 3.3.2 en una cadena de 1000 utf8 caracteres [generada del código aquí] (http://stackoverflow.com/a/1477572/344821). (Sería de velocidad comparable si escribió el mismo algoritmo en C, supuestamente) – Dougal

+0

@Dougal, gracias por ejecutar la prueba. Esa es información útil, esencial para evaluar posibles soluciones. Tenía la sensación de que podría ser más lento, pero no sabía la magnitud. ¿Has probado ambas versiones? –

+1

La versión con 'utf8_char_len_2' es aproximadamente 1.5x más lenta que' utf8_char_len_1'. Por supuesto, estamos hablando de menos de un milisegundo en todos los casos, por lo que si solo lo hace unas pocas veces no tiene importancia alguna: 2 μs/375 μs/600 μs. Dicho esto, copiar 1kb de memoria tampoco es probable que importe. :) – Dougal

Cuestiones relacionadas