我有一个Oracle数据库,该数据库以简体中文存储一些数据值。我创建了一个应该显示此信息的ASP.net MVC C#网页。我正在使用a OdbcConnection来检索数据,但是当我运行da.Fill(t)命令时,值返回为“?”
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 有数据,但是所有应该是汉字的内容只是一系列“ ?????”
t
字符集的问题很常见,让我尝试给出一些一般性注释。
原则上,您必须考虑 四种 不同的字符集设置。
NLS_CHARACTERSET
NLS_NCHAR_CHARACTERSET
例: AL32UTF8
AL32UTF8
它们 仅 在您的数据库上定义,您可以使用
SELECT * FROM V$NLS_PARAMETERS WHERE PARAMETER IN ('NLS_CHARACTERSET', 'NLS_NCHAR_CHARACTERSET');
这些设置定义了哪些字符(以哪种格式)可以存储在数据库中-不多也不少。如果必须在现有数据库上进行更改,则需要付出一些努力(请参阅“ 字符集迁移”和/或“ Oracle Unicode数据库迁移助手”)。
NLS_LANG
例: AMERICAN_AMERICA.AL32UTF8
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位)。HKLM\SOFTWARE\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG(适用于64位)。根据您的应用程序,可能还有其他方法可以指定NLS_LANG,但让我们坚持基础知识。如果未提供NLS_LANG值,则Oracle默认将其设置为AMERICAN_AMERICA.US7ASCII
HKLM\SOFTWARE\Wow6432Node\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG
HKLM\SOFTWARE\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG
AMERICAN_AMERICA.US7ASCII
NLS_LANG的格式为NLS_LANG=language_territory.charset。NLS_LANG 的{ charset }部分 未 显示在任何系统表或视图中。在NLS_LANG定义的所有组件都是可选的,所以下面的定义都是有效的:NLS_LANG=.WE8ISO8859P1,NLS_LANG=_GERMANY,NLS_LANG=AMERICAN,NLS_LANG=ITALIAN_.WE8MSWIN1252,NLS_LANG=_BELGIUM.US7ASCII。
NLS_LANG=language_territory.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,即不可靠。
Unknown
在此处找到更多非常有用的信息:NLS_LANG常见问题解答
请注意,某些技术没有利用NLS_LANG,那里的设置没有任何效果,例如:
ODP.NET托管驱动程序不NLS_LANG敏感。它仅对.NET语言环境敏感。(请参阅.NET开发人员指南的数据提供程序)
OraOLEDB(来自Oracle)始终使用UTF-16(请参阅OraOLEDB提供程序特定的功能)
基于Java的JDBC(例如SQL Developer)具有自己的处理字符集的方法(有关更多详细信息,请参见《数据库JDBC开发人员指南-全球化支持》)。
.sql
例: UTF-8
UTF-8
如果您在Windows终端上工作(即使用SQL * plus),则可以chcp在Unix / Linux 上使用command询问代码页,等效于locale charmapor echo $LANG。您可以从此处获取所有Windows代码页标识符的列表:代码页标识符。请注意,关于UTF-8(chcp 65001)存在一些问题,请参见此讨论。
chcp
locale charmap
echo $LANG
chcp 65001
如果使用.sql文件和TOAD或SQL- Developer之类的编辑器,则必须检查保存选项。通常你可以选择喜欢的值UTF-8,ANSI,ISO-8859-1等 ANSI指Windows的ANSI代码,通常CP1252,你可以在你注册处查询HKLM\SYSTEM\ControlSet001\Control\Nls\CodePage\ACP或浏览:国家语言支持(NLS)API参考
ANSI
ISO-8859-1
CP1252
HKLM\SYSTEM\ControlSet001\Control\Nls\CodePage\ACP
[Microsoft删除了此参考,将其形成为Web归档 国家语言支持(NLS)API参考 ]
最重要的一点是匹配NLS_LANG和终端的“真实”字符集。应用程序或.sql文件编码
一些常见的对是:
CP850-> WE8PC850
WE8PC850
CP1252或ANSI(对于“西方” PC)-> WE8MSWIN1252
WE8MSWIN1252
ISO-8859-1-> WE8ISO8859P1
WE8ISO8859P1
ISO-8859-15-> WE8ISO8859P15
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';
有些技术使您的生活更轻松,例如Oracle的ODP.NET(非驱动程序驱动程序)或ODBC驱动程序会自动从NLS_LANG值继承字符集,因此以上条件始终为真。
是否需要将客户端NLS_LANG值设置为等于数据库NLS_CHARACTERSET值?
不,不一定!例如,如果您具有 数据库 字符集NLS_CHARACTERSET=AL32UTF8和 客户端 字符集,NLS_LANG=.ZHS32GB18030则它们可以正常工作(前提是您的客户端确实使用GB18030),尽管这些字符集完全不同。GB18030是中文常用的字符集,就像UTF-8它支持所有Unicode字符一样。
NLS_CHARACTERSET=AL32UTF8
NLS_LANG=.ZHS32GB18030
例如NLS_CHARACTERSET=AL32UTF8,如果您有,NLS_LANG=.WE8ISO8859P1它也将起作用(同样,只要您的客户确实使用ISO-8859-P1)。但是,数据库可能会存储您的客户端无法显示的字符,而是客户端将显示一个占位符(例如¿)。
¿
无论如何,具有匹配的NLS_LANG和NLS_CHARACTERSET值(如果适用)是有益的。如果它们相等,则可以确保可以显示可能存储在数据库中的任何字符,并且您在终端中输入或在.sql文件中写入的任何字符也可以存储在数据库中,并且不会被占位符代替。
您可以阅读很多次建议,例如“ NLS_LANG字符集必须与您的数据库字符集相同”(也在SO上找到)。这根本不是事实,而是一个流行的神话!
这是证明:
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:
cmd.exe
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为单个语句设置两个不同的值-这是不可能的。