OdbcConnection返回汉字为“?”

我有一个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和2: NLS_CHARACTERSETNLS_NCHAR_CHARACTERSET

示例: AL32UTF8

它们在您的数据库中定义,您可以使用它们进行查询

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

这些设置定义了哪些字符(以哪种格式)可以存储在数据库中 – 不多也不少。 如果您必须在现有数据库上进行更改,则需要付出一些努力(请参阅字符集迁移和/或适用于Unicode的Oracle数据库迁移助手 )。

3: NLS_LANG

示例: AMERICAN_AMERICA.AL32UTF8

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

NLS_LANG可以由环境变量NLS_LANG或Windows注册表定义在HKLM\SOFTWARE\Wow6432Node\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG (对于32位),resp。 HKLM\SOFTWARE\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG (64位)。 根据您的应用程序,可能还有其他方法来指定NLS_LANG,但让我们坚持基础知识。 如果未提供NLS_LANG值,则Oracle将其默认为AMERICAN_AMERICA.US7ASCII

NLS_LANG的格式为NLS_LANG=language_territory.charset 。 NLS_LANG的{ charset }部分显示在任何系统表或视图中。 NLS_LANG定义的所有组件都是可选的,因此以下定义均有效: NLS_LANG=.WE8ISO8859P1NLS_LANG=_GERMANYNLS_LANG=AMERICANNLS_LANG=ITALIAN_.WE8MSWIN1252NLS_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常见问题解答

4:终端的“真实”字符集,应用程序或.sql文件的编码

示例: UTF-8

如果您在Windows终端上工作(即使用SQL * plus),您可以使用命令chcp查询代码页,在Unix / Linux上,等效的是locale charmapecho $LANG 。 您可以从此处获取所有Windows代码页标识符的列表: 代码页标识符 。 注意,对于UTF-8( chcp 65001 ),存在一些问题,请参阅此讨论 。

如果使用.sql文件和TOAD或SQL-Developer之类的编辑器,则必须检查保存选项。 通常您可以选择UTF-8ANSIISO-8859-1等值ISO-8859-1表示Windows ANSI代码页,通常为CP1252 ,您可以在HKLM\SYSTEM\ControlSet001\Control\Nls\CodePage\ACP检入您的注册表或在此处: 国家语言支持(NLS)API参考

[Microsoft删除了此引用,将其作为Web存档国家语言支持(NLS)API参考 ]

注意:根据您的技术,您不必太在意这个主题,例如:

  • ODP.NET(unmanged driver)自动从NLS_LANG值inheritance字符集。

  • ODP.NET托管驱动程序不敏感NLS_LANG 。 它只对.NET语言环境敏感。 (请参阅.NET Developer’s Guide的数据提供程序 )

  • OraOLEDB(来自Oracle)始终使用UTF-16(参见OraOLEDB Provider特定function )

  • 基于Java的JDBC(例如SQL Developer)有自己的方法来处理字符集(有关详细信息,请参阅数据库JDBC开发人员指南 – 全球化支持 )

如何设置所有这些值?

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

一些常见的配对是:

  • CP850 – > WE8PC850

  • CP1252或ANSI(在“Western”PC的情况下) – > WE8MSWIN1252

  • ISO-8859-1 – > WE8ISO8859P1

  • ISO-8859-15 – > WE8ISO8859P15

  • UTF-8 – > AL32UTF8

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

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

是否需要将客户端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字符集必须与数据库字符集相同”(也在SO上)。 这根本不是真的,也是一个流行的神话!

这是certificate:

 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设置两个不同的值 – 这是不可能的。