web-dev-qa-db-fra.com

Qu'est-ce que l'UTF-8 normalisé?

Le projet IC (qui a également maintenant un bibliothèque PHP ) contient les classes nécessaires pour aider à normaliser les chaînes UTF-8 pour faciliter la comparaison des valeurs lors de la recherche.

Cependant, j'essaie de comprendre ce que cela signifie pour les applications. Par exemple, dans quels cas est-ce que je veux "équivalence canonique" au lieu de "équivalence de compatibilité", ou vice-versa?

123
Xeoncross

Tout ce que vous n'avez jamais voulu savoir sur la normalisation Unicode

Normalisation canonique

Unicode comprend plusieurs façons d'encoder certains caractères, notamment les caractères accentués. La normalisation canonique transforme les points de code en une forme d'encodage canonique. Les points de code résultants doivent apparaître identiques à ceux d'origine, sauf bogues dans les polices ou le moteur de rendu.

Quand utiliser

Étant donné que les résultats semblent identiques, il est toujours sûr d'appliquer une normalisation canonique à une chaîne avant de la stocker ou de l'afficher, tant que vous pouvez tolérer que le résultat ne soit pas bit à bit identique à l'entrée.

La normalisation canonique se présente sous 2 formes: NFD et NFC. Les deux sont équivalents dans le sens où l'on peut convertir entre ces deux formes sans perte. La comparaison de deux chaînes sous NFC donnera toujours le même résultat que leur comparaison sous NFD.

NFD

NFD a les personnages complètement développés. C'est le formulaire de normalisation le plus rapide à calculer, mais les résultats en plus de points de code (c'est-à-dire utilisent plus d'espace).

Si vous souhaitez simplement comparer deux chaînes qui ne sont pas déjà normalisées, il s'agit du formulaire de normalisation préféré, sauf si vous savez que vous avez besoin d'une normalisation de compatibilité.

NFC

NFC recombine les points de code lorsque cela est possible après l'exécution de l'algorithme NFD. Cela prend un peu plus de temps, mais entraîne des chaînes plus courtes.

Normalisation de la compatibilité

Unicode comprend également de nombreux personnages qui n'appartiennent vraiment pas, mais qui étaient utilisés dans les jeux de caractères hérités. Unicode les a ajoutés pour permettre au texte de ces jeux de caractères d'être traité comme Unicode, puis d'être reconverti sans perte.

La normalisation de compatibilité les convertit en la séquence correspondante de "vrais" caractères et effectue également la normalisation canonique. Les résultats de la normalisation de la compatibilité peuvent ne pas sembler identiques aux originaux.

Les caractères qui incluent des informations de mise en forme sont remplacés par ceux qui n'en contiennent pas. Par exemple, le caractère Est converti en 9. D'autres n'impliquent pas de différences de formatage. Par exemple, le caractère romain Est converti en lettres régulières IX.

De toute évidence, une fois cette transformation effectuée, il n'est plus possible de reconvertir sans perte le jeu de caractères d'origine.

Quand utiliser

Le consortium Unicode suggère de penser à la normalisation de la compatibilité comme une transformation ToUpperCase. C'est quelque chose qui peut être utile dans certaines circonstances, mais vous ne devez pas simplement l'appliquer à volonté.

Un excellent cas d'utilisation serait un moteur de recherche car vous voudriez probablement une recherche de 9 Pour correspondre à .

Une chose que vous ne devriez probablement pas faire est d'afficher le résultat de l'application de la normalisation de la compatibilité à l'utilisateur.

NFKC/NFKD

Le formulaire de normalisation de la compatibilité se présente sous deux formes: NFKD et NFKC. Ils ont la même relation qu'entre NFD et C.

Toute chaîne dans NFKC est intrinsèquement également dans NFC, et la même chose pour NFKD et NFD. Ainsi NFKD(x)=NFD(NFKC(x)), et NFKC(x)=NFC(NFKD(x)), etc.

Conclusion

En cas de doute, optez pour la normalisation canonique. Choisissez NFC ou NFD en fonction du compromis espace/vitesse applicable, ou en fonction de ce qui est requis par quelque chose avec lequel vous interagissez.

171
Kevin Cathcart

Certains caractères, par exemple une lettre avec un accent (disons, é) peut être représenté de deux manières: un seul point de code U+00E9 ou la lettre simple suivie d'une marque d'accentuation combinante U+0065 U+0301. La normalisation ordinaire choisira l'un d'entre eux pour toujours le représenter (le point de code unique pour NFC, le formulaire de combinaison pour NFD).

Pour les caractères qui pourraient être représentés par plusieurs séquences de caractères de base et des marques combinées (par exemple, "s, point ci-dessous, point au-dessus" vs mettre point au-dessus puis point ci-dessous ou utiliser un caractère de base qui a déjà l'un des points), NFD choisissez également l'un d'entre eux (ci-dessous va en premier, comme il se passe)

Les décompositions de compatibilité incluent un certain nombre de caractères qui "ne devraient pas vraiment" être des caractères mais le sont parce qu'ils ont été utilisés dans des encodages hérités. La normalisation ordinaire ne les unifiera pas (pour préserver l'intégrité aller-retour - ce n'est pas un problème pour les formulaires de combinaison car aucun encodage hérité [sauf une poignée d'encodages vietnamiens] n'utilisait les deux), mais la normalisation de compatibilité le fera. Pensez comme le signe du kilogramme "kg" qui apparaît dans certains encodages d'Asie de l'Est (ou le katakana et l'alphabet demi-largeur/pleine largeur), ou la ligature "fi" dans MacRoman.

Voir http://unicode.org/reports/tr15/ pour plus de détails.

38
Random832

Les formes normales (d'Unicode, pas de bases de données) traitent principalement (exclusivement?) Des caractères qui ont des signes diacritiques. Unicode fournit certains caractères avec des signes diacritiques "intégrés", tels que U + 00C0, "Latin A majuscule avec tombe". Le même caractère peut être créé à partir d'un "Latin majuscule A" (U + 0041) avec un "Combining Grave Accent" (U + 0300). Cela signifie que même si les deux séquences produisent le même caractère résultant, un octet par octet la comparaison les montrera comme étant complètement différents.

La normalisation est une tentative pour y faire face. La normalisation assure (ou essaie du moins) que tous les caractères sont codés de la même manière - tous utilisant une marque diacritique combinée distincte si nécessaire, ou tous utilisant un point de code unique dans la mesure du possible. D'un point de vue comparatif, peu importe vraiment ce que vous choisissez - à peu près n'importe quelle chaîne normalisée se comparera correctement avec une autre chaîne normalisée.

Dans ce cas, la "compatibilité" signifie la compatibilité avec le code qui suppose qu'un point de code équivaut à un caractère. Si vous avez du code comme ça, vous voudrez probablement utiliser le format normal de compatibilité. Bien que je ne l'ai jamais vu énoncé directement, les noms des formes normales impliquent que le consortium Unicode considère préférable d'utiliser des marques diacritiques combinées distinctes. Cela nécessite plus d'intelligence pour compter les caractères réels dans une chaîne (ainsi que des choses comme casser une chaîne intelligemment), mais est plus polyvalent.

Si vous utilisez pleinement l'ICU, il est probable que vous souhaitiez utiliser la forme canonique normale. Si vous essayez d'écrire du code par vous-même qui (par exemple) suppose qu'un point de code est égal à un caractère, alors vous voulez probablement la forme normale de compatibilité qui rend cela vrai aussi souvent que possible.

13
Jerry Coffin

Si deux chaînes unicode sont canoniquement équivalentes, les chaînes sont vraiment les mêmes, en utilisant uniquement des séquences unicode différentes. Par exemple, Ä peut être représenté en utilisant le caractère Ä ou une combinaison de A et ◌̈.

Si les chaînes sont uniquement équivalentes à la compatibilité, les chaînes ne sont pas nécessairement les mêmes, mais elles peuvent être les mêmes dans certains contextes. Par exemple. ff pourrait être considéré comme ff.

Donc, si vous comparez des chaînes, vous devez utiliser l'équivalence canonique, car l'équivalence de compatibilité n'est pas une équivalence réelle.

Mais si vous souhaitez trier un ensemble de chaînes, il peut être judicieux d'utiliser l'équivalence de compatibilité car elles sont presque identiques.

5
NikiC

Que l'équivalence canonique ou l'équivalence de compatibilité vous concerne plus dépend de votre application. La façon ASCII de penser les comparaisons de chaînes correspond approximativement à l'équivalence canonique, mais Unicode représente beaucoup de langues. Je ne pense pas qu'il soit sûr de supposer qu'Unicode code toutes les langues de manière à ce que vous permet de les traiter comme ASCII d'Europe occidentale.

Figures 1 et 2 fournissent de bons exemples des deux types d'équivalence. Sous équivalence de compatibilité, il semble que le même nombre sous forme de sous-script et de super-script serait égal. Mais je ne suis pas sûr que cela résout le même problème que la forme arabe cursive ou les caractères pivotés.

La dure vérité du traitement de texte Unicode est que vous devez réfléchir profondément aux exigences de traitement de texte de votre application, puis y répondre du mieux que vous pouvez avec les outils disponibles. Cela ne répond pas directement à votre question, mais une réponse plus détaillée nécessiterait des experts linguistiques pour chacune des langues que vous prévoyez de prendre en charge.

4
ObscureRobot

C'est en fait assez simple. UTF-8 a en fait plusieurs représentations différentes du même "caractère". (J'utilise des caractères entre guillemets car, en octets, ils sont différents, mais pratiquement identiques.) Un exemple est donné dans le document lié.

Le caractère "Ç" peut être représenté par la séquence d'octets 0xc387. Mais il peut également être représenté par un C (0x43) suivi de la séquence d'octets 0xcca7. Vous pouvez donc dire que 0xc387 et 0x43cca7 sont le même caractère. La raison pour laquelle cela fonctionne, c'est que 0xcca7 est une marque de combinaison; c'est-à-dire qu'il prend le caractère devant lui (un C ici), et le modifie.

Maintenant, en ce qui concerne la différence entre l'équivalence canonique et l'équivalence de compatibilité, nous devons examiner les caractères en général.

Il existe 2 types de caractères, ceux qui transmettent un sens à travers valeur, et ceux qui prennent un autre caractère et le modifient. 9 est un personnage significatif. Un super-script ⁹ prend ce sens et le modifie par la présentation. Donc, canoniquement, ils ont des significations différentes, mais ils représentent toujours le caractère de base.

L'équivalence canonique est l'endroit où la séquence d'octets rend le même caractère avec la même signification. L'équivalence de compatibilité est lorsque la séquence d'octets rend un caractère différent avec la même signification de base (même si elle peut être modifiée). Les 9 et ⁹ sont équivalents en termes de compatibilité car ils signifient tous les deux "9", mais ne sont pas équivalents canoniquement car ils n'ont pas la même représentation.

4
ircmaxell

Le problème de comparer les chaînes : deux chaînes avec un contenu équivalent pour la plupart des applications peuvent contenir des séquences de caractères différentes.

Voir équivalence canonique Unicode : si l'algorithme de comparaison est simple (ou doit être rapide), l'équivalence Unicode n'est pas effectuée. Ce problème se produit, par exemple, dans la comparaison canonique XML, voir http://www.w3.org/TR/xml-c14n

Pour éviter ce problème ... Quelle norme utiliser? "UTF8 étendu" ou "UTF8 compact"?
Utilisez "ç" ou "c + ◌̧."?

Le W3C et d'autres (ex. noms de fichiers ) suggèrent d'utiliser le "composé comme canonique" (gardez à l'esprit le C des chaînes plus courtes "les plus compactes") ... Donc,

La norme est [~ # ~] c [~ # ~]! en cas de doute, utilisez [~ # ~] nfc [~ # ~]

Pour l'interopérabilité et pour choix "convention sur configuration" , la recommandation est l'utilisation de [~ # ~] nfc [~ # ~] , pour "canoniser" les chaînes externes. Pour stocker du XML canonique, par exemple, stockez-le dans le "FORM_C". Le W3C CSV sur le Web Working Group recommande également NFC (section 7.2).

PS: de "FORM_C" est le formulaire par défaut dans la plupart des bibliothèques. Ex. dans normalizer.isnormalized () de PHP .


Le terme " compostion forme" (FORM_C) Est utilisé pour les deux, pour dire que "une chaîne est sous la forme canonique C" (le résultat d'un NFC transformation) et dire qu'un algorithme de transformation est utilisé ... Voir http://www.macchiato.com/unicode/nfc-faq

(...) chacune des séquences suivantes (les deux premières étant des séquences à caractère unique) représentent le même caractère:

  1. U + 00C5 (Å) LETTRE MAJUSCULE LATINE A BAGUE CI-DESSUS
  2. U + 212B (Å) SIGNE ANGSTROM
  3. U + 0041 (A) LETTRE MAJUSCULE LATINE A + U + 030A (̊) ANNEAU COMBINÉ CI-DESSUS

Ces séquences sont appelées canoniquement équivalentes. Le premier de ces formulaires est appelé NFC - pour le formulaire de normalisation C, où le C est pour compostion. (...) Une fonction transformant une chaîne S au format NFC peut être abrégée en toNFC(S), tandis que celle qui teste si S est en NFC est abrégé en isNFC(S).


Remarque: pour tester la normalisation de petites chaînes (références UTF-8 pures ou entités XML), vous pouvez utiliser ce test/normaliser le convertisseur en ligne .

1
Peter Krauss