Отображение шестнадцатеричного значения строки из oracle varchar2?
У нас возникают проблемы с текстом, который кодируется несколькими способами, но хранится в одном столбце таблицы. Длинная история. В MySQL я могу "выбрать hex (str) из таблицы where", и я вижу байты строки точно так, как я их устанавливал.
В Oracle у меня есть строка, которая начинается с турецкого символа İ, который является символом Юникода 0x0130 "ЛАТИНСКОЕ КАПИТАЛОВОЕ ПИСЬМО С ДВОЙНОЙ ВЫШЕ". Это в моей печатной копии книги Unicode версии 2.0. В UTF-8 этот символ равен 0xc4b0.
У нас очень старые клиентские приложения, которые нам нужно поддерживать. Они отправят нам этот текст в "windows-1254". Раньше мы просто закрывали глаза, хранили его и отдавали обратно. Теперь нам нужен Unicode, или нам дается Unicode.
Итак, у меня есть:
SQL> select id, name from table where that thing;
ID NAME
------ ------------------------
746 Ý
Это имеет смысл, потому что "İ" - это 0xdd в windows-1254, а 0xdd в wondows-1252 - "Ý". Мой терминал предположительно установлен на обычный Windows-1252.
Но:
SQL> select id, rawtohex(name) from table where that thing;
ID RAWTOHEX(NAME)
------ ------------------------
746 C39D
Кажется, что нет никакого эквивалента функции hex (name) в MySQL. Но я должен что-то упустить. Что мне здесь не хватает?
Мой код Java должен принять utf8, который мне предоставляется, и сохранить копию utf8 и копию окна-1252. Код java дает мне:
bytes (utf8): c4 b0
bytes (1254): dd
Тем не менее, когда я его сохраняю, клиент не получает правильный символ. И когда я пытаюсь понять, что на самом деле хранит Oracle, я получаю мусор, увиденный выше. Я понятия не имею, откуда C39D. Любые предложения?
У нас есть ojdbc14.jar, встроенный во все наши приложения, и мы подключаемся к базе данных, которая говорит, что это "Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production".
Ответы
Ответ 1
Используйте функцию dump
, чтобы увидеть, как Oracle хранит данные внутренне.
У вас, похоже, есть недоразумение относительно того, как Oracle обрабатывает символы VARCHAR2
, которые задают преобразования: вы не можете влиять на то, как Oracle сохраняет свои данные физически. (Также, если вы еще этого не сделали, полезно прочитать: Абсолютный минимум Каждый разработчик программного обеспечения абсолютно должен положительно знать о юникодном и символьном наборах).
Ваш клиент говорит Oracle только в двоичном формате. Фактически все системы обмениваются информацией только в двоичном формате. Чтобы понимать друг друга, необходимо, чтобы обе системы знали, какой язык (набор символов) используется.
В вашем случае мы можем восстановить то, что происходит:
- Ваш клиент отправляет байт
dd
в Oracle и говорит, что это windows-1252
(вместо 1254)
.
- Oracle ищет свою таблицу набора символов и видит, что эти данные переводятся в символ
Ý
в этом наборе символов.
- Oracle логически сохраняет эту информацию в своей таблице.
-
Поскольку Oracle настроен в UTF-8
, он преобразует эти данные в двоичную репликацию UTF-8
Ý
:
SQL> SELECT rawtohex('Ý') FROM dual;
RAWTOHEX('Ý')
--------------
C39D
-
Oracle хранит C39D
внутренне.
Как вы можете видеть, проблема возникает с первого шага: есть проблема настройки. Пока вы не исправите это, системы не смогут успешно вести диалог.
Преобразование автоматически при использовании VARCHAR2
, потому что этот тип данных является логическим текстовым интерфейсом (у вас нет контроля над тем, чтобы заставлять фактические двоичные данные сохраняться).
Ответ 2
У меня есть байты в UTF-8, чтобы начать.
String strFromUTF8 = new String(bytes, "UTF8");
byte[] strInOldStyle = strFromUTF8.getBytes("Cp1254");
С MySQL я закончил. Я беру эти байты, превращаю их в шестнадцатеричную строку и выполняю обновление с помощью unhex (hexStr). Это позволяет мне помещать устаревшие байты в столбцы varchar.
С Oracle я должен сделать:
String again = new String(strInOldStyle, "Cp1254");
byte[] nextOldBytes = again.getBytes("UTF8");
Теперь я могу сделать обновление и получить байты в столбце varchar2 с помощью:
update table set colName = UTL_RAW.CAST_TO_VARCHAR2(HEXTORAW('hexStr')) where ...
Странно, нет? Я уверен, что сделал это более сложным, чем нужно.
Что мы видим, это, тем не менее,
"İ" in UTF-8 == 0xc4d0
"İ" in Cp1254 == 0xdd == "Ý" in Cp1252
"Ý" in UTF-8 == 0xc3d9
Итак, если я получаю строку "İ" и делаю:
update table set name = UTL_RAW.CAST_TO_VARCHAR2(HEXTORAW('C3D9')) where ...
Затем наш старый клиент дает нам "İ". Ага. Он работает.