如何使用python cx_oracle从US7aScii oracle读取国家字符(> 127)



我在使用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

插入Unicode

如果要在客户端应用程序中获得不变的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应该简单地将其设置为客户端的默认编码。查看更多可靠的选择:

  1. 扩展数据库的字符集,以在Varchar列中允许这些字符。
  2. 将此特定列升级到NVARCHAR。您也许可以为此列使用新名称,并创建一个带有旧名称的VARCHAR计算列,用于读取。
  3. 将数据库保持原样,但输入数据时检查数据库,并用可接受的ASCII等效替换所有非ASCII字符。

哪种选项最佳取决于非ASCII字符的常见程度。如果还有更多具有相同问题的桌子,我会建议选项1。如果这是唯一的表格。:选项3。

数据库的任务之一是在毕竟保留数据的质量,如果您在强行将非法字符插入列中作弊,它将无法正确完成其工作,每个新客户或升级或导出都会出现具有有趣的新不确定行为。


编辑:请参阅NLS_LANG FAQ中类似设置的示例的Oracle评论(我的重点):

在带有US7aScii字符的UNIX系统上创建数据库 放。连接到数据库的Windows客户端可与 WE8MSWIN1252字符集(区域设置 ->西欧/ACP 1252)和DBA,使用Unix Shell(Roman8)在 数据库。 nls_lang设置为American_america.us7ascii 客户端和服务器。

注意:

这是解释字符集转换的不正确设置,不要 在您的环境中使用它!

最新更新