18 votos

Truncar unicode, por lo que se ajusta a un tamaño máximo cuando se codifica para la transferencia bancaria

Dada una cadena Unicode y estos requisitos:

  • La cadena ser codificadas en algunos bytes de la secuencia de formato (por ejemplo, UTF-8 o JSON de escape unicode)
  • La cadena codificada que tiene una longitud máxima

Por ejemplo, el iPhone, el servicio de difusión requiere codificación JSON con un total máximo tamaño de paquete de 256 bytes.

¿Cuál es la mejor manera para truncar la cadena, por lo que se re-codifica para Unicode válido y que se muestra razonablemente correctamente?

(Humanos, la comprensión del lenguaje, no es necesario—la versión truncada puede parecer extraña por ejemplo, para un huérfano de combinación de caracteres o un Tailandés vocal, tan largo como el software no se bloquee cuando el manejo de los datos.)

Vea También:

13voto

Denis Otkidach Puntos 13111
def unicode_truncate(s, length, encoding='utf-8'):
    encoded = s.encode(encoding)[:length]
    return encoded.decode(encoding, 'ignore')

Aquí es un ejemplo de cadena unicode donde cada carácter es representado con 2 bytes UTF-8:

>>> unicode_truncate(u'абвгд', 5)
u'\u0430\u0431'

7voto

u0b34a0f6ae Puntos 14874

Uno de UTF-8 propiedades es que es fácil de resincronización, que es encontrar el carácter unicode límites fácilmente en el codificados bytestream. Todo lo que necesitas hacer es cortar la cadena codificada en el máximo de longitud, y luego caminar hacia atrás desde el final de la eliminación de los bytes que se > 127 -- esos son parte de, o el inicio de un juego de caracteres multibyte.

Como está escrito ahora, esto es demasiado sencillo, borrará a la última ASCII char, posiblemente toda la cadena. Lo que tenemos que hacer es comprobar que no truncada de dos bytes (inicio con 110yyyxx) de tres bytes (1110yyyy) o de cuatro bytes (11110zzz)

Python 2.6 aplicación en claro de código. La optimización no debería ser un problema, sin importar de longitud, sólo se compruebe la última de 1 a 4 bytes.

# coding: UTF-8

def decodeok(bytestr):
    try:
        bytestr.decode("UTF-8")
    except UnicodeDecodeError:
        return False
    return True

def is_first_byte(byte):
    """return if the UTF-8 @byte is the first byte of an encoded character"""
    o = ord(byte)
    return ((0b10111111 & o) != o)

def truncate_utf8(bytestr, maxlen):
    u"""

    >>> us = u"ウィキペディアにようこそ"
    >>> s = us.encode("UTF-8")

    >>> trunc20 = truncate_utf8(s, 20)
    >>> print trunc20.decode("UTF-8")
    ウィキペディ
    >>> len(trunc20)
    18

    >>> trunc21 = truncate_utf8(s, 21)
    >>> print trunc21.decode("UTF-8")
    ウィキペディア
    >>> len(trunc21)
    21
    """
    L = maxlen
    for x in xrange(1, 5):
        if is_first_byte(bytestr[L-x]) and not decodeok(bytestr[L-x:L]):
            return bytestr[:L-x]
    return bytestr[:L]

if __name__ == '__main__':
    # unicode doctest hack
    import sys
    reload(sys)
    sys.setdefaultencoding("UTF-8")
    import doctest
    doctest.testmod()

3voto

YOU Puntos 44812

Esto va a hacer para UTF8, Si te gusta hacer en regex.

import re

partial="\xc2\x80\xc2\x80\xc2"

re.sub("([\xf6-\xf7][\x80-\xbf]{0,2}|[\xe0-\xef][\x80-\xbf]{0,1}|[\xc0-\xdf])$","",partial)

"\xc2\x80\xc2\x80"

Su cubierta de U+0080 (2 bytes) U+10FFFF (4 bytes) cadenas de caracteres utf8

Su realmente sencillo como UTF8 algoritmo

De U+0080 U+07FF se necesitan 2 bytes 110yyyxx 10xxxxxx Su media, si usted ve un solo byte en el final como 110yyyxx (0b11000000 a 0b11011111) Es [\xc0-\xdf], va a ser parcial.

De U+0800 a U+FFFF es de 3 bytes necesarios 1110yyyy 10yyyyxx 10xxxxxx Si usted ve sólo 1 o 2 bytes en la final, va a ser parcial. Se coincide con este patrón [\xe0-\xef][\x80-\xbf]{0,1}

De U+10000–U+10FFFF es de 4 bytes necesarios 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx Si usted ve sólo de 1 a 3 bytes en la final, que será parcial Se coincide con este patrón [\xf6-\xf7][\x80-\xbf]{0,2}

Actualización :

Si sólo necesita Plano Multilingüe Básico, Usted puede colocar último Patrón. Esto va a hacer.

re.sub("([\xe0-\xef][\x80-\xbf]{0,1}|[\xc0-\xdf])$","",partial)

Déjeme saber si hay algún problema con que regex.

1voto

JasonSmith Puntos 34470

Para el formato JSON (de escape unicode, por ejemplo \uabcd), estoy utilizando el siguiente algoritmo para lograr esto:

  • Codificar la cadena Unicode en la secuencia de escape formato que podría, eventualmente, ser en la versión JSON
  • Truncar 3 bytes más que mi límite final
  • El uso de una expresión regular para detectar y cortar un parcial de codificación de un valor Unicode

Así que (en Python 2.5), con some_string y un requisito para cortar alrededor de 100 bytes:

# Given some_string is a long string with arbitrary Unicode data.
encoded_string = some_string.encode('unicode_escape')
partial_string = re.sub(r'([^\\])\\(u|$)[0-9a-f]{0,3}$', r'\1', encoded_string[:103])
final_string   = partial_string.decode('unicode_escape')

Ahora final_string está de vuelta en Unicode, pero garantizado para encajar dentro de las JSON paquete más tarde. Yo trunca a 103 debido a una puramente Unicode mensaje sería 102 bytes codificados.

Descargo de responsabilidad: Solo probado en el Plano Multilingüe Básico. Sí, sí, lo sé.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by: