我在使用python 3.3 cx_oracle 5.1.2和" nls_lang"环境变量的"英语_united Kingdom.us7ascii" oracle 11数据库中显示国家字符有问题。DB表列类型是" Varchar2(2000字节)"
如何显示Python中Oracle Us7ascii的字符串"£aàáâäåæçè"?这将是某种黑客。汉克(Hank)在其他所有脚本语言上工作,php,pl/sql和 python 2.7 ,但它在python 3.3中不起作用。
在Oracle 11数据库中我创建了Security_hints.answer ="£aàáâäåæçè"。答案列类型为" Varchar2(2000字节)"。
现在使用cx_oracle和默认的nls_lang时,我得到了"
使用nls_lang ="英语_united kingdom.us7ascii"我得到
"UnicodeDecodeError: 'ascii' codec can't decode byte 0xa3 in position 0: ordinal not in range(128)"
update1 我取得了一些进步。切换到Python 2.7和CX_oracle 5.1.2 for Python 2.7问题消失了(我从DB中获得了所有> 127个字符)。在Python中,2个字符串表示为字节,在Python中,3 字符串表示为Unicode。对于Python 3.3,我仍然需要最好的解决方案。
update2 解决该问题的一种可能解决方案是使用RAWTOHEX(utl_raw.cast_to_raw请参见下面的代码。
cursor.execute("select rawtohex(utl_raw.cast_to_raw(ANSWER)) from security_hints where userid = '...'")
for rawValue in cursor:
print (''.join(['%c' % iterating_var for iterating_var in binascii.unhexlify(rawValue[0])]))
我脚本的源代码在下面或github和github sollution
def test_nls(nls_lang=None):
print (">>> run test_nls for %s" %(nls_lang))
if nls_lang:
os.environ["NLS_LANG"] = nls_lang
os.environ["ORA_NCHAR_LITERAL_REPLACE"] = "TRUE"
connection = get_connection()
cursor = connection.cursor()
print("version=%snencoding=%stnencoding=%stmaxBytesPerCharacter=%s" %(connection.version, connection.encoding,
connection.nencoding, connection.maxBytesPerCharacter))
cursor.execute("SELECT USERENV ('language') FROM DUAL")
for result in cursor:
print("%s" %(result))
cursor.execute("select ANSWER from SECURITY_HINTS where USERID = '...'")
for rawValue in cursor:
print("query returned [%s]" % (rawValue))
answer = rawValue[0]
str = ""
for iterating_var in answer:
str = ("%s [%d]" % (str, ord(iterating_var)))
print ("str %s" %(str))
cursor.close()
connection.close()
if __name__ == '__main__':
test_nls()
test_nls(".AL32UTF8")
test_nls("ENGLISH_UNITED KINGDOM.US7ASCII")
请参阅下面的日志输出。
run test_nls for None
version=11.1.0.7.0
encoding=WINDOWS-1252 nencoding=WINDOWS-1252 maxBytesPerCharacter=1
ENGLISH_UNITED KINGDOM.US7ASCII
query returned [¿a¿¿¿¿¿¿¿¿¿]
str [191] [97] [191] [191] [191] [191] [191] [191] [191] [191] [191
run test_nls for .AL32UTF8
version=11.1.0.7.0
encoding=UTF-8 nencoding=UTF-8 maxBytesPerCharacter=4
AMERICAN_AMERICA.US7ASCII
query returned [�a���������]
str [65533] [97] [65533] [65533] [65533] [65533] [65533] [65533] [65533] [65533] [65533]
run test_nls for ENGLISH_UNITED KINGDOM.US7ASCII
version=11.1.0.7.0
encoding=US-ASCII nencoding=US-ASCII maxBytesPerCharacter=1
ENGLISH_UNITED KINGDOM.US7ASCII
Traceback (most recent call last):
File "C:/dev/tmp/Python_US7ASCII_cx_Oracle/showUS7ASCII.py", line 71, in <module>
test_nls("ENGLISH_UNITED KINGDOM.US7ASCII")
File "C:/dev/tmp/Python_US7ASCII_cx_Oracle/showUS7ASCII.py", line 55, in test_nls
for rawValue in cursor:
UnicodeDecodeError: 'ascii' codec can't decode byte 0xa3 in position 0: ordinal not in range(128)
我正在尝试将其显示在Django网页中。但是每个字符都以代码为191或65533的字符。
我看着为Oracle选择NLS_LANG和使用Python的正确编码从Oracle导入
无法使用CX-Oracle
如果要在客户端应用程序中获得不变的ASCII字符串,最好的方法是以二进制模式将其从DB转移。因此,必须借助UTL_RAW
软件包和标准rawtohex
功能在服务器端进行第一次转换。
您在cursor.execute
中的选择看起来像:
select rawtohex(utl_raw.cast_to_raw(ANSWER)) from SECURITY_HINTS where USERID = '...'
在客户端上,您得到了一串十六进制字符,可以在binascii.unhexlify
函数的帮助下转换为字符串表示:
for rawValue in cursor:
print("query returned [%s]" % (binascii.unhexlify(rawValue)))
P.S。我不知道Python
语言,所以最后的语句可能不正确。
我认为您不应该恢复到如此邪恶的骗局。NLS_LANG应该简单地将其设置为客户端的默认编码。查看更多可靠的选择:
- 扩展数据库的字符集,以在Varchar列中允许这些字符。
- 将此特定列升级到NVARCHAR。您也许可以为此列使用新名称,并创建一个带有旧名称的VARCHAR计算列,用于读取。
- 将数据库保持原样,但输入数据时检查数据库,并用可接受的ASCII等效替换所有非ASCII字符。
哪种选项最佳取决于非ASCII字符的常见程度。如果还有更多具有相同问题的桌子,我会建议选项1。如果这是唯一的表格。:选项3。
数据库的任务之一是在毕竟保留数据的质量,如果您在强行将非法字符插入列中作弊,它将无法正确完成其工作,每个新客户或升级或导出都会出现具有有趣的新不确定行为。
编辑:请参阅NLS_LANG FAQ中类似设置的示例的Oracle评论(我的重点):
在带有US7aScii字符的UNIX系统上创建数据库 放。连接到数据库的Windows客户端可与 WE8MSWIN1252字符集(区域设置 ->西欧/ACP 1252)和DBA,使用Unix Shell(Roman8)在 数据库。 nls_lang设置为American_america.us7ascii 客户端和服务器。
注意:
这是解释字符集转换的不正确设置,不要 在您的环境中使用它!