web-dev-qa-db-fra.com

Affichage de la valeur hexadécimale d'une chaîne à partir d'un varchar2 Oracle?

Nous rencontrons des problèmes avec du texte codé de différentes manières mais conservé dans une seule colonne d'un tableau. Longue histoire. Sur MySQL, je peux faire "sélectionner hex (str) dans la table où" et je vois les octets de la chaîne exactement comme je les ai définis.

Sur Oracle, j'ai une chaîne qui commence par le caractère turc ©, qui est le caractère Unicode 0x0130 "LETTRE MAJUSCULE LATINE AVEC POINT CI-DESSUS". C'est dans ma copie imprimée du livre Unicode Version 2.0. En UTF-8, ce caractère est 0xc4b0.

Nous avons de très anciennes applications clientes que nous devons prendre en charge. Ils nous enverraient ce texte dans "windows-1254". Nous avions l'habitude de fermer les yeux, de le ranger et de le rendre plus tard. Maintenant, nous avons besoin de l'Unicode, ou recevons l'Unicode.

Donc j'ai:

SQL> select id, name from table where that thing;

ID     NAME
------ ------------------------
746    Ý

Cela a du sens car le "İ" est 0xdd dans windows-1254 et 0xdd dans wondows-1252 est "Ý". Mon terminal est vraisemblablement réglé sur la fenêtre 1252 habituelle.

Mais:

SQL> select id, rawtohex(name) from table where that thing;

ID     RAWTOHEX(NAME)
------ ------------------------
746    C39D

Il ne semble pas y avoir d'équivalent à la fonction hex (nom) dans MySQL. Mais je doit manquer quelque chose. Qu'est-ce que j'oublie ici?

Mon code Java doit prendre l'utf8 qui m'est fourni et enregistrer une copie utf8 et une copie windows-1252. Le code Java Java me donne:

bytes (utf8):  c4 b0
bytes (1254):  dd

Pourtant, lorsque je l'enregistre, le client n'obtient pas le bon caractère. Et quand j'essaie de voir ce qu'Oracle a réellement stocké, j'obtiens les ordures vues ci-dessus. J'ai non idée d'où vient le C39D. Aucune suggestion?

Nous avons intégré ojdbc14.jar dans toutes nos applications et nous nous connectons à une base de données qui indique qu'il s'agit de "Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production".

13
Ray Kiddy

Utilisez la fonction dump pour voir comment Oracle stocke les données en interne.

Vous semblez avoir un malentendu sur la façon dont Oracle traite VARCHAR2 conversions de jeux de caractères: vous ne pouvez pas influencer physiquement la façon dont Oracle stocke ses données . (De plus, si vous ne l'avez pas déjà fait, il est utile de lire: Le minimum absolu que chaque développeur de logiciel doit absolument connaître à propos d'Unicode et des jeux de caractères ).

Votre client ne parle à Oracle qu'en binaire. En fait, tous les systèmes échangent des informations uniquement en binaire. Pour se comprendre, il est nécessaire que les deux systèmes sachent quelle langue (jeu de caractères) est utilisée.

Dans votre cas, nous pouvons reconstruire ce qui se passe:

  1. Votre client envoie l'octet dd à Oracle et lui dit qu'il est windows-1252 (au lieu de 1254).
  2. Oracle recherche sa table de jeux de caractères et voit que ces données sont traduites en symbole Ý dans ce jeu de caractères.
  3. Oracle logiquement stocke ces informations dans sa table.
  4. Comme Oracle est configuré dans UTF-8, il convertit ces données en UTF-8 représentation binaire de Ý:

    SQL> SELECT rawtohex('Ý') FROM dual;
    
    RAWTOHEX('Ý')
    --------------
    C39D
    
  5. Oracle stocke C39D en interne.

Comme vous pouvez le voir, le problème vient de la première étape: il y a un problème de configuration. Tant que vous ne résolvez pas cela, les systèmes ne pourront pas dialoguer avec succès.

La conversion est automatique lorsque vous utilisez VARCHAR2 car ce type de données est une interface de symbole de texte logique (vous n'avez pratiquement aucun contrôle sur le forçage des données binaires réelles stockées).

19
Vincent Malgrat

J'ai des octets en UTF-8 pour commencer.

String strFromUTF8 = new String(bytes, "UTF8");
byte[] strInOldStyle = strFromUTF8.getBytes("Cp1254");

Avec MySQL, j'ai terminé. Je prends ces octets, les transforme en une chaîne hexadécimale et fais une mise à jour avec unhex (hexStr). Cela me permet de mettre les octets hérités dans une colonne varchar.

Avec Oracle, je dois faire:

String again = new String(strInOldStyle, "Cp1254");
byte[] nextOldBytes = again.getBytes("UTF8");

Maintenant, je peux faire une mise à jour et obtenir les octets dans une colonne varchar2 avec:

update table set colName = UTL_RAW.CAST_TO_VARCHAR2(HEXTORAW('hexStr')) where ...

Étrange, non? Je suis sûr d'avoir rendu cela plus complexe que nécessaire.

Ce que nous voyons est ceci, cependant,

"İ" in UTF-8 == 0xc4d0
"İ" in Cp1254 == 0xdd == "Ý" in Cp1252
"Ý" in UTF-8 == 0xc3d9

Donc, si je reçois la chaîne "İ" et fais:

update table set name = UTL_RAW.CAST_TO_VARCHAR2(HEXTORAW('C3D9')) where ...

Ensuite, notre ancien client nous donne un "İ". Oui. Ça marche.

5
Ray Kiddy