Как сохранить данные юникода в оракул?

Я пытаюсь сохранить данные юникода (греческий) в базе данных оракула (10 г). Я создал простую таблицу:

альтернативный текст

Я понимаю, что NVARCHAR2 всегда использует кодировку UTF-16, поэтому она должна подойти для всех (человеческих) языков.

Затем я пытаюсь вставить строку в базу данных. Я жестко закодировал строку («Как дела?» по-гречески) в коде. Затем я пытаюсь вернуть его из базы данных и показать.

class Program
{
    static string connectionString = "<my connection string>";

    static void Main (string[] args) {
        string textBefore = "Τι κάνεις;";

        DeleteAll ();
        SaveToDatabase (textBefore);
        string textAfter = GetFromDatabase ();

        string beforeData = String.Format ("Before: {0}, ({1})", textBefore, ToHex (textBefore));
        string afterData = String.Format ("After: {0}, ({1})", textAfter, ToHex (textAfter));

        Console.WriteLine (beforeData);
        Console.WriteLine (afterData);

        MessageBox.Show (beforeData);
        MessageBox.Show (afterData);

        Console.ReadLine ();
    }

    static void DeleteAll () {
        using (var oraConnection = new OracleConnection (connectionString)) {
            oraConnection.Open ();
            var command = oraConnection.CreateCommand ();

            command.CommandText = "delete from UNICODEDATA";
            command.ExecuteNonQuery ();
        }            
    }

    static void SaveToDatabase (string stringToSave) {
        using (var oraConnection = new OracleConnection (connectionString)) {
            oraConnection.Open ();
            var command = oraConnection.CreateCommand ();

            command.CommandText = "INSERT into UNICODEDATA (ID, UNICODESTRING) Values (11, :UnicodeString)";
            command.Parameters.Add (":UnicodeString", stringToSave);
            command.ExecuteNonQuery ();
        }
    }

    static string GetFromDatabase () {
        using (var oraConnection = new OracleConnection (connectionString)) {
            oraConnection.Open ();

            var command = oraConnection.CreateCommand ();
            command.CommandText = "Select * from UNICODEDATA";
            var erpReader = command.ExecuteReader ();

            string s = String.Empty;
            while (erpReader.Read ()) {
                string text = erpReader.GetString (1);
                s += text + ", ";
            }

            return s;
        }
    }

    static string ToHex (string input) {
        string bytes = String.Empty;
        foreach (var c in input)
            bytes += ((int)c).ToString ("X4") + " ";

        return bytes;
    }
}

Вот разные выходы:

Текст перед отправкой в ​​базу данных в окне сообщения: альтернативный текст

Текст после получения из базы данных в окне сообщения: альтернативный текст

Вывод консоли: альтернативный текст

Пожалуйста, не могли бы вы подсказать, что я могу делать неправильно здесь?


person Hemant    schedule 28.07.2009    source источник


Ответы (6)


Я вижу пять потенциальных областей для проблем:

  1. Как вы на самом деле получаете текст в свое приложение .NET? Если он жестко запрограммирован в виде строкового литерала, уверены ли вы, что компилятор использует правильную кодировку для вашего исходного файла?

  2. Может быть проблема в том, как вы отправляете его в базу данных.

  3. Может быть проблема с тем, как он хранится в базе данных.

  4. Может быть проблема с тем, как вы извлекаете его из базы данных.

  5. Может возникнуть проблема с тем, как вы снова отобразите его после этого.

Теперь области 2-4 кажутся менее вероятными, чем 1 и 5. Как вы потом отображаете текст? Вы на самом деле извлекаете его из базы данных в .NET или используете Toad или что-то подобное, чтобы попытаться его увидеть?

Если вы снова пишете это из .NET, я предлагаю вам полностью пропустить базу данных — если вы просто отобразите саму строку, что вы увидите?

У меня есть статья, которая может оказаться полезной для вас по отладке проблем с Unicode. В частности, сосредоточьтесь на каждом месте, где кодировка может пойти не так, и убедитесь, что всякий раз, когда вы «отображаете» строку, вы выводите точные символы Unicode (как целые числа), чтобы вы могли проверить их, а не чем просто то, что ваш текущий шрифт хочет отобразить.

РЕДАКТИРОВАТЬ: Итак, база данных замешана где-то в проблеме.

Я настоятельно предлагаю вам исключить из уравнения все, что связано с ASP и HTML. Напишите простое консольное приложение, которое ничего не делает, кроме как вставляет строку и извлекает ее снова. Заставьте его сбрасывать отдельные символы Unicode (как целые числа) до и после. Затем попробуйте посмотреть, что находится в базе данных (например, с помощью Toad). Я не знаю функций Oracle для преобразования строк в последовательности отдельных символов Unicode, а затем преобразования этих символов в целые числа, но вполне возможно, что это будет следующая вещь, которую я попробую.

РЕДАКТИРОВАТЬ: еще два предложения (приятно видеть консольное приложение, кстати).

  1. Укажите тип данных для параметра, а не просто присвойте ему объект. Например:

    command.Parameters.Add (":UnicodeString",
                            OracleType.NVarChar).Value = stringToSave;
    
  2. Рассмотрите возможность использования собственного драйвера Oracle вместо встроенного в .NET. Вы можете сделать это в любом случае, так как обычно считается, что это быстрее и надежнее, как мне кажется.

person Jon Skeet    schedule 28.07.2009
comment
Если я пропущу базу данных и покажу строку напрямую, она правильно отобразит греческую строку. Я обновил вопрос о том, как я получаю данные из базы данных. Пожалуйста, не могли бы вы пролить на это больше света? - person Hemant; 28.07.2009
comment
Следует также отметить, что если я использую экспресс-выпуск SQL Server и делаю то же самое (заменяя код вставки и получения данных запросами Linq), он правильно отображает строку. - person Hemant; 28.07.2009
comment
Джон: Я обновил вопрос (содержит код в консольном приложении). Странно, что консольный вывод тоже испорчен, но окно сообщения отображается корректно... - person Hemant; 28.07.2009
comment
Ты жжешь! Ваш первый пункт решил проблему, и я также ценю ваш второй пункт (blogs.msdn.com/adonet/archive/2009/06/15/). Спасибо за помощь и терпение. - person Hemant; 28.07.2009
comment
Превосходно! Обидно, что это необходимо (я действительно просто догадывался), но я рад, что это решило проблему :) - person Jon Skeet; 28.07.2009

Вы можете определить, какой набор символов ваша база данных использует для NCHAR с запросом:

SQL> SELECT VALUE
  2    FROM nls_database_parameters
  3   WHERE parameter = 'NLS_NCHAR_CHARACTERSET';

VALUE
------------
AL16UTF16

чтобы проверить правильность конфигурации вашей базы данных, вы можете запустить в SQL*Plus следующее:

SQL> CREATE TABLE unicodedata (ID NUMBER, unicodestring NVARCHAR2(100)); 

Table created
SQL> INSERT INTO unicodedata VALUES (11, 'Τι κάνεις;');

1 row inserted
SQL> SELECT * FROM unicodedata;

        ID UNICODESTRING
---------- ---------------------------------------------------
        11 Τι κάνεις;
person Vincent Malgrat    schedule 28.07.2009
comment
Я пробовал то же самое, что и вы, используя SQLPlus. Он сказал, что кодировка для NVARCHAR - AL16UTF16 (такая же, как у вас). Разница в том, что я не могу написать команду вставки, как это сделали вы. Когда я вставляю, греческий текст преобразуется в случайные вопросительные знаки. Я использую версию SQLLite 10.2! - person Hemant; 28.07.2009

Стоит отметить еще одну вещь.

Если вы используете клиент oracle и хотите включить символы Юникода в CommandText, вы должны добавить следующую строку в начало вашего приложения:

System.Environment.SetEnvironmentVariable("ORA_NCHAR_LITERAL_REPLACE", "TRUE");

Это позволит вам, в случае необходимости, использовать следующий синтаксис:

command.CommandText = "INSERT into UNICODEDATA (ID, UNICODESTRING) Values (11, N'Τι κάνεις;')";
person Noam    schedule 28.07.2009
comment
Странный. Кажется, это работает только для драйвера ODBC. Есть идеи, почему? - person Dejan; 13.07.2015

После некоторых исследований мы идем:

ввод строки = "•"; символ с = ввод [0];

       //table kuuku with column kuku(nvarchar2(100))
        string connString = "your connection";

        //CLEAN TABLE
        using (System.Data.OracleClient.OracleConnection cn = new System.Data.OracleClient.OracleConnection(connString))
        {
            cn.Open();
            System.Data.OracleClient.OracleCommand cmd = new System.Data.OracleClient.OracleCommand("delete from  kuku ", cn);
            cmd.ExecuteNonQuery();
            cn.Close();
        }


        //INSERT WITH PARAMETER BINDING - UNICODE SAVED
        using (System.Data.OracleClient.OracleConnection cn = new System.Data.OracleClient.OracleConnection(connString))
        {
            cn.Open();
            System.Data.OracleClient.OracleCommand cmd = new System.Data.OracleClient.OracleCommand("insert into  kuku (kuku) values(:UnicodeString)", cn);
            cmd.Parameters.Add(":UnicodeString", System.Data.OracleClient.OracleType.NVarChar).Value = input + " OK" ;
            cmd.ExecuteNonQuery();
            cn.Close();
        }

        //INSERT WITHOUT PARAMETER BINDING - UNICODE NOT SAVED
        using (System.Data.OracleClient.OracleConnection cn = new System.Data.OracleClient.OracleConnection(connString))
        {
            cn.Open();
            System.Data.OracleClient.OracleCommand cmd = new System.Data.OracleClient.OracleCommand("insert into  kuku (kuku) values('" +input+" WRONG')", cn);
            cmd.ExecuteNonQuery();
            cn.Close();
        }
        //FETCH RESULT
        using (System.Data.OracleClient.OracleConnection cn = new System.Data.OracleClient.OracleConnection(connString))
        {
            cn.Open();
            System.Data.OracleClient.OracleCommand cmd = new System.Data.OracleClient.OracleCommand("select kuku from kuku", cn);
            System.Data.OracleClient.OracleDataReader dr = cmd.ExecuteReader();
            if(dr.Read())
            {
                string output = (string) dr[0];
                char sa = output[0];
            }
            cn.Close();
        }
    }

PL SQL look

person Sergey Bazarnik    schedule 21.11.2013

При чтении записей попробуйте

Encoding utf = Encoding.Default;   
var utfBytes = odatareader.GetOracleString(0).GetNonUnicodeBytes();//OracleDataReader
Console.WriteLine(utf.GetString(utfBytes));
person TenB    schedule 02.11.2011

Решение: установите NLS_LANG!

Детали: У меня просто была такая же проблема, и на самом деле была точно такая же ситуация, как описана в расследовании Сергея Базарника. С переменными связывания работает, а без них - нет.

РЕШЕНИЕ состоит в том, чтобы установить NLS_LANG в нужном месте. Поскольку у меня есть сервер Windows, я установил его в реестре Windows под HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\ORACLE\KEY_OraClient11g_home1

Обратите внимание, что местоположение реестра может отличаться, поэтому самый простой способ — найти в реестре строку «ORACLE_HOME». Также другие системы, такие как Linux, Unix, могут настроить это по-другому (экспортировать NLS_LANG...)

В моем случае я поставил "NLS_LANG"="CROATIAN_CROATIA.UTF8". Поскольку у меня не было этой переменной, она перешла к значению по умолчанию. После изменения реестра вы должны перезапустить процесс. В моем случае я перезапустил IIS.

Что касается причины, по которой он работает с переменными связывания, может быть потому, что это действительно происходит на стороне сервера, а без этого на самом деле происходит на стороне клиента. Таким образом, даже эта БД может вставлять правильные значения - прежде чем это произойдет, клиент вносит нежелательные исправления, поскольку считает, что должен это делать. Это связано с тем, что NLS_LANG по умолчанию использует более простую кодовую страницу. Но вместо того, чтобы выполнять полезную задачу, это создает проблему, которую (как показано в исследовании, трудно понять).

Если у вас несколько версий оракула, обязательно исправьте все версии в реестре (в моем случае у Oracle 10 были допустимые настройки, а у Oracle 11 вообще не было NLS_LANG).

person Veljac    schedule 30.08.2016