Que dois-je faire dans Python pour déterminer le codage d'une chaîne?
Dans Python 3, toutes les chaînes sont des séquences de caractères Unicode. Il existe un type bytes
qui contient des octets bruts.
Dans Python 2, une chaîne peut être de type str
ou de type unicode
. Vous pouvez dire à l'aide de code quelque chose comme ceci:
def whatisthis(s):
if isinstance(s, str):
print "ordinary string"
Elif isinstance(s, unicode):
print "unicode string"
else:
print "not a string"
Cela ne distingue pas "Unicode ou ASCII"; il ne distingue que les types Python. Une chaîne Unicode peut être constituée uniquement de caractères compris dans la plage ASCII et une chaîne d'octets peut contenir des données ASCII, Unicode codé ou même des données non textuelles.
Vous pouvez utiliser type
ou isinstance
.
Dans Python 2:
>>> type(u'abc') # Python 2 unicode string literal
<type 'unicode'>
>>> type('abc') # Python 2 byte string literal
<type 'str'>
Dans Python 2, str
n'est qu'une séquence d'octets. Python ne sait pas quel est son encodage. Le type unicode
est le moyen le plus sûr de stocker du texte. Si vous voulez comprendre davantage, je recommande http://farmdev.com/talks/unicode/ .
Dans Python 3:
>>> type('abc') # Python 3 unicode string literal
<class 'str'>
>>> type(b'abc') # Python 3 byte string literal
<class 'bytes'>
Dans Python 3, str
ressemble à Python 2 unicode
et permet de stocker du texte. Ce qui s'appelait str
dans Python 2 s'appelle bytes
dans Python 3.
Vous pouvez appeler decode
. Si elle déclenche une exception UnicodeDecodeError, ce n'est pas valide.
>>> u_umlaut = b'\xc3\x9c' # UTF-8 representation of the letter 'Ü'
>>> u_umlaut.decode('utf-8')
u'\xdc'
>>> u_umlaut.decode('ascii')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)
Dans python 3.x, toutes les chaînes sont des séquences de caractères Unicode. et faire la vérification isinstance pour str (ce qui signifie une chaîne unicode par défaut) devrait suffire.
isinstance(x, str)
En ce qui concerne python 2.x, la plupart des gens semblent utiliser une instruction if comportant deux vérifications. un pour str et un pour unicode.
Si vous voulez vérifier si vous avez un objet 'ressemblant à une chaîne' avec une seule instruction, vous pouvez procéder comme suit:
isinstance(x, basestring)
Unicode n'est pas un encodage - pour citer Kumar McMillan:
Si ASCII, UTF-8 et d'autres chaînes d'octets sont "text" ...
... alors Unicode est "text-ness";
c'est la forme abstraite du texte
Prenez connaissance de la conversation Unicode de McMillan en Python, complètement démystifiée de PyCon 2008, qui explique beaucoup mieux que la plupart des réponses connexes sur Stack Overflow.
Si votre code doit être compatible avec les deux Python 2 et Python 3, vous ne pouvez pas utiliser directement des éléments tels que isinstance(s,bytes)
ou isinstance(s,unicode)
sans les insérer dans try/except ou dans un test de version python, car bytes
n'est pas défini dans Python 2 et unicode
n'est pas défini dans Python 3.
Il existe des solutions de contournement laides. Une très laide consiste à comparer le nom du type au lieu de comparer le type lui-même. Voici un exemple:
# convert bytes (python 3) or unicode (python 2) to str
if str(type(s)) == "<class 'bytes'>":
# only possible in Python 3
s = s.decode('ascii') # or s = str(s)[2:-1]
Elif str(type(s)) == "<type 'unicode'>":
# only possible in Python 2
s = str(s)
Une solution de contournement légèrement moins laide consiste à vérifier le numéro de version Python, par exemple:
if sys.version_info >= (3,0,0):
# for Python 3
if isinstance(s, bytes):
s = s.decode('ascii') # or s = str(s)[2:-1]
else:
# for Python 2
if isinstance(s, unicode):
s = str(s)
Ce sont tous les deux non rythmiques, et la plupart du temps, il y a probablement une meilleure solution.
utilisation:
import six
if isinstance(obj, six.text_type)
à l'intérieur des six bibliothèques, il est représenté par:
if PY3:
string_types = str,
else:
string_types = basestring,
Notez que sur Python 3, il n'est pas vraiment juste de dire:
str
s sont UTFx pour tout x (par exemple UTF8)
str
s sont en Unicode
str
s sont des collections ordonnées de caractères Unicode
Le type str
de Python est (normalement) une séquence de points de code Unicode, dont certains correspondent à des caractères.
Même sur Python 3, il n'est pas aussi simple de répondre à cette question que vous pourriez l'imaginer.
Un moyen évident de tester les chaînes compatibles ASCII consiste à tenter un encodage:
"Hello there!".encode("ascii")
#>>> b'Hello there!'
"Hello there... ☃!".encode("ascii")
#>>> Traceback (most recent call last):
#>>> File "", line 4, in <module>
#>>> UnicodeEncodeError: 'ascii' codec can't encode character '\u2603' in position 15: ordinal not in range(128)
L'erreur distingue les cas.
Dans Python 3, certaines chaînes contiennent même des points de code Unicode non valides:
"Hello there!".encode("utf8")
#>>> b'Hello there!'
"\udcc3".encode("utf8")
#>>> Traceback (most recent call last):
#>>> File "", line 19, in <module>
#>>> UnicodeEncodeError: 'utf-8' codec can't encode character '\udcc3' in position 0: surrogates not allowed
La même méthode pour les distinguer est utilisée.
Cela peut aider quelqu'un d'autre. J'ai commencé par tester le type de chaîne de la variable s, mais pour mon application, il était plus logique de simplement renvoyer s en tant que utf-8. Le processus appelant return_utf sait alors de quoi il traite et peut gérer la chaîne de manière appropriée. Le code n’est pas vierge, mais j’ai l’intention de ne pas utiliser Python version sans test de version ni en importer six. Veuillez commenter avec des améliorations à l'exemple de code ci-dessous pour aider d'autres personnes.
def return_utf(s):
if isinstance(s, str):
return s.encode('utf-8')
if isinstance(s, (int, float, complex)):
return str(s).encode('utf-8')
try:
return s.encode('utf-8')
except TypeError:
try:
return str(s).encode('utf-8')
except AttributeError:
return s
except AttributeError:
return s
return s # assume it was already utf-8
Vous pouvez utiliser niversal Encoding Detector , mais sachez que cela vous donnera la meilleure estimation, pas l'encodage réel, car il est impossible de connaître l'encodage d'une chaîne "abc" par exemple. Vous aurez besoin d'obtenir des informations de codage ailleurs, par exemple, le protocole HTTP utilise l'en-tête Content-Type pour cela.
Pour la compatibilité py2/py3, utilisez simplement
import six if isinstance(obj, six.text_type)