65.9K
CodeProject 正在变化。 阅读更多。
Home

为 Oracle 设置 NLS_LANG 值

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2016 年 1 月 1 日

CPOL

6分钟阅读

viewsIcon

107388

downloadIcon

229

如何设置 Oracle 的 NLS_LANG 值以支持您的语言/应用程序中所需的所有特殊字符。

引言

很多时候,当您有一个 Oracle 应用程序并且必须支持特殊字符,如 `ö,ä,ü,é,è` 或货币符号(例如 `€`)时,您会遇到显示不正确的问题。通常,此问题是由于 `NLS_LANG` 值设置不当引起的。

`NLS_LANG` 设置客户端应用程序和数据库服务器使用的语言和区域。它还设置客户端的字符集,即客户端程序输入或显示的数据的字符集。

数据库字符集

创建 Oracle 数据库时,DBA 必须指定 `CHARACTER SET` 和 `NATIONAL CHARACTER SET`。

如今,默认值是

  • `CHARACTER SET` 为 `AL32UTF8`,
  • `NATIONAL CHARACTER SET` 为 `AL16UTF16`

这些 **数据库** 字符集定义了哪些字符(以何种格式)可以存储在 `CHAR`、`CLOB`、`VARCHAR2` 中,以及 `NCHAR`、`NCLOB`、`NVARCHAR2` 列中。在现有数据库上,您可以使用以下命令查询这些值

SELECT * 
FROM NLS_DATABASE_PARAMETERS 
WHERE PARAMETER LIKE '%CHARACTERSET';

PARAMETER 	              VALUE
==========================================
NLS_CHARACTERSET 	      AL32UTF8
NLS_NCHAR_CHARACTERSET 	  AL16UTF16

2 row(s) selected.

数据库字符集不定义字符是否以及如何显示在您的客户端应用程序中!

NLS_LANG 的一些事实

`NLS_LANG` 定义的格式是 `NLS_LANG = LANGUAGE_TERRITORY.CHARSET`

`NLS_LANG` 定义的所有组件都是可选的;任何未指定的项都使用其默认值。如果指定了区域或字符集,则必须包含前面的分隔符[区域的下划线 (`_`),字符集的句点 (`.`)。否则,该值将被解析为语言名称。

以下定义均有效

  • NLS_LANG=.WE8ISO8859P1
  • NLS_LANG=_GERMANY
  • NLS_LANG=AMERICAN
  • NLS_LANG=ITALIAN_.WE8MSWIN1252
  • NLS_LANG=_BELGIUM.US7ASCII

如果未提供 `NLS_LANG` 值,则 Oracle 默认为 `AMERICAN_AMERICA.US7ASCII`。

`LANGUAGE` 和 `TERRITORY` 设置许多其他 NLS 参数的默认值,请参阅 此表 以获取概述。`CHARSET` 用于让 Oracle 知道您在客户端使用的是什么字符集,以便 Oracle 可以进行适当的转换。设置 `NLS_LANG` 的 `LANGUAGE` 和 `TERRITORY` 参数与在数据库中存储字符的能力无关。这里,您可以看到可用 语言、 区域 和 字符集 的列表。

您可以通过以下方式更改会话的语言和区域

ALTER SESSION SET NLS_LANGUAGE = '...';
respective  
ALTER SESSION SET NLS_TERRITORY = '...';

但是,您不能使用任何 SQL 命令更改客户端 `charset`,它只能由 `NLS_LANG` 值设置。

一些设置可以在 SQL 函数中显式设置,例如

SELECT TO_CHAR(SYSDATE, 'DD Month', 'NLS_DATE_LANGUAGE = FRENCH') 
FROM dual;

其他不能,例如

SELECT TRUNC(SYSDATE, 'DY', 'NLS_TERRITORY = AMERICA') AS FIRST_DAY_OF_WEEK 
FROM dual;

不起作用。

您无法通过任何字典或动态性能视图或任何其他 SQL 命令查询客户端 `charset`。此外,字典视图 `NLS_SESSION_PARAMETERS` 显示的是 **数据库** 字符集,而不是 **客户端** 字符集!

您可以运行查询

SELECT CLIENT_CHARSET 
FROM V$SESSION_CONNECT_INFO;

然而,这些值似乎不可靠。有时,它显示 `NULL` 或“未知”。

NLS_LANG 的定义

`NLS_LANG` 可以通过 `Environment` 变量设置(例如 `SET NLS_LANG=AMERICAN_AMERICA.WE8MSWIN1252`),也可以通过注册表在 `HKEY_LOCAL_MACHINE\Software\Oracle\KEY_{ORACLE_HOME_NAME}\NLS_LANG`(对于 64 位 Windows 上的 32 位 Oracle 客户端,则为 `HKEY_LOCAL_MACHINE\Software\Wow6432Node\Oracle\KEY_{ORACLE_HOME_NAME}\NLS_LANG`)设置。`Environment` 变量的优先级高于注册表条目。

您可以使用以下命令查询现有值

Windows:

reg query HKEY_LOCAL_MACHINE\Software\Oracle\KEY_{ORACLE_HOME_NAME} /f NLS_LANG
reg query HKEY_LOCAL_MACHINE\Software\Wow6432Node\Oracle\KEY_{ORACLE_HOME_NAME} /f NLS_LANG
set NLS_LANG

Unix/Linux:

echo $NLS_LANG

NLS_LANG 的正确值

通常,`LANGUAGE` 和 `TERRITORY` 的值是显而易见的,并且在应用程序中不太关键。最有趣的是 `CHARACTER SET` 值。许多时候,您在论坛中(有时甚至在官方文档中)读到:“客户端 `NLS_LANG` 字符集必须与数据库字符集的值相同”——**这根本不是真的!** 考虑数据库有两个字符集,即“正常”字符集和国家字符集。在客户端,您只有一个值,所以实际上它们不可能相等。某些字符集 仅在客户端可用,这也证明了我的说法。

`NLS_LANG` 字符集有两个要求

  1. NLS_LANG 字符集必须支持您希望在应用程序中使用的字符。
  2. NLS_LANG 字符集必须与应用程序的字符集(或编码)匹配。

某些应用程序/驱动程序在启动时加载 `NLS_LANG` 定义,并从 `NLS_LANG` 值派生其字符集。在这种情况下,它变得更容易,并且只适用第一个要求。

SQL*Plus 中的 NLS_LANG

SQL*Plus 继承了启动它的终端会话的字符集。在 Windows 上,您可以使用 `chcp` 获取当前字符集(此处称为“`Codepage`”),Linux/Unix 的等效命令是 `locale charmap` 或 `echo $LANG`。因此,一个合适的设置例如是

C:\>chcp
Active Codepage: 850.

C:\>set NLS_LANG=.WE8PC850

C:\>sqlplus ...

使用 `chcp`,您还可以更改您的代码页,例如,`chcp 1252`。您可以使用小的 批处理文件 永久更改命令行窗口的 `代码页`

@ECHO off

SET ROOT_KEY="HKEY_CURRENT_USER"


FOR /f "skip=2 tokens=3" %%i in _
('reg query HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage /v OEMCP') do set OEMCP=%%i

ECHO.
ECHO ...............................................
ECHO Select Codepage 
ECHO ...............................................
ECHO.
ECHO 1 - CP1252
ECHO 2 - UTF-8
ECHO 3 - CP850
ECHO 4 - ISO-8859-1
ECHO 5 - ISO-8859-15
ECHO 6 - US-ASCII
ECHO.
ECHO 9 - Reset to System Default (CP%OEMCP%)
ECHO 0 - EXIT
ECHO.


SET /P  CP="Select a Codepage: "

if %CP%==1 (
    echo Set default Codepage to CP1252
    reg add "%ROOT_KEY%\Software\Microsoft\Command Processor" /v Autorun /t REG_SZ /d "chcp 1252" /f
) else if %CP%==2 (
    echo Set default Codepage to UTF-8
    reg add "%ROOT_KEY%\Software\Microsoft\Command Processor" /v Autorun /t REG_SZ /d "chcp 65001" /f
) else if %CP%==3 (
    echo Set default Codepage to CP850
    reg add "%ROOT_KEY%\Software\Microsoft\Command Processor" /v Autorun /t REG_SZ /d "chcp 850" /f
) else if %CP%==4 (
    echo Set default Codepage to ISO-8859-1
    add "%ROOT_KEY%\Software\Microsoft\Command Processor" /v Autorun /t REG_SZ /d "chcp 28591" /f
) else if %CP%==5 (
    echo Set default Codepage to ISO-8859-15
    add "%ROOT_KEY%\Software\Microsoft\Command Processor" /v Autorun /t REG_SZ /d "chcp 28605" /f
) else if %CP%==5 (
    echo Set default Codepage to ASCII
    add "%ROOT_KEY%\Software\Microsoft\Command Processor" /v Autorun /t REG_SZ /d "chcp 20127" /f
) else if %CP%==9 (
    echo Reset Codepage to System Default
    reg delete "%ROOT_KEY%\Software\Microsoft\Command Processor" /v AutoRun /f
) else if %CP%==0 (
    echo Bye
) else (
    echo Invalid choice
    pause
)

请注意,这些设置仅适用于当前用户。如果您想为所有用户设置,请替换行

SET ROOT_KEY="HKEY_CURRENT_USER"

by

SET ROOT_KEY="HKEY_LOCAL_MACHINE"

使用代码页 UTF-8 ( `chcp 65001` ) 要小心,存在一个错误,请参阅 此讨论 。我不知道这是否已在较新的 Windows / SQL*Plus 版本中修复。

.sql 文件中的 NLS_LANG

当您在 SQL*Plus 中运行 sql 文件时,请检查编辑器的保存选项。通常,您可以选择 `ISO-8859-1`、`UTF-8`、`ANSI`、`CP1252` 等值作为编码。术语“ANSI”表示默认的 Windows 代码页。在西方 PC 上,这是 `CP1252`。

您可以使用以下命令查询默认的 Windows 代码页

C:\>reg query HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage /v ACP

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage
    ACP    REG_SZ    1252

C:\>

或从 国家语言支持 (NLS) API 参考 中的任何区域设置中读取“ANSI 代码页”。

您必须根据文本编辑器的编码设置 `NLS_LANG` 的字符集。这里是可用 代码页 的列表。

.NET 应用程序中的 NLS_LANG

  • ODP.NET 管理驱动程序对 `NLS_LANG` 不敏感。
    它只对 .NET 区域敏感。(请参阅 .NET 开发人员指南数据提供程序
  • Oracle 的 ODBC、ODP.NET 和 OLE DB 驱动程序在加载时读取 `NLS_LANG` 值并继承定义,确保了任何客户端/数据库字符设置的正确字符转换。
  • Microsoft 的 ODBC、ADO.NET 和 OLE DB 提供程序在加载时也会读取 `NLS_LANG` 值。但是,它们有一些限制,尤其是在 Unicode 方面。

如果不知道,如何确定我的应用程序的字符集?

首先,您应该查阅应用程序和所用驱动程序的文档。

如果您仍然不清楚所使用的字符集,我开发了以下方法。

  • 将您的 `NLS_LANG` 设置为 `NLS_LANG=.AL32UTF8`
  • 使用 SQL*Plus 连接到支持 UTF-8 的数据库,即字符集为 `AL32UTF8` 的数据库
    当客户端字符集等于数据库字符集时,不会发生字符转换,所有字节都“按原样”传输。
  • 在您的应用程序中,运行一个带有特殊字符的查询,如下所示
select dump('€') from dual;

DUMP('€')
-----------------
Typ=96 Len=1: 164

然后您可以使用 C# 编写的函数来估算字符集,如下所示

  byte[] o = new byte[] { 164 };
   foreach ( var enc in Encoding.GetEncodings() ) {
      var convertedString = enc.GetEncoding().GetBytes("€");
      if ( convertedString.SequenceEqual(o) )
         Console.WriteLine(String.Format("{0}\t{1}\t{2}", enc.CodePage, enc.Name, enc.DisplayName));
   }

该函数将打印出应用程序可能使用的字符集列表。有时,打印输出会明显给出所使用的字符集,有时您必须使用更多其他特殊字符。有些代码页仅在一个字符上有所不同!

如果我的字符仍未正确显示,该怎么办?

  • 仔细检查您的应用程序和所用驱动程序的文档。也许它们很旧,尚不支持 Unicode。更新到最新版本的驱动程序。
  • 检查您的字体是否支持所需的字符。例如,您可以使用此页面 Unicode 字符的字体支持 来验证所使用的字体。
  • 检查数据库的 **真实** 内容。运行诸如 `SELECT DUMP(THE_COLUMN, 1016) FROM ...` 之类的查询,以查看表中的字节。也许数据是由客户端以错误的 `NLS_LANG` 定义插入的。别担心,通常您只需检查几个字符/字节即可获得结果。
© . All rights reserved.