py cx_oracle 中文乱码问题分析
如何检测编码
操作系统默认编码(文件内容的默认解码)
# file: get_encoding.py
import sys
import localeprint("locale.getpreferredencoding():", locale.getpreferredencoding())
print("sys.getfilesystemencoding():", sys.getfilesystemencoding())
print("sys.getdefaultencoding():", sys.getdefaultencoding())
locale.getpreferredencoding():文本模式open()未显式指定encoding时的默认编码(在 Windows 中文环境常为cp936=GBK)。sys.getfilesystemencoding():文件名的编码。sys.getdefaultencoding():Python 内部默认(Py3 通常为utf-8),不用于文件 I/O 默认。
Oracle 数据库字符集
-- 查询数据库字符集
SELECT PARAMETER, VALUE
FROM NLS_DATABASE_PARAMETERS
WHERE PARAMETER IN ('NLS_CHARACTERSET', 'NLS_NCHAR_CHARACTERSET');-- 查询会话级设置
SELECT * FROM NLS_SESSION_PARAMETERS
WHERE PARAMETER IN ('NLS_LANGUAGE','NLS_TERRITORY','NLS_CHARACTERSET');
文件自身编码(可选自动探测)
# 需要: pip install chardet
import chardetwith open('input.txt', 'rb') as f:raw = f.read()
info = chardet.detect(raw)
print(info) # {'encoding': 'GBK', 'confidence': 0.99, ...}
读取文件的方式选取与差异
推荐:统一用“显式指定编码”的文本读取,避免依赖系统默认。
# Py2 & Py3 兼容写法
from io import open
with open('file_name.txt', 'r', encoding='utf-8') as f:text = f.read() # 得到 Unicode 文本
常见方式对比
open('r')(Py3):返回str(Unicode),自动处理换行;建议总是指定encoding。open('rb'):返回bytes,保留原始字节与换行,不做解码。codecs.open('r', encoding=...)(Py2/3):按指定编码解码为文本;在 Py2 处理中更直观。io.open('r', encoding=...):Py2 里提供 Py3 行为;Py3 等价于内建open()。pathlib.Path.open('r', encoding=...)(Py3):面向对象路径方式,行为同open()。
换行差异示例(Windows)
import codecs, io
from pathlib import Pathwith open('test_file.txt', 'r') as f:print("open('r'):", repr(f.read())) # 'hello\nworld'
with open('test_file.txt', 'rb') as f:print("open('rb'):", f.read()) # b'hello\r\nworld'
with codecs.open('test_file.txt', 'r', encoding='utf-8') as f:print("codecs.open:", repr(f.read())) # 'hello\r\nworld'
with io.open('test_file.txt', 'r', encoding='utf-8') as f:print("io.open:", repr(f.read())) # 'hello\nworld'
with Path('test_file.txt').open('r', encoding='utf-8') as f:print("pathlib.open:", repr(f.read())) # 'hello\nworld'
编码转换与入库示例
统一思路:读取时按“文件实际编码”解码为 Unicode;入库时确保客户端与数据库编码一致,或让驱动进行正确转换。
Python 3(推荐)
from io import open
import cx_Oracle# 读取文件(假设文件是 GBK)
with open('input_gbk.txt', 'r', encoding='gbk') as f:text = f.read() # Unicode# 连接数据库(显式指定客户端编码,推荐与数据库一致,如 UTF-8)
conn = cx_Oracle.connect(user, pwd, dsn, encoding='UTF-8', nencoding='UTF-8')
cur = conn.cursor()
cur.execute("INSERT INTO t_logs(message) VALUES (:v)", v=text)
conn.commit()
cur.close(); conn.close()
Python 2
# -*- coding: utf-8 -*-
import codecs
import cx_Oracle# 读取文件(假设文件是 GBK)
with codecs.open('input_gbk.txt', 'r', encoding='gbk') as f:text_u = f.read() # unicode# 建议设置客户端编码,确保转换正确
import os
os.environ['NLS_LANG'] = 'AMERICAN_AMERICA.AL32UTF8' # 视数据库设置调整conn = cx_Oracle.connect(user, pwd, dsn)
cur = conn.cursor()
cur.execute(u"INSERT INTO t_logs(message) VALUES (:v)", v=text_u)
conn.commit()
cur.close(); conn.close()
可选:手动转换字节编码(不推荐)
# 将 Unicode 文本编码为数据库编码的 bytes(示例:UTF-8)
text_bytes = text_u.encode('utf-8')
# 某些驱动/接口可能需要字节流,但多数情况下直接传 Unicode 更安全
常见坑与建议
- Windows 中文环境默认
locale.getpreferredencoding()为cp936(GBK),容易误解码 UTF-8 文件导致UnicodeDecodeError。始终显式指定encoding。 - Py2 的
open('r')返回字节串(str),请使用codecs.open或io.open获取 Unicode。 open('rb')不做任何转换,适合处理二进制或自控解码场景。- 处理换行:Py3
open(..., newline=None)会统一换行;需要保留原始\r\n时使用二进制或codecs.open。 - 统一为 Unicode 流:读取→Unicode,传输→Unicode;让 cx_Oracle/客户端处理最终编码转换更稳妥。
- 环境变量
NLS_LANG会影响客户端到数据库的编码转换;与数据库字符集保持一致(如AL32UTF8)。
快速清单(Checklist)
- 确认文件实际编码(工具或约定:UTF-8/GBK)。
open(..., encoding=...)显式读取为 Unicode。- 查询数据库字符集并对齐客户端编码。
- Python 变量统一为 Unicode(Py2 用
unicode,Py3 用str)。 - 入库前如需转换,使用
.encode(target_charset),否则直接传 Unicode。