Linux 環境中 LANG 與 NLS_LANG 同樣包含「語言_地區.字元集」三要素,但作用層級、生效範圍、優先級完全不同。混淆兩者會導致系統介面語言正常但 Oracle 客戶端亂碼、或反之。本文深度對比兩者機制、設定方法、驗證方式與典型問題場景。
| 維度 | LANG (系統語系) | NLS_LANG (Oracle 客戶端語系) |
|---|---|---|
| 作用範圍 | 整個 Linux 使用者空間 (Shell、GUI、C/Python/Go 程式等) | 僅 Oracle 客戶端工具 (sqlplus、exp/imp、rman、ODP.NET、JDBC Thin 等) |
| 生效機制 | glibc locale 子系統、gettext 國際化框架 | Oracle NLS (National Language Support) 運行時庫 |
| 格式 | 語言_地區.編碼 (如 zh_CN.UTF-8) | 語言_地區.字元集 (如 AMERICAN_AMERICA.ZHS16GBK) |
| 字元集命名 | IANA/glibc 標準名 (UTF-8, GBK, GB2312) | Oracle 專有名 (ZHS16GBK, AL32UTF8, ZHT16BIG5) |
| 優先級 | 受 LC_ALL、LC_* 覆蓋 | Oracle 客戶端唯一依據,不受系統 locale 影響 |
| 設定位置 | /etc/locale.conf、~/.bashrc、localectl | Shell 啟動腳本、.bash_profile、Oracle 用戶 .profile |
locale
# LANG=zh_CN.UTF-8
# LC_CTYPE="zh_CN.UTF-8" # 字元編碼/分類 (核心)
# LC_NUMERIC="zh_CN.UTF-8" # 數字格式 (小數點、分組)
# LC_TIME="zh_CN.UTF-8" # 日期時間格式
# LC_COLLATE="zh_CN.UTF-8" # 排序規則
# LC_MONETARY="zh_CN.UTF-8" # 貨幣格式
# LC_MESSAGES="zh_CN.UTF-8" # 系統訊息語言
# LC_PAPER="zh_CN.UTF-8" # 紙張尺寸
# LC_NAME="zh_CN.UTF-8" # 姓名格式
# LC_ADDRESS="zh_CN.UTF-8" # 地址格式
# LC_TELEPHONE="zh_CN.UTF-8" # 電話格式
# LC_MEASUREMENT="zh_CN.UTF-8" # 度量衡
# LC_IDENTIFICATION="zh_CN.UTF-8"
# LC_ALL= # 未設定,否則覆蓋以上所有
關鍵: LC_CTYPE 決定字元編碼轉換,LC_MESSAGES 決定介面語言。若只想要中文介面但保持英文排序/數字格式:
export LANG=zh_CN.UTF-8
export LC_COLLATE=C
export LC_NUMERIC=C
NLS_LANG=AMERICAN_AMERICA.ZHS16GBK
# 語言 : AMERICAN (錯誤訊息、月份名稱等)
# 地區 : AMERICA (日期格式、數字格式、貨幣符號)
# 字元集 : ZHS16GBK (簡體中文 GBK 編碼,16-bit)
| Oracle 字元集 | 對應編碼 | 說明 |
|---|---|---|
AL32UTF8 | UTF-8 | Unicode 完整支援,推薦新資料庫 |
ZHS16GBK | GBK (CP936) | 簡體中文,含生僻字,Windows 相容 |
ZHS16CGB231280 | GB2312 | 舊國標,字元較少 |
ZHT16BIG5 | Big5 | 繁體中文 |
WE8ISO8859P1 | ISO-8859-1 | 西歐語系,單位元組 |
UTF8 | UTF-8 (舊版) | Oracle 8i/9i 版本,有缺陷,勿用 |
SELECT * FROM DATABASE_PROPERTIES
WHERE PROPERTY_NAME IN ('NLS_LANGUAGE','NLS_TERRITORY','NLS_CHARACTERSET');
# NLS_LANGUAGE: AMERICAN
# NLS_TERRITORY: AMERICA
# NLS_CHARACTERSET: AL32UTF8
現象: LANG=zh_CN.UTF-8,但 sqlplus 查詢中文資料顯示亂碼/問號。
原因: NLS_LANG 未設或設錯 (如 AMERICAN_AMERICA.WE8ISO8859P1),Oracle 客戶端以單位元組編碼解讀 UTF-8 資料。
解決:
# 針對 AL32UTF8 資料庫
export NLS_LANG=AMERICAN_AMERICA.AL32UTF8
# 或簡體中文環境
export NLS_LANG=SIMPLIFIED CHINESE_CHINA.AL32UTF8
# 永久生效:加入 oracle 用戶 ~/.bash_profile
原因: 導出時 NLS_LANG 與資料庫字元集不匹配,導致字元集轉換錯誤。
鐵律: 導出/導入時 NLS_LANG 必須與資料庫 NLS_CHARACTERSET 完全一致。
# 資料庫為 ZHS16GBK
export NLS_LANG=AMERICAN_AMERICA.ZHS16GBK
exp userid=system/pass file=dump.dmp full=y
# 資料庫為 AL32UTF8
export NLS_LANG=AMERICAN_AMERICA.AL32UTF8
imp userid=system/pass file=dump.dmp full=y
JDBC Thin 為純 Java 實現,使用 JVM file.encoding (受 LANG/LC_CTYPE 影響) 而非 NLS_LANG。需確保:
-Dfile.encoding=UTF-8
# 連線屬性
oracle.jdbc.defaultNChar=true # 正確處理 NCHAR/NVARCHAR2
# ~/.bash_profile (Oracle 專用用戶 oracle)
# === 系統語系 ===
export LANG=en_US.UTF-8 # 系統介面保持英文,利於排錯
export LC_CTYPE=en_US.UTF-8 # 編碼 UTF-8
export LC_MESSAGES=en_US.UTF-8
# === Oracle 客戶端語系 ===
# 根據目標資料庫字元集設定,範例為 AL32UTF8
export NLS_LANG=AMERICAN_AMERICA.AL32UTF8
# === 其他 Oracle 環境 ===
export ORACLE_HOME=/u01/app/oracle/product/19c/dbhome_1
export PATH=$ORACLE_HOME/bin:$PATH
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:$LD_LIBRARY_PATH
| 檢查項 | 命令 | 預期 |
|---|---|---|
| 系統 Locale | locale | LANG/LC_CTYPE 為 UTF-8 |
| NLS_LANG 環境變數 | echo $NLS_LANG | 與目標 DB 字元集一致 |
| sqlplus 連線測試 | sqlplus user/pass@db | 中文查詢/插入正常 |
| 資料庫字元集 | SQL 查詢 NLS_CHARACTERSET | 與 NLS_LANG 字元集部分一致 |
明確區分 LANG (系統級) 與 NLS_LANG (Oracle 客戶端級),並根據資料庫字元集精確設定 NLS_LANG,是避免 Oracle 中文亂碼、導出導入損壞的核心鑰匙。