我在使用Python 3.3 cx_Oracle 5.1.2和“NLS_LANG”环境变量显示来自“ENGLISH_UNITED KINGDOM.US7ASCII”Oracle 11数据库的国家字符时遇到问题。 Db表列类型为“VARCHAR2(2000 BYTE)”
如何在Python中用Oracle US7ASCII显示字符串“£aÀÁÂÃÄÅÆÇÈ”?这将是某种黑客攻击。 hank适用于所有其他脚本语言Perl,PHP,PL / SQL和 Python 2.7 ,但它在Python 3.3中不起作用。
在Oracle 11数据库中,我创建了SECURITY_HINTS.ANSWER =“£aÀÁÂÃÄÅÆÇÈ”。 ANSWER列类型为“VARCHAR2(2000 BYTE)”。
现在使用cx_Oracle和默认NLS_LANG时,我得到“¿a¿¿¿¿¿¿¿¿¿”
当使用NLS_LANG =“ENGLISH_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=%s\nencoding=%s\tnencoding=%s\tmaxBytesPerCharacter=%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的字符。
我看着 choosing NLS_LANG for Oracle和 Importing from Oracle using the correct encoding with Python答案 0 :(得分:2)
如果要在客户端应用程序中获取未更改的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
语言,所以最后的陈述可能不正确。
答案 1 :(得分:1)
我认为你不应该回归这种邪恶的诡计。 NLS_LANG应该只是设置为客户端的默认编码。看看更可靠的选项:
哪个选项最好取决于非ASCII字符的常见程度。如果有更多的表有相同的问题,我建议选项1.如果这是唯一的表,选项2.如果整个表中只有几个非ASCII字符,并且他们的损失不是那么大的交易:选项3。
数据库的任务之一就是保持数据质量,如果你在强行插入非法字符时作弊,它就无法正常工作,每个新客户端或升级或导出都会到来有趣的新未定义行为。
编辑:请参阅Oracle对NLS_LANG faq中类似设置示例的评论(我的重点):
在具有US7ASCII字符的UNIX系统上创建数据库 组。 连接到数据库的Windows客户端使用 WE8MSWIN1252字符集(区域设置 - &gt;西欧/ ACP 1252)和DBA一样,使用UNIX shell(ROMAN8)来工作 数据库。 NLS_LANG设置为american_america.US7ASCII 客户和服务器。
注意:强>
这是一个解释字符集转换的INCORRECT设置,不是 在您的环境中使用它!