Quelle est la différence entre une chaîne et une chaîne d'octets?

210

Je travaille avec une bibliothèque qui renvoie une chaîne d'octets et je dois la convertir en chaîne.

Bien que je ne sois pas sûr de la différence - le cas échéant.

Sheldon
la source

Réponses:

261

En supposant Python 3 (en Python 2, cette différence est un peu moins bien définie) - une chaîne est une séquence de caractères, c'est-à-dire des points de code unicode ; il s'agit d'un concept abstrait qui ne peut pas être directement stocké sur disque. Une chaîne d'octets est une séquence, sans surprise, d'octets - des choses qui peuvent être stockées sur le disque. Le mappage entre eux est un codage - il y en a beaucoup (et infiniment possible) - et vous devez savoir ce qui s'applique dans le cas particulier afin de faire la conversion, car un codage différent peut mapper les mêmes octets à une chaîne différente:

>>> b'\xcf\x84o\xcf\x81\xce\xbdo\xcf\x82'.decode('utf-16')
'蓏콯캁澽苏'
>>> b'\xcf\x84o\xcf\x81\xce\xbdo\xcf\x82'.decode('utf-8')
'τoρνoς'

Une fois que vous savez lequel utiliser, vous pouvez utiliser la .decode()méthode de la chaîne d'octets pour obtenir la bonne chaîne de caractères comme ci-dessus. Pour être complet, la .encode()méthode d'une chaîne de caractères va dans le sens inverse:

>>> 'τoρνoς'.encode('utf-8')
b'\xcf\x84o\xcf\x81\xce\xbdo\xcf\x82'
lvc
la source
7
Pour clarifier pour les utilisateurs de Python 2: le strtype est le même que le bytestype; cette réponse compare de manière équivalente le unicodetype (n'existe pas en Python 3) au strtype.
craymichael
3
@KshitijSaraogi ce n'est pas tout à fait vrai non plus; toute cette phrase a été modifiée et est un peu malheureux. La représentation en mémoire des strobjets Python 3 n'est ni accessible ni pertinente du côté Python; la structure des données n'est qu'une séquence de points de code. Sous PEP 393 , l'encodage interne exact est l'un de Latin-1, UCS2 ou UCS4, et une représentation utf-8 peut être mise en cache après sa première demande, mais même le code C est déconseillé de s'appuyer sur ces détails internes.
lvc
1
S'ils ne peuvent pas être directement stockés sur le disque, comment sont-ils stockés en mémoire?
z33k
2
@orety, ils doivent être codés en quelque sorte en interne pour exactement cette raison, mais cela ne vous est pas exposé à partir du code Python, tout comme vous n'avez pas à vous soucier de la façon dont les nombres à virgule flottante sont stockés.
lvc
1
@ChrisStryczynski voir les commentaires ci-dessus - bien sûr, ils sont stockés en mémoire d'une manière ou d' une autre , mais ce formulaire est explicitement supprimé. En effet, de nos jours, il peut changer pendant la durée de vie d'un programme et être différent entre différentes chaînes ou peut même en être plusieurs (certains encodages sont mis en cache), selon les caractères qu'ils contiennent - mais la seule fois dont vous devez vous soucier c'est si vous piratez l'implémentation du type de chaîne lui-même.
lvc
390

La seule chose qu'un ordinateur peut stocker, ce sont des octets.

Pour stocker quoi que ce soit dans un ordinateur, vous devez d'abord le coder , c'est-à-dire le convertir en octets. Par exemple:

  • Si vous voulez stocker de la musique, vous devez d' abord encoder à l'aide MP3, WAVetc.
  • Si vous souhaitez stocker une image, vous devez d' abord encoder à l'aide PNG, JPEGetc.
  • Si vous voulez stocker du texte, vous devez d' abord encoder à l'aide ASCII, UTF-8etc.

MP3, WAV, PNG, JPEG, ASCIIEt UTF-8sont des exemples de codages . Un encodage est un format pour représenter l'audio, les images, le texte, etc. en octets.

En Python, une chaîne d'octets n'est que cela: une séquence d'octets. Ce n'est pas lisible par l'homme. Sous le capot, tout doit être converti en une chaîne d'octets avant de pouvoir être stocké dans un ordinateur.

D'un autre côté, une chaîne de caractères, souvent simplement appelée "chaîne", est une séquence de caractères. Il est lisible par l'homme. Une chaîne de caractères ne peut pas être stockée directement dans un ordinateur, elle doit d'abord être codée (convertie en chaîne d'octets). Il existe plusieurs encodages grâce auxquels une chaîne de caractères peut être convertie en une chaîne d'octets, comme ASCIIet UTF-8.

'I am a string'.encode('ASCII')

Le code Python ci-dessus encodera la chaîne en 'I am a string'utilisant l'encodage ASCII. Le résultat du code ci-dessus sera une chaîne d'octets. Si vous l'imprimez, Python le représentera sous la forme b'I am a string'. N'oubliez pas, cependant, que les chaînes d'octets ne sont pas lisibles par l'homme , c'est juste que Python les décode ASCIIlorsque vous les imprimez. En Python, une chaîne d'octets est représentée par un b, suivi de la ASCIIreprésentation de la chaîne d'octets .

Une chaîne d'octets peut être décodée en une chaîne de caractères, si vous connaissez l'encodage qui a été utilisé pour l'encoder.

b'I am a string'.decode('ASCII')

Le code ci-dessus renverra la chaîne d'origine 'I am a string'.

Le codage et le décodage sont des opérations inverses. Tout doit être encodé avant de pouvoir être écrit sur le disque, et il doit être décodé avant d'être lu par un humain.

Zenadix
la source
59
Zenadix mérite quelques félicitations ici. Après quelques années de fonctionnement dans cet environnement, c'est la première explication qui a cliqué avec moi. Je peux le tatouer sur mon autre bras (un bras a déjà "Le minimum absolu que tous les développeurs de logiciels doivent absolument savoir sur Unicode et les jeux de caractères (pas d'excuses!) Par Joel Spolsky"
neil.millikin
4
Absolument brillant. Lucide et facile à comprendre. Cependant, je voudrais mentionner que cette ligne - "Si vous l'imprimez, Python la représentera comme b'I am a string '" est vraie pour Python3 comme pour Python2 octets et str sont la même chose.
SRC
5
Je vous accorde cette prime pour avoir offert une explication très lisible par l'homme pour mettre un peu de clarté sur ce sujet!
fedorqui 'SO arrête de nuire'
3
Très bonne réponse. La seule chose qui pourrait peut-être être ajoutée est de souligner plus clairement que, historiquement, les programmeurs et les langages de programmation ont eu tendance à supposer explicitement ou implicitement qu'une séquence d'octets et une chaîne ASCII étaient la même chose . Python 3 a décidé de briser explicitement cette hypothèse, correctement à mon humble avis.
nekomatic à 9h39
4
Lien vers le message de Joel mentionné par @ neil.millikin ci-dessus: joelonsoftware.com/2003/10/08/…
Kshitij Saraogi
14

Remarque: Je développerai davantage ma réponse pour Python 3 car la fin de vie de Python 2 est très proche.

En Python 3

bytesse compose de séquences de valeurs non signées 8 bits, tandis que se strcompose de séquences de points de code Unicode qui représentent des caractères textuels de langues humaines.

>>> # bytes
>>> b = b'h\x65llo'
>>> type(b)
<class 'bytes'>
>>> list(b)
[104, 101, 108, 108, 111]
>>> print(b)
b'hello'
>>>
>>> # str
>>> s = 'nai\u0308ve'
>>> type(s)
<class 'str'>
>>> list(s)
['n', 'a', 'i', '̈', 'v', 'e']
>>> print(s)
naïve

Même si byteset strsemblent travailler de la même façon, leurs instances ne sont pas compatibles les uns avec les autres, à savoir, byteset les strcas ne peuvent pas être utilisés conjointement avec des opérateurs tels que >et +. De plus, gardez à l'esprit que la comparaison byteset les strinstances pour l'égalité, c'est-à-dire l'utilisation ==, seront toujours évaluées Falsemême lorsqu'elles contiennent exactement les mêmes caractères.

>>> # concatenation
>>> b'hi' + b'bye' # this is possible
b'hibye'
>>> 'hi' + 'bye' # this is also possible
'hibye'
>>> b'hi' + 'bye' # this will fail
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't concat str to bytes
>>> 'hi' + b'bye' # this will also fail
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "bytes") to str
>>>
>>> # comparison
>>> b'red' > b'blue' # this is possible
True
>>> 'red'> 'blue' # this is also possible
True
>>> b'red' > 'blue' # you can't compare bytes with str
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '>' not supported between instances of 'bytes' and 'str'
>>> 'red' > b'blue' # you can't compare str with bytes
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '>' not supported between instances of 'str' and 'bytes'
>>> b'blue' == 'red' # equality between str and bytes always evaluates to False
False
>>> b'blue' == 'blue' # equality between str and bytes always evaluates to False
False

Un autre problème lors de l'utilisation byteset strest présent lors de l'utilisation de fichiers renvoyés à l'aide de la openfonction intégrée. D'une part, si vous ne voulez pas lire ou écrire de données binaires vers / depuis un fichier, ouvrez toujours le fichier en utilisant un mode binaire comme 'rb' ou 'wb'. D'un autre côté, si vous souhaitez lire ou écrire des données Unicode vers / depuis un fichier, faites attention au codage par défaut de votre ordinateur, donc si nécessaire passez le encodingparamètre pour éviter les surprises.

En Python 2

strse compose de séquences de valeurs 8 bits, tandis que se unicodecompose de séquences de caractères Unicode. Une chose à garder à l' esprit est que , stret unicodepeut être utilisé conjointement avec les opérateurs si strseulement se compose de 7 bits des caractères ASCI.

Il peut être utile d'utiliser des fonctions d'assistance pour convertir entre stret unicodeen Python 2, et entre byteset stren Python 3.

lmiguelvargasf
la source
4

De ce qui est Unicode :

Fondamentalement, les ordinateurs traitent uniquement des chiffres. Ils stockent des lettres et d'autres caractères en attribuant un numéro à chacun.

......

Unicode fournit un numéro unique pour chaque personnage, quelle que soit la plate-forme, quel que soit le programme, quelle que soit la langue.

Ainsi, lorsqu'un ordinateur représente une chaîne, il trouve les caractères stockés dans l'ordinateur de la chaîne via leur numéro Unicode unique et ces chiffres sont stockés en mémoire. Mais vous ne pouvez pas écrire directement la chaîne sur le disque ou transmettre la chaîne sur le réseau via leur numéro Unicode unique car ces chiffres sont simplement un nombre décimal simple. Vous devez coder la chaîne en chaîne d'octets, telle que UTF-8. UTF-8est un encodage de caractères capable d'encoder tous les caractères possibles et il stocke les caractères sous forme d'octets (il ressemble à ceci ). La chaîne encodée peut donc être utilisée partout car elle UTF-8est presque prise en charge partout. Lorsque vous ouvrez un fichier texte encodé enUTF-8à partir d'autres systèmes, votre ordinateur le décodera et y affichera les caractères via leur numéro Unicode unique. Lorsqu'un navigateur reçoit des données de chaîne codées à UTF-8partir du réseau, il décode les données en chaîne (assumer le navigateur dans le UTF-8codage) et affiche la chaîne.

En python3, vous pouvez transformer les chaînes de caractères et les chaînes d'octets l'une à l'autre:

>>> print('中文'.encode('utf-8'))
b'\xe4\xb8\xad\xe6\x96\x87'
>>> print(b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8'))
中文 

En un mot, la chaîne est destinée à être affichée aux humains pour être lue sur un ordinateur et la chaîne d'octets est destinée au stockage sur disque et à la transmission de données.

Sam Yang
la source
1

Unicode est un format convenu pour la représentation binaire des caractères et différents types de formatage (par exemple minuscules / majuscules, nouvelle ligne, retour chariot), et d'autres "choses" (par exemple emojis). Un ordinateur n'est pas moins capable de stocker une représentation unicode (une série de bits), que ce soit en mémoire ou dans un fichier, que de stocker une représentation ascii (une série de bits différente), ou toute autre représentation (série de bits ).

Pour que la communication ait lieu, les parties à la communication doivent convenir de la représentation qui sera utilisée.

Parce que l'unicode cherche à représenter tous les caractères possibles (et autres "choses") utilisés dans la communication inter-humaine et inter-informatique, il nécessite un plus grand nombre de bits pour la représentation de nombreux caractères (ou choses) que d'autres systèmes de représentation qui chercher à représenter un ensemble plus limité de personnages / choses. Pour «simplifier» et peut-être pour tenir compte de l'utilisation historique, la représentation unicode est presque exclusivement convertie en un autre système de représentation (par exemple ascii) dans le but de stocker des caractères dans des fichiers.

Ce n'est pas le cas que l'unicode ne peut pas être utilisé pour stocker des caractères dans des fichiers, ou les transmettre via n'importe quel canal de communication, simplement que ce n'est pas le cas.

Le terme «chaîne» n'est pas défini avec précision. «Chaîne», dans son utilisation courante, fait référence à un ensemble de caractères / choses. Dans un ordinateur, ces caractères peuvent être stockés dans l'une des nombreuses représentations bit à bit différentes. Une "chaîne d'octets" est un ensemble de caractères stockés à l'aide d'une représentation qui utilise huit bits (huit bits étant appelés octets). Depuis, de nos jours, les ordinateurs utilisent le système Unicode (caractères représentés par un nombre variable d'octets) pour stocker des caractères en mémoire, et des chaînes d'octets (caractères représentés par des octets simples) pour stocker des caractères dans des fichiers, une conversion doit être utilisée avant que les caractères ne soient représentés en mémoire sera déplacé vers le stockage dans des fichiers.

Gordon Shephard
la source
0

Ayons une simple chaîne d'un caractère 'š'et encodons-la en une séquence d'octets:

>>> 'š'.encode('utf-8')
b'\xc5\xa1'

Aux fins de cet exemple, affichons la séquence d'octets sous sa forme binaire:

>>> bin(int(b'\xc5\xa1'.hex(), 16))
'0b1100010110100001'

Maintenant, il n'est généralement pas possible de décoder les informations sans savoir comment elles ont été codées. Seulement si vous savez que l' utf-8encodage de texte a été utilisé, vous pouvez suivre l' algorithme de décodage utf-8 et acquérir la chaîne d'origine:

11000101 10100001
   ^^^^^   ^^^^^^
   00101   100001

Vous pouvez afficher le nombre binaire 101100001sous forme de chaîne:

>>> chr(int('101100001', 2))
'š'
Jeyekomon
la source
0

Les langages Python incluent stret bytesen standard "Built-in Types". En d'autres termes, ce sont les deux classes. Je ne pense pas que cela vaille la peine d'essayer de rationaliser pourquoi Python a été implémenté de cette façon.

Cela dit, stret bytessont très similaires les uns aux autres. Les deux partagent la plupart des mêmes méthodes. Les méthodes suivantes sont propres à la strclasse:

casefold
encode
format
format_map
isdecimal
isidentifier
isnumeric
isprintable

Les méthodes suivantes sont propres à la bytesclasse:

decode
fromhex
hex
cinquante cartes
la source