Come unquote un urlencoded stringa unicode in python?

Ho una stringa unicode come “Tanım”, che è codificato come “Tan%u0131m” in qualche modo. Come posso convertire questa stringa codificata torna a originale unicode.
A quanto pare urllib.unquote non supporta unicode.

InformationsquelleAutor hamdiakoguz | 2008-11-18



5 Replies
  1. 68

    %uXXXX è un non-standard schema di codifica che è stato respinto dal w3c, nonostante il fatto che un’implementazione continua a vivere in JavaScript terra.

    La più comune tecnica sembra essere in UTF-8 codifica la stringa e poi % fuga risultante byte usando %XX. Questo sistema è supportato da urllib.unquote:

    >>> urllib2.unquote("%0a")
    '\n'

    Purtroppo, se davvero bisogno per sostenere %uXXXX, si avrà probabilmente avere a rotolare il vostro proprio decoder. In caso contrario, è probabile che sia molto più preferibile semplicemente UTF-8 codifica unicode e quindi % fuga risultante byte.

    Un esempio più completo:

    >>> u"Tanım"
    u'Tan\u0131m'
    >>> url = urllib.quote(u"Tanım".encode('utf8'))
    >>> urllib.unquote(url).decode('utf8')
    u'Tan\u0131m'
    • ‘urllib2.unquote’ dovrebbe essere ‘urllib.unquote’
    • Interessante che un URI è un codificati in percentuale byte stringa, piuttosto che una stringa di caratteri.
    • non necessariamente, in Python 2.7.5+ è possibile utilizzare urllib2.unquote basta provare print(dir(urllib2))
    • urllib.unquote(url.encode(‘utf-8’)) funzionato per me invece
    • è una pessima abitudine di fare qualcosa di simile unquote(urlencode())?
  2. 10
    def unquote(text):
        def unicode_unquoter(match):
            return unichr(int(match.group(1),16))
        return re.sub(r'%u([0-9a-fA-F]{4})',unicode_unquoter,text)
    • Questo funziona solo per Python 2, purtroppo, che è rapidamente avvicinando il suo fine-di-vita. Non è difficile di corretto per fare questo Python 2 e 3 compatibili (try: unichr, except NameError: unichr = chr), ma questa versione non gestisce coppie di surrogati. L’intento della %hhhh fuga formato di codifica UTF-16 codepoint, per i non-BMP sequenze (ad esempio un gran numero di emoji) si otterrebbe una stringa non valida su qualsiasi cosa, ma un UCS-2 Python 2 build.
  3. 6

    Questo farà se è assolutamente necessario disporre di questo (ho davvero d’accordo con il grido di “non standard”):

    from urllib import unquote
    
    def unquote_u(source):
        result = unquote(source)
        if '%u' in result:
            result = result.replace('%u','\\u').decode('unicode_escape')
        return result
    
    print unquote_u('Tan%u0131m')
    
    > Tanım
    • Leggermente patologica caso, ma: unquote_u(‘Tan%25u0131m’) –> u’Tan\u0131m’ piuttosto che ‘Tan%u0131’ come dovrebbe. Solo un promemoria del perché probabilmente non si desidera scrivere un decoder a meno che non si ha realmente bisogno.
    • Sono totalmente d’accordo. È per questo che ho davvero non era appassionato di offrire una soluzione vera e propria. Queste cose non sono mai così semplici. O. P. potrebbe essere stato disperato, però, e credo che questo completa la tua risposta eccellente.
    • Questo funziona solo per Python 2, purtroppo, che è rapidamente avvicinando il suo fine-di-vita. L’uso di unicode_escape rende un po ‘ più difficile da correggere per Python 3 utilizzo (avresti bisogno per la codifica in utf-8, primo), ma questa versione non gestisce coppie di surrogati. L’intento della %hhhh fuga formato di codifica UTF-16 codepoint, per i non-BMP sequenze (ad esempio un gran numero di emoji) si otterrebbe una stringa non valida su qualsiasi cosa, ma un UCS-2 Python 2 build.
  4. 4

    c’è un bug nella versione superiore, dove fa impazzire a volte, quando ci sono sia codificato ascii e unicode codifica caratteri nella stringa. Penso in particolare quando ci sono personaggi da 128 superiore intervallo come ‘\xab’ oltre a unicode.

    ad esempio. “%5B%AB%u03E1%BB%5D” le cause di questo errore.

    Ho trovato, se hai appena fatto l’unicode quelli di prima, il problema è andato via:

    def unquote_u(source):
      result = source
      if '%u' in result:
        result = result.replace('%u','\\u').decode('unicode_escape')
      result = unquote(result)
      return result
    • \xab non è un personaggio ma un byte. In effetti il tuo esempio “stringa” contiene byte e caratteri, che non è valida come una singola stringa in qualsiasi lingua che non conosco.
    • Cosa sarebbe "%5B%AB%u03E1%BB%5D" decodificare come? 0x5B 0xAB e 0xBB 0x5D sono difficilmente valido UTF-8 sequenze.
    • Ho visto casi concreti (una libreria Java da qualche parte) che codifica per un po di ASCII carattere come gli spazi per %hh sequenze, e nulla più 0x7F per %uhhhh sequenze. Terribile, ma accurata.
  5. 1

    Si dispone di un URL utilizzando un non-standard schema di codifica, rigettata dagli organismi di normalizzazione, ma ancora prodotte da alcuni encoder. Il Python urllib.parse.unquote() funzione non in grado di gestire questi.

    Creare il vostro proprio decoder non è difficile, per fortuna. %uhhhh voci sono destinate ad essere UTF-16 codepoint qui, quindi abbiamo bisogno di prendere coppie di surrogati in considerazione. Ho visto anche %hh codepoint mescolati, per una maggiore confusione.

    Con questo in mente, qui è un decoder che funziona sia in Python 2 e Python 3, a condizione che si passa in un str oggetto in Python 3 (Python 2 si preoccupa meno):

    try:
        # Python 3
        from urllib.parse import unquote
        unichr = chr
    except ImportError:
        # Python 2
        from urllib import unquote
    
    def unquote_unicode(string, _cache={}):
        string = unquote(string)  # handle two-digit %hh components first
        parts = string.split(u'%u')
        if len(parts) == 1:
            return parts
        r = [parts[0]]
        append = r.append
        for part in parts[1:]:
            try:
                digits = part[:4].lower()
                if len(digits) < 4:
                    raise ValueError
                ch = _cache.get(digits)
                if ch is None:
                    ch = _cache[digits] = unichr(int(digits, 16))
                if (
                    not r[-1] and
                    u'\uDC00' <= ch <= u'\uDFFF' and
                    u'\uD800' <= r[-2] <= u'\uDBFF'
                ):
                    # UTF-16 surrogate pair, replace with single non-BMP codepoint
                    r[-2] = (r[-2] + ch).encode(
                        'utf-16', 'surrogatepass').decode('utf-16')
                else:
                    append(ch)
                append(part[4:])
            except ValueError:
                append(u'%u')
                append(part)
        return u''.join(r)

    La funzione è fortemente ispirata al corrente standard-implementazione di libreria.

    Demo:

    >>> print(unquote_unicode('Tan%u0131m'))
    Tanım
    >>> print(unquote_unicode('%u05D0%u05D9%u05DA%20%u05DE%u05DE%u05D9%u05E8%u05D9%u05DD%20%u05D0%u05EA%20%u05D4%u05D8%u05E7%u05E1%u05D8%20%u05D4%u05D6%u05D4'))
    איך ממירים את הטקסט הזה
    >>> print(unquote_unicode('%ud83c%udfd6'))  # surrogate pair
    🏖
    >>> print(unquote_unicode('%ufoobar%u666'))  # incomplete
    %ufoobar%u666

    La funzione lavora su Python 2 (testato su 2.4 – 2.7) e Python 3 (testato su 3.3 – 3.8).

Lascia un commento