Trunkieren Unicode so passt es eine maximale Größe, wenn für die Übertragung übertragen codiert

Angesichts einer Unicode-Zeichenfolge und dieser Anforderungen:

  • Die Zeichenfolge wird in ein Byte-Sequenz-Format codiert (zB UTF-8 oder JSON Unicode-Escape)
  • Der codierte String hat eine maximale Länge

Zum Beispiel benötigt der iPhone-Push-Dienst eine JSON-Codierung mit einer maximalen Gesamtpaketgröße von 256 Bytes.

Was ist der beste Weg, um die Zeichenfolge zu verkürzen, so dass es wieder auf gültige Unicode kodiert und dass es vernünftig korrekt angezeigt wird?

(Das menschliche Verstehen ist nicht nötig – die abgeschnittene Version kann z. B. für einen verwaisten Kombinationszeichen oder einen Thai-Vokal seltsam aussehen, so lange die Software beim Umgang mit den Daten nicht abstürzt.)

Siehe auch:

  • Verwandte Java-Frage: Wie kann ich einen Java-String abschneiden, um in eine gegebene Anzahl von Bytes passen, sobald UTF-8 codiert?
  • Verwandte Javascript-Frage: Verwenden von JavaScript, um Text auf eine bestimmte Größe abzuschneiden

  • Wie konvertiere ich Unicode in Unicode-Escaped Text
  • Zeichenlesung aus Datei in Python
  • Python: Jede Möglichkeit, diesen "Hybrid" Split () auf mehrsprachigen (zB chinesischen & englischen) Strings durchzuführen?
  • Alle Unicode-Codes zwischen U + E000-U + F8FF mit Python auflisten
  • Python urllib.request und utf8 decoding Frage
  • Unicode-Regex, um eine Zeichenklasse von chinesischen Zeichen zu entsprechen
  • Einkapseln von Unicode von redis
  • Was bedeutet 'du' in einer Liste?
  • 5 Solutions collect form web for “Trunkieren Unicode so passt es eine maximale Größe, wenn für die Übertragung übertragen codiert”

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

    Hier ist ein Beispiel für Unicode-String, bei dem jedes Zeichen mit 2 Bytes in UTF-8 dargestellt wird:

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

    Eines der Eigenschaften von UTF-8 ist, dass es einfach ist, neu zu resynchronisieren, das findet die Unicode-Zeichengrenzen leicht in dem codierten bytestream. Alles, was Sie tun müssen, ist, die verschlüsselte Saite auf maximale Länge zu schneiden, dann gehen Sie rückwärts von dem Ende entfernen alle Bytes, die sind> 127 – die sind Teil oder der Beginn eines Multibyte-Zeichen.

    Wie jetzt geschrieben, ist das zu einfach – wird gelöscht, um ASCII char, evtl. die ganze Saite zu löschen. Was wir tun müssen, ist zu überprüfen, dass kein abgeschnittenes Zwei-Byte (Start mit 110yyyxx ) Drei-Byte ( 1110yyyy ) oder Vier-Byte ( 11110zzz )

    Python 2.6 Implementierung in klarem Code. Optimierung sollte kein Problem sein – unabhängig von der Länge, überprüfen wir nur die letzten 1-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[Lx]) and not decodeok(bytestr[Lx:L]): return bytestr[:Lx] return bytestr[:L] if __name__ == '__main__': # unicode doctest hack import sys reload(sys) sys.setdefaultencoding("UTF-8") import doctest doctest.testmod() 

    Das wird für UTF8 tun, wenn du es gern in regex machst.

     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" 

    Seine Abdeckung von U + 0080 (2 Bytes) bis U + 10FFFF (4 Bytes) utf8 Strings

    Es ist wirklich einfach so wie der UTF8-Algorithmus

    Von U + 0080 bis U + 07FF Es braucht 2 Bytes 110yyyxx 10xxxxxx Sein Mittel, wenn man nur ein Byte am Ende sieht wie 110yyyxx (0b11000000 bis 0b11011111) Es ist [\xc0-\xdf] , wird es teilweise sein.

    Von U + 0800 bis U + FFFF ist 3 Bytes erforderlich 1110yyyyyyyyxxxxxxxx Wenn Sie nur 1 oder 2 Bytes am Ende sehen, wird es teilweise sein. Es wird mit diesem Muster [\xe0-\xef][\x80-\xbf]{0,1}

    Von U + 10000-U + 10FFFF wird 4 Bytes benötigt 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx Wenn Sie nur 1 bis 3 Bytes am Ende sehen, wird es teilweise sein Es wird mit diesem Muster [\xf6-\xf7][\x80-\xbf]{0,2}

    Update:

    Wenn du nur noch mehrsprachiges Flugzeug brauchst, kannst du das letzte Muster fallen lassen. Das wird es tun.

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

    Lassen Sie mich wissen, ob es irgendwelche Probleme mit diesem Regex gibt.

    Für JSON-Formatierung (Unicode-Escape, zB \uabcd ) verwende ich den folgenden Algorithmus, um dies zu erreichen:

    • Kodiere die Unicode-Zeichenfolge in das Backslash-Escape-Format, das es letztlich in der JSON-Version wäre
    • Trennt 3 Bytes mehr als meine endgültige Grenze
    • Verwenden Sie einen regulären Ausdruck, um eine partielle Kodierung eines Unicode-Werts zu erkennen und abzubrechen

    Also (in Python 2.5), mit some_string und einer Anforderung, auf rund 100 Bytes zu schneiden:

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

    Jetzt ist final_string wieder in Unicode, aber garantiert, um in das JSON Paket später zu passen. Ich trennte mich auf 103, weil eine rein-Unicode-Nachricht 102 Bytes codiert wäre.

    Haftungsausschluss: Nur auf der Basis Mehrsprachige Ebene getestet. Ja, ja ich weiß es.

    Überprüfe das letzte Zeichen der Zeichenfolge. Wenn das Hochbit gesetzt ist, dann ist es nicht das letzte Byte in einem UTF-8 Zeichen, also sorge es und versuche es noch einmal, bis du das gefunden hast.

     mxlen=255 while( toolong.encode("utf8")[mxlen-1] & 0xc0 == 0xc0 ): mxlen -= 1 truncated_string = toolong.encode("utf8")[0:mxlen].decode("utf8") 
    Python ist die beste Programmiersprache der Welt.