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 ответ
Проблемы с набором символов довольно распространены, позвольте мне сделать несколько общих замечаний.
В принципе, вы должны рассмотреть четыре различных набора символов.
1 и 2: NLS_CHARACTERSET
а также NLS_NCHAR_CHARACTERSET
Пример: AL32UTF8
Они определены только в вашей базе данных, вы можете опросить их с
SELECT *
FROM V$NLS_PARAMETERS
WHERE PARAMETER IN ('NLS_CHARACTERSET', 'NLS_NCHAR_CHARACTERSET');
Эти параметры определяют, какие символы (в каком формате) могут храниться в вашей базе данных - ни больше, ни меньше. Это требует определенных усилий (см. Миграция набора символов и / или Oracle Database Migration Assistant для Unicode), если вам нужно изменить его в существующей базе данных.
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 бит), соотв. 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
, Часть { charset } NLS_LANG не отображается ни в одной системной таблице или представлении. Все компоненты определения NLS_LANG являются необязательными, поэтому допустимы следующие определения: NLS_LANG=.WE8ISO8859P1
, NLS_LANG=_GERMANY
, NLS_LANG=AMERICAN
, NLS_LANG=ITALIAN_.WE8MSWIN1252
, NLS_LANG=_BELGIUM.US7ASCII
,
Как указано выше, часть {charset} NLS_LANG
недоступен в базе данных ни в одной системной таблице / представлении или любой функции. Строго говоря, это правда, однако вы можете выполнить этот запрос:
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 FAQ
4: "реальный" набор символов вашего терминала, вашего приложения или кодировки .sql
файлы
Пример: UTF-8
Если вы работаете в терминале Windows (т.е. с SQL*plus), вы можете запросить кодовую страницу с помощью команды chcp
в Unix/Linux это эквивалентно locale charmap
или же echo $LANG
, Вы можете получить список всех идентификаторов кодовых страниц Windows здесь: Идентификаторы кодовых страниц. Обратите внимание, для UTF-8 (chcp 65001
) Есть некоторые проблемы, смотрите это обсуждение.
Если вы работаете с .sql
файлы и редактор, такие как TOAD или SQL-Developer, вы должны проверить параметры сохранения. Обычно вы можете выбрать такие значения, как UTF-8
, ANSI
, ISO-8859-1
, так далее. ANSI
означает кодовую страницу Windows ANSI, обычно CP1252
, вы можете проверить в своем реестре на HKLM\SYSTEM\ControlSet001\Control\Nls\CodePage\ACP
или здесь: Справочник по поддержке национальных языков (NLS)
[Microsoft удалила эту ссылку, примите форму веб-архива Справочник по поддержке национальных языков (NLS) ]
Примечание: в зависимости от вашей технологии вам не нужно особо заботиться об этой теме, вот несколько примеров:
ODP.NET (неуправляемый драйвер) автоматически наследует набор символов из
NLS_LANG
значение.Управляемый драйвер ODP.NET не является
NLS_LANG
чувствительны. Он чувствителен только к локали.NET. (см. " Поставщик данных для.NET Developer's Guide")OraOLEDB (от Oracle) всегда использует UTF-16 (см. Особенности поставщика OraOLEDB)
JDBC на основе Java (например, SQL Developer) имеет свои собственные методы для работы с наборами символов (см . Руководство разработчика базы данных JDBC - Поддержка глобализации для получения дополнительной информации)
Как установить все эти значения?
Наиболее важным моментом является соответствие NLS_LANG
и ваш "реальный" набор символов вашего терминала, соответственно. приложение или кодировка вашего .sql
файлы
Некоторые общие пары:
CP850 ->
WE8PC850
CP1252 или ANSI (в случае "западного" ПК) ->
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=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 соответственно:
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
для одного заявления - что невозможно.