OdbcConnection возвращает китайские символы как «?»

У меня есть firebase database 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_CHARACTERSET и NLS_NCHAR_CHARACTERSET

Пример: AL32UTF8

Они определяются только в вашей базе данных, вы можете допросить их с помощью

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

Эти настройки определяют, какие символы (в каком формате) могут быть сохранены в вашей базе данных – не более, не меньше. Это требует некоторых усилий (см. Миграция набора символов и / или помощник по миграции базы данных Oracle для 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 кодовую страницу с помощью команды 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 или здесь: Справочник по API поддержки национальных языков (NLS)

[Microsoft удалила эту ссылку, получив ее в форме веб-архива. Справочник по API поддержки национальных языков (NLS) ].

Примечание. В зависимости от вашей технологии вам не нужно так заботиться об этой теме, некоторые примеры:

  • ODP.NET (unmanged driver) автоматически наследует набор символов из значения NLS_LANG .

  • Управляемый драйвер NLS_LANG не чувствителен к NLS_LANG . Он чувствителен только к языку .NET. (см. Руководство для поставщика данных для .NET Developer Guide )

  • OraOLEDB (от Oracle) всегда использует UTF-16 (см. Спецификацию поставщика OraOLEDB )

  • Java JDBC (например, разработчик SQL) имеет свои собственные методы работы с наборами символов (см . Руководство для разработчиков баз данных 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). Тем не менее, firebase database может хранить символы, которые ваш клиент не может отображать, вместо этого клиент отобразит заполнитель (например, ¿ ).

Во всяком случае, полезно иметь соответствующие значения 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 для одного оператора, что невозможно.

  • Получить идентификатор последней вставленной записи в oracle db
  • Обработка ExecuteScalar (), когда результаты не возвращаются
  • Ошибка вывода скрипта SQL
  • Удаление повторяющихся строк из таблицы в Oracle
  • C #: эквивалентность типа данных Oracle с OracleDbType
  • Как передать List из java в Oracle Procedure?
  • PL / SQL распечатать курсор ref, возвращенный хранимой процедурой
  • if (выберите count (столбец) из таблицы)> 0, тогда
  • Oracle PL / SQL - повышение пользовательского исключения с помощью пользовательского SQLERRM
  • Является ли java.sql.Timestamp конкретным часовым поясом?
  • Как отправить электронную почту с помощью Oracle 10 g Forms
  • Interesting Posts
    Давайте будем гением компьютера.