Как сохранить данные Unicode в Oracle?

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

http://i25.tinypic.com/dvpqnk.png

Я понимаю, что 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;
    }
}

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

Текст перед отправкой в ​​базу данных в окне сообщения: http://i31.tinypic.com/2ymdo2u.png

Текст после получения из базы данных в окне сообщения: http://i28.tinypic.com/2wmq9a0.png

Вывод на консоль: http://i29.tinypic.com/6xz3px.png

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

6 ответов

Решение

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

строка ввода = "•"; char s = input[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

Вы можете определить, какой набор символов ваша база данных использует для 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 Τι κάνεις;

Еще одна вещь, которую стоит отметить.

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

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

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

command.CommandText = "INSERT into UNICODEDATA (ID, UNICODESTRING) Values (11, N'Τι κάνεις;')";

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

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

Решение: установите 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, обязательно исправьте все версии в реестре (в моем случае Oracle 10 имел допустимую настройку, но в Oracle 11 вообще не было установлено значение NLS_LANG).

Другие вопросы по тегам