ホームページ  >  記事  >  バックエンド開発  >  Python - Cx_Oracle をクエリするときの UnicodeDecodeError の問題を解決する

Python - Cx_Oracle をクエリするときの UnicodeDecodeError の問題を解決する

高洛峰
高洛峰オリジナル
2017-02-16 11:13:591692ブラウズ

最近のプロジェクトでは、100 万件を超えるレコードを含むテーブルに対してクエリを実行し、その後、いくつかのデータ統計を実行する必要がありましたが、このプロセス中に、ほんの数個のデータがクエリされた後で UnicodeDecodeError が発生することがわかりました。
ここでは、クエリに sqlalchemy ライブラリを使用し、対応する操作を実行するために Cx_Oracle が内部で使用されます。使用される Python バージョンは 3.5.0、ホスト システムは Windows 2008 Server で、次のような操作が実行されます。

ここでは、まずデータベースへの接続を作成し、次に対応するクエリ操作を実行します。残念ながら、10 件未満のレコードをクエリした後に UnicodeDecodeError エラーが発生しました。

当初、これはデータベースのサーバーエンコーディングの問題だと思いました。そこで、create_engine 関数にエンコーディングパラメータを追加して、次のように変更しました。

from sqlalchemy import create_engine

engine = create_engine('oracle://demo:123456@192.168.1.202/TEST')
conn = engine.connect()
sql = 'select t.type from TS t'
result = conn.execute(sql)
for row in result:
    print(row['type'])
もう 1 つの利用可能な方法は、接続パスでエンコーディングを直接指定することです。以下:

engine = create_engine('oracle://demo:123456@192.168.1.202/TEST',encoding="UTF-8")
しかし、問題はまだ解決されていません。インターネットで検索しましたが、適切な解決策が見つかりませんでした。Mysql データベース (個人的には Postgresql の方が好きです) を使用しているときに、文字化けが発生したときによく次の操作を行っていたことを思い出しました。ターミナルの文字化け問題をサーバーのエンコードではなくクライアントのエンコードに変更することで解決します(PostgresqlのデフォルトデータベースはUTF-8なので文字化けの可能性は低いです)。さらに、Linux に Oracle クライアントをインストールする場合、NLS_LANG の環境変数が設定されます。詳細については、「Ubuntu 14.04 への Oracle Instant Client のインストール」の記事を参照してください。もちろん、この記事にはいくつかの詳細が記載されています。 . ものは紹介されていません。

通常、cmd で次の設定を行います:

engine = create_engine('oracle://demo:123456@192.168.1.202/TEST?charset=utf-8')

Oracle メッセージで使用される言語が簡体字中国語で、クライアントの文字セットが GBK であることを指定します。 NLS_LANG的环境变量,详情可以参考Ubuntu14.04安装Oracle Instant Client这篇文章,当然这篇文章有一些细节的东西没有介绍。  
一般情况下,我们在cmd中进行如下的设置:

set names gbk;

我们指定Oracle消息使用的语言为简体中文,而客户端的字符集为GBK。  
另外,我们还可以执行如下的语句来确保上述的操作是正确的:

setenv NLS_LANG=SIMPLIFIED CHINESE_CHINA.ZHS16GBK

由于上述数据库服务器是部署在Windows上的,因此其结果自然为GBK,因此如果我们客户端使用UTF8字符集进行解码,自然而言会出现解码的错误。  
我们需要注意的是,只有在数据库服务器端与客户端的编码一致的情况下,我们才能正常的显示非ASCII编码,而在sqlalchemy中默认会将查询的字符串强制将其转换为Unicode。因此类似Python3的如下过程:

SELECT * FROM v$nls_parameters;

而在sqlalchemy中由于强制进行编码转换,因此类似执行如下的过程:

>>> a='中国'.encode('gbk')
>>> a
b'\xd6\xd0\xb9\xfa'

因此就出现上述的问题了。而正常的情况应该指定其编码为GBK:

>>> a.decode('utf-8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd6 in position 0: invalid continuation byte

而设定NLS_LANGさらに、次のステートメントを実行して、上記の操作が正しいことを確認することもできます:

>>> a.decode('gbk')
'中国'

上記のデータベース サーバーは Windows にデプロイされているため、結果は当然 GBK になるため、クライアントが UTF8 文字セット デコードを使用している場合、当然デコードエラーが発生します。

注意する必要があるのは、データベース サーバーとクライアントのエンコーディングが一致している場合にのみ、非 ASCII エンコーディングを通常どおり表示でき、sqlalchemy はデフォルトでクエリ文字列を強制的に Unicode に変換するということです。そのため、Python3では次のような処理になります:

rrreee
sqlalchemyでは、強制的なエンコード変換により、次のような処理となってしまいます:

rrreee

そのため、上記の問題が発生します。通常の状況では、エンコーディングは GBK:

rrreee🎜 として指定する必要があります。NLS_LANG を設定することは、上記のエンコーディングを GBK に変更することと同じです。 🎜🎜Cx_Oracle をクエリする際の UnicodeDecodeError の問題を Python で解決する方法については、PHP 中国語 Web サイトの関連記事に注目してください。 🎜🎜🎜🎜🎜🎜
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。