【问题标题】:How to save unicode data to oracle?如何将unicode数据保存到oracle?
【发布时间】:2010-11-14 14:30:41
【问题描述】:

我正在尝试将 unicode 数据(希腊语)保存在 oracle 数据库(10 g)中。我创建了一个简单的表:

我了解 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;
    }
}

这里有不同的输出:

在消息框中发送到数据库之前的文本:

在消息框中从数据库获取后的文本:

控制台输出:

你能建议我在这里做错什么吗?

【问题讨论】:

    标签: c# oracle unicode oracle10g


    【解决方案1】:

    我可以看到五个潜在的问题领域:

    1. 您实际上是如何将文本输入到您的 .NET 应用程序中的?如果它在字符串文字中硬编码,您确定编译器为您的源文件采用正确的编码吗?

    2. 您将其发送到数据库的方式可能存在问题。

    3. 它在数据库中的存储方式可能存在问题。

    4. 您在数据库中获取数据的方式可能存在问题。

    5. 您之后再次显示的方式可能存在问题。

    现在区域 2-4 听起来比 1 和 5 不太可能成为问题。之后您如何显示文本?您实际上是从 .NET 中的数据库中获取它,还是使用 Toad 或类似的东西来尝试查看它?

    如果您再次从 .NET 中写出,我建议您完全跳过数据库 - 如果您只显示字符串本身,您会看到什么?

    我在debugging Unicode problems 上有一篇文章可能对您有用。特别要注意编码可能出错的每一个地方,并确保每当你“显示”一个字符串时你转储出确切的Unicode字符(作为整数),这样你就可以检查那些而不是而不仅仅是您当前想要显示的字体。

    编辑:好的,所以数据库涉及到问题的某个地方。

    强烈建议您将 ASP 和 HTML 之类的东西排除在外。编写一个简单的控制台应用程序,它什么都不做,但插入字符串并再次获取它。让它在前后转储单个 Unicode 字符(作为整数)。然后尝试查看数据库中的内容(例如使用 Toad)。我不知道 Oracle 函数将字符串转换为单个 Unicode 字符序列,然后将这些字符转换为整数,但这很可能是我接下来要尝试的事情。

    编辑:另外两个建议(很高兴看到控制台应用程序,顺便说一句)。

    1. 指定参数的数据类型,而不是仅仅给它一个对象。例如:

      command.Parameters.Add (":UnicodeString",
                              OracleType.NVarChar).Value = stringToSave;
      
    2. 考虑使用 Oracle 自己的驱动程序而不是 .NET 中内置的驱动程序。无论如何,您可能希望这样做,因为我相信它通常被认为更快、更可靠。

    【讨论】:

    • 如果我跳过数据库直接显示字符串,它会正确显示希腊字符串。我已经更新了关于如何从数据库中获取数据的问题。请问你能多介绍一下吗?
    • 另外需要注意的是,如果我使用 SQL Server express edition 并做同样的事情(替换通过 Linq 查询插入和获取数据的代码),它会正确显示字符串。
    • Jon:我已经更新了问题(包含控制台应用程序中的代码)。奇怪的是控制台输出也被搞砸了,但消息框显示正确......
    • 你摇滚!您的第一点解决了问题,我也重视您的第二点(blogs.msdn.com/adonet/archive/2009/06/15/…)。感谢您的帮助和耐心。
    • 太棒了!这似乎很遗憾(我真的只是在猜测)但我很高兴它解决了它:)
    【解决方案2】:

    您可以通过查询确定数据库对 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 Τι κάνεις;
    

    【讨论】:

    • 我尝试了与您使用 SQLPlus 完全相同的方法。它说 NVARCHAR 的编码是 AL16UTF16 (和你的一样)。不同之处在于我不能像你那样编写插入命令。当我粘贴时,希腊文本被转换为随机问号。我使用的是 10.2 版本的 SQLLite!
    【解决方案3】:

    还有一点值得注意。

    如果您使用的是 oracle 客户端,并且希望在 CommandText 中包含 unicode 字符,则应将以下行添加到应用程序的开头:

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

    这将允许您在需要时使用以下语法:

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

    【讨论】:

    • 很奇怪。这似乎只适用于 ODBC 驱动程序。知道为什么吗?
    【解决方案4】:

    阅读记录,试试

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

    【讨论】:

      【解决方案5】:

      经过一些调查,我们开始:

      字符串输入 = "•"; 字符 s = 输入[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();
              }
          }
      

      【讨论】:

        【解决方案6】:

        解决方案:设置 NLS_LANG!

        详情: 我只是遇到了同样的问题,实际上与 Sergey Bazarnik 的调查中描述的情况完全相同。使用绑定变量它可以工作,没有它们就不行。

        解决方案是将 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)。

        【讨论】:

          猜你喜欢
          • 2012-12-23
          • 1970-01-01
          • 1970-01-01
          • 2012-01-15
          • 2018-03-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多