web-dev-qa-db-fra.com

Comment ne pas citer une chaîne Unicode encodée en Python?

J'ai une chaîne unicode comme "Tanım" qui est codée en quelque sorte "Tan% u0131m". Comment puis-je reconvertir cette chaîne encodée en unicode d'origine. Apparemment, urllib.unquote ne prend pas en charge l'unicode.

48
hamdiakoguz

% uXXXX est un schéma de codage non standard qui a été rejeté par le w3c, malgré le fait qu'une implémentation continue de vivre en JavaScript.

La technique la plus courante semble être de coder en UTF-8 la chaîne puis de% échapper les octets résultants en utilisant% XX. Ce schéma est pris en charge par urllib.unquote:

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

Malheureusement, si vous avez vraiment besoin de prendre en charge% uXXXX, vous devrez probablement faire rouler votre propre décodeur. Sinon, il est probablement beaucoup plus préférable de simplement coder en UTF-8 votre unicode, puis d'échapper% des octets résultants.

Un exemple plus complet:

>>> u"Tanım"
u'Tan\u0131m'
>>> url = urllib.quote(u"Tanım".encode('utf8'))
>>> urllib.unquote(url).decode('utf8')
u'Tan\u0131m'
68
Aaron Maenpaa
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)
10
Markus Jarderot

Cela le fera si vous devez absolument avoir cela (je suis vraiment d'accord avec les cris de "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
6
Ali Afshar

il y a un bogue dans la version ci-dessus où il flippe parfois quand il y a à la fois des caractères encodés ascii et unicode dans la chaîne. Je pense que c'est spécifiquement quand il y a des caractères de la gamme 128 supérieure comme '\ xab' en plus de l'unicode.

par exemple. "% 5B% AB% u03E1% BB% 5D" provoque cette erreur.

J'ai trouvé que si vous venez de faire les unicodes en premier, le problème a disparu:

def unquote_u(source):
  result = source
  if '%u' in result:
    result = result.replace('%u','\\u').decode('unicode_escape')
  result = unquote(result)
  return result
4
Jermaine

Vous avez une URL utilisant un schéma d'encodage non standard , rejeté par les organismes de normalisation mais toujours produit par certains encodeurs. La fonction Python urllib.parse.unquote() ne peut pas les gérer.

La création de votre propre décodeur n'est pas si difficile, heureusement. %uhhhh les entrées sont censées être les points de code UTF-16 ici, nous devons donc prendre en compte paires de substitution . J'ai aussi vu %hh codepoints mélangés, pour plus de confusion.

Dans cet esprit, voici un décodeur qui fonctionne à la fois Python 2 et Python 3, à condition de passer un objet str dans Python 3 (Python 2 se soucie moins):

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 fonction est fortement inspirée par la implémentation actuelle de la bibliothèque standard .

Démo:

>>> 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 fonction fonctionne sur Python 2 (testé sur 2.4 - 2.7) et Python 3 (testé sur 3.3 - 3.8)).

1
Martijn Pieters