当前位置: 首页 > 知识库问答 >
问题:

OdbcConnection将汉字返回为“?”

商迪
2023-03-14

我有一个Oracle数据库,用简体中文存储一些数据值。我创建了一个ASP.NET MVC C#网页,该网页应该显示此信息。我正在使用odbcconnection来检索数据,但是当我运行da.fill(t)命令时,值返回为“?”

        OdbcCommand cmd = new OdbcCommand();
        cmd.CommandText = select;

        OdbcConnection SqlConn = new OdbcConnection("Driver={Oracle in instantclient_11_2};Dbq=Database;Uid=Username;pwd=password;");
        DataTable t = new DataTable();
        cmd.Connection = SqlConn;

        SqlConn.Open();
        OdbcDataAdapter da = new OdbcDataAdapter(cmd);
        SqlConn.Close();
        da.Fill(t);
        return t;

T有数据,但所有被认为是汉字的东西只是一系列“??????”

共有1个答案

法兴德
2023-03-14

字符集的问题是相当常见的,让我试着给出一些一般性的注释。

原则上,您必须考虑四种不同的字符集设置。

示例:AL32UTF8

    SELECT * 
    FROM V$NLS_PARAMETERS 
    WHERE PARAMETER IN ('NLS_CHARACTERSET', 'NLS_NCHAR_CHARACTERSET');

此值仅在您的客户端上定义。NLS_LANG与在数据库中存储字符的能力无关。它用于让Oracle知道您在客户端使用的字符集。当您设置NLS_LANG值(例如,设置为AL32UTF8)时,您只需告诉Oracle数据库“我的客户端使用字符集AL32UTF8”--这并不一定意味着您的客户端真的在使用AL32UTF8!(见下文#4)

NLS_LANG可以由环境html" target="_blank">变量NLS_LANG定义,也可以由HKLM\Software\WOW6432Node\Oracle\Key_%ORACLE_HOME_NAME%\NLS_LANG处的Windows注册表定义(对于32位)。hklm\software\oracle\key_%oracle_home_name%\nls_lang(用于64位)。根据您的应用程序,可能有其他方法来指定NLS_LANG,但让我们坚持使用基本方法。如果未提供NLS_LANG值,则Oracle将其默认为american_america.us7ascii

NLS_LANG的格式NLS_LANG=Language_Territore.charset。NLS_LANG的{charset}部分未显示在任何系统表或视图中。NLS_LANG=.WE8ISO8859P1,NLS_LANG=_Germany,NLS_LANG=American,NLS_LANG=Italian_.WE8MSWin1252,NLS_LANG=_Belgium.US7ASCII

如上所述,NLS_LANG的{charset}部分在数据库中的任何系统表/视图或任何函数中都不可用。严格地说,这是正确的,但是您可以运行以下查询:

SELECT DISTINCT CLIENT_CHARSET
FROM V$SESSION_CONNECT_INFO
WHERE (SID, SERIAL#) = (SELECT SID, SERIAL# FROM v$SESSION WHERE AUDSID = USERENV('SESSIONID'));

它应该从您当前的NLS_LANG设置返回字符集-但是根据我的经验,该值通常为NULL或unknown,即不可靠。

在这里找到更多非常有用的信息:NLS_LANG常见问题

OraOLEDB(来自Oracle)始终使用UTF-16(请参阅OraOLEDB提供程序特定功能)

基于Java的JDBC(例如SQL Developer)有自己的方法来处理字符集(请参阅数据库JDBC Developer指南-全球化支持以了解更多详细信息)

示例:UTF-8

最重要的一点是匹配NLS_LANG和终端的“真实”字符集。应用程序或.sql文件的编码

一些常见的配对有:

>

  • CP850->WE8PC850

    CP1252或ANSI(在“Western”PC的情况下)->WE8MSWin1252

    ISO-8859-1->WE8ISO8859P1

    ISO-8859-15->WE8ISO8859P15

    或运行此查询以获取更多信息:

    SELECT VALUE AS ORACLE_CHARSET, UTL_I18N.MAP_CHARSET(VALUE) AS IANA_NAME
    FROM V$NLS_VALID_VALUES
    WHERE PARAMETER = 'CHARACTERSET';
    

    一些技术使您的生活变得更简单,例如ODP.NET(无管理驱动程序)或Oracle的ODBC驱动程序自动继承NLS_LANG值中的字符集,因此上面的条件始终为真。

    是否需要将客户端NLS_LANG值设置为等于数据库NLS_CharacterSet值?

    不,不一定!例如,如果您有数据库字符集nls_characterset=al32utf8和客户端字符集nls_lang=.zhs32GB18030那么它将不会有任何问题地工作(前提是您的客户端确实使用GB18030),尽管这些字符集是完全不同的。GB18030是中文常用的字符集,和UTF-8一样,它支持所有的Unicode字符。

    如果您有,例如nls_characterset=al32utf8nls_lang=.we8iso8859p1,它也可以工作(同样,前提是您的客户端确实使用ISO-8859-P1)。但是,数据库可能存储客户端无法显示的字符,而客户端将显示一个占位符(例如)。

    无论如何,如果合适,具有匹配的NLS_LANG和NLS_CHARACTERSET值是有益的。如果它们相等,则可以确保任何可能存储在数据库中的字符也可以显示,任何输入终端或写入.sql文件的字符也可以存储在数据库中,并且不被占位符替代。

    因此,您可以多次阅读“NLS_LANG字符集必须与您的数据库字符集相同”这样的建议(这里也是如此)。这根本不是真的,是一个流行的神话!

    下面是证据:

    C:\>set NLS_LANG=.AL32UTF8
    
    C:\>sqlplus ...
    
    SQL> SET SERVEROUTPUT ON
    SQL> DECLARE
      2  CharSet VARCHAR2(20);
      3  BEGIN
      4     SELECT VALUE INTO Charset FROM nls_database_parameters WHERE parameter = 'NLS_CHARACTERSET';
      5     DBMS_OUTPUT.PUT_LINE('Database NLS_CHARACTERSET is '||Charset);
      6     IF UNISTR('\20AC') = '€' THEN
      7             DBMS_OUTPUT.PUT_LINE ( '"€" is equal to U+20AC' );
      8     ELSE
      9             DBMS_OUTPUT.PUT_LINE ( '"€" is not the same as U+20AC' );
     10     END IF;
     11  END;
     12  /
    
    Database NLS_CHARACTERSET is AL32UTF8
    "€" is not the same as U+20AC
    
    PL/SQL procedure successfully completed.
    

    客户端和数据库字符集都是AL32UTF8,但字符不匹配。原因是,我的cmd.exe以及SQL*Plus使用的是Windows CP1252。因此我必须相应地设置NLS_LANG:

    C:\>chcp
    Active code page: 1252
    
    C:\>set NLS_LANG=.WE8MSWIN1252
    
    C:\>sqlplus ...
    
    SQL> SET SERVEROUTPUT ON
    SQL> DECLARE
      2  CharSet VARCHAR2(20);
      3  BEGIN
      4     SELECT VALUE INTO Charset FROM nls_database_parameters WHERE parameter = 'NLS_CHARACTERSET';
      5     DBMS_OUTPUT.PUT_LINE('Database NLS_CHARACTERSET is '||Charset);
      6     IF UNISTR('\20AC') = '€' THEN
      7             DBMS_OUTPUT.PUT_LINE ( '"€" is equal to U+20AC' );
      8     ELSE
      9             DBMS_OUTPUT.PUT_LINE ( '"€" is not the same as U+20AC' );
     10     END IF;
     11  END;
     12  /
    
    Database NLS_CHARACTERSET is AL32UTF8
    "€" is equal to U+20AC
    
    PL/SQL procedure successfully completed.
    

    还请考虑以下示例:

    CREATE TABLE ARABIC_LANGUAGE (
        LANG_CHAR VARCHAR2(20), 
        LANG_NCHAR NVARCHAR2(20));
    
    INSERT INTO ARABIC_LANGUAGE VALUES ('العربية', 'العربية');
    

    您需要为单个语句的NLS_LANG设置两个不同的值--这是不可能的。

  •  类似资料:
    • 我得到了谷歌云存储桶的URL。我必须: > 对于每个blob,我进行一些gcsapi调用,以获取关于blob的信息(blob.size、blob.name等) 对于每个Blob,我还必须读取它,在它里面找到一些东西,并将其添加到从GCS API调用中获得的值中 对于每个blob,我必须将步骤2和步骤3中找到的关于blob的值写入BigQuery 我有数千个blob,因此这需要使用ApacheBea

    • 问题内容: 我正在编写一个将数据存储在字典对象中的程序,但是该数据需要在程序执行过程中的某个时候保存,并在再次运行该程序时重新加载到字典对象中。我如何将字典对象转换为可以写入文件并再加载回字典对象的字符串?希望这将支持包含词典的词典。 问题答案: json模块是一个很好的解决方案。与pickle相比,它的优势在于它仅生成纯文本输出,并且是跨平台和跨版本的。

    • 问题内容: 为了使进度报告过程更加可靠,并使它与请求/响应脱钩,我正在Windows Service中执行处理,并将预期的响应持久化到文件中。当客户端开始轮询更新时,其目的是控制器以JSON字符串形式返回文件的内容(无论它们是什么)。 该文件的内容已预序列化为JSON。这是为了确保在响应过程中没有任何阻碍。无需进行任何处理(只需将文件内容读入字符串并返回)即可获得响应。 我最初虽然很简单,但事实并

    • 问题内容: 为了转换字符串,我将其转换为字节,如下所示: 为了转换,我做了:显然不起作用。我将如何转换回去? 问题答案: 您原来的城市名称中有哪些字符?尝试这样的UTF-8版本:

    • 问题内容: 我试图除以2计数以返回一个百分比。 返回以下查询: 我应该应聘演员吗? 问题答案: 我会用两个s来做不同的事情:

    • 问题内容: 我有一个javascript函数,该函数调用通用函数对服务器进行ajax调用。我需要从ajax调用的回调函数中检索结果(true / false),但是我得到的结果始终是’undefined’。 如果没有我的全部逻辑,泛型函数的超级简化版本将是: 调用它的函数将类似于: “结果”变量始终为“未定义”,并且对其进行调试,我可以看到正在执行回调函数的“返回真”行。 为什么会这样?如何将返回