Active DirectoryからMySQLサーバーにユーザーアカウントデータをプッシュしようとしています。これは問題なく機能しますが、文字列はウムラウトやその他の特殊文字のエンコードされたバージョンを表示することになります。
Active Directoryは、次のサンプル形式を使用して文字列を返します:_M\xc3\xbcller
_
これは実際には_Müller
_のUTF-8エンコードですが、_Müller
_ではなく_M\xc3\xbcller
_をデータベースに書き込みたいです。
この行で文字列を変換しようとしましたが、データベース内の同じ文字列になります:tempEntry[1] = tempEntry[1].decode("utf-8")
pythonコンソールでprint "M\xc3\xbcller".decode("utf-8")
を実行すると、出力は正しくなります。
この文字列を正しい方法で挿入する方法はありますか?この正確な形式を使用したいWeb開発者には、この特定の形式が必要です。PHPを直接使用して文字列を変換できない理由はわかりません。
追加情報:MySQLdbを使用しています。テーブルと列のエンコードはutf8_general_ciです
問題の解決策を見つけました。 .decode('unicode_escape').encode('iso8859-1').decode('utf8')
を使用した文字列のデコードは、ようやく機能しました。これで、すべてが必要に応じて挿入されました。他の完全なソリューションはここにあります: python-ldapを介してActive DirectoryからUnicodeエンコードされた文字列を操作する
@ marr75が示唆するように、必ずcharset='utf8'
接続。設定use_unicode=True
は、文字セットを設定することで暗示されるため、厳密にである必要はありません。
次に、カーソルに渡した文字セットを使用してエンコードするので、db接続にnicodeオブジェクトを渡してください。 utf8でエンコードされた文字列を渡す場合、データベースに到達すると二重にエンコードされます。
だから、次のようなもの:
conn = MySQLdb.connect(Host="localhost", user='root', password='', db='', charset='utf8')
data_from_ldap = 'M\xc3\xbcller'
name = data_from_ldap.decode('utf8')
cursor = conn.cursor()
cursor.execute(u"INSERT INTO mytable SET name = %s", (name,))
また、init_command paramを渡すことでutf8を使用するように接続を強制することもできますが、これが必要かどうかはわかりません。 5分間のテストが決定に役立ちます。
conn = MySQLdb.connect(charset='utf8', init_command='SET NAMES UTF8')
また、4.1は非常に古いため、言及する価値はほとんどありません。MySQL> = 4.1を使用していることを確認してください。
MySQLdbを使用していると仮定すると、接続を作成するときにuse_unicode = Trueとcharset = "utf8"を渡す必要があります。
更新:私が得るテストテーブルに対して次を実行すると-
>>> db = MySQLdb.connect(Host="localhost", user='root', passwd='passwd', db='sandbox', use_unicode=True, charset="utf8")
>>> c = db.cursor()
>>> c.execute("INSERT INTO last_names VALUES(%s)", (u'M\xfcller', ))
1L
>>> c.execute("SELECT * FROM last_names")
1L
>>> print c.fetchall()
(('M\xc3\xbcller',),)
これは「正しい方法」であり、文字は正しく保存および取得されますが、PHPスクリプトを記述している友人は、出力時にエンコードを正しく処理していません。
Robが指摘しているように、use_unicodeとcharsetの組み合わせは接続に関して冗長ですが、標準ライブラリ以外の最も有用なpythonライブラリでさえも自然な妄想を持っているので、ライブラリが変更された場合にバグを見つけやすくします。
import MySQLdb
# connect to the database
db = MySQLdb.connect("****", "****", "****", "****") #don't use charset here
# setup a cursor object using cursor() method
cursor = db.cursor()
cursor.execute("SET NAMES utf8mb4;") #or utf8 or any other charset you want to handle
cursor.execute("SET CHARACTER SET utf8mb4;") #same as above
cursor.execute("SET character_set_connection=utf8mb4;") #same as above
# run a SQL question
cursor.execute("****")
#and make sure the MySQL settings are correct, data too
最近、フィールド値がユニコードではなくバイト文字列であるという同じ問題がありました。少し分析します。
一般に、カーソルからユニコード値を得るために必要なことは、charset
引数を接続コンストラクターに渡し、非バイナリテーブルフィールド(utf8_general_ci
など)を持たせることです。 use_unicode
を渡すことは、charset
に値がある場合は常にtrueに設定されるため、役に立ちません。
MySQLdbはカーソルの説明フィールドタイプを尊重するため、カーソルにDATETIME
列がある場合、値はPython datatime.datetime
インスタンス、DECIMAL
からdecimal.Decimal
などに変換されます。ほとんどのデコーダーは MySQLdb.converters
で定義されており、接続コンストラクターにconv
引数を指定することでインスタンスごとにオーバーライドできます。
ただし、Unicodeデコーダーはここでは例外であり、これはおそらく設計上の欠点です。これらは、コンストラクタ内の接続インスタンスコンバータに対して 直接追加 です。したがって、instance-basicでのみオーバーライドできます。
課題コードを見てみましょう。
import MySQLdb
connection = MySQLdb.connect(user = 'guest', db = 'test', charset = 'utf8')
cursor = connection.cursor()
cursor.execute(u"SELECT 'abcdё' `s`, ExtractValue('<a>abcdё</a>', '/a') `b`")
print cursor.fetchone()
# (u'abcd\u0451', 'abcd\xd1\x91')
print cursor.description
# (('s', 253, 6, 15, 15, 31, 0), ('b', 251, 6, 50331648, 50331648, 31, 1))
print cursor.description_flags
# (1, 0)
b
フィールドがUnicodeではなくバイト文字列として返されることを示しています。ただし、バイナリではなく、MySQLdb.constants.FLAG.BINARY & cursor.description_flags[1]
( MySQLdbフィールドフラグ )。ライブラリのバグのようです( #9 )。しかし、その理由は、MySQLdb.constants.FIELD_TYPE.LONG_BLOB
(cursor.description[1][1] == 251
、 MySQLdbフィールドタイプ )にはコンバーターがまったくありません。
import MySQLdb
import MySQLdb.converters as conv
import MySQLdb.constants as const
connection = MySQLdb.connect(user = 'guest', db = 'test', charset = 'utf8')
connection.converter[const.FIELD_TYPE.LONG_BLOB] = connection.converter[const.FIELD_TYPE.BLOB]
cursor = connection.cursor()
cursor.execute(u"SELECT 'abcdё' `s`, ExtractValue('<a>abcdё</a>', '/a') `b`")
print cursor.fetchone()
# (u'abcd\u0451', u'abcd\u0451')
print cursor.description
# (('s', 253, 6, 15, 15, 31, 0), ('b', 251, 6, 50331648, 50331648, 31, 1))
print cursor.description_flags
# (1, 0)
したがって、接続インスタンスconverter
dictを操作することにより、目的のUnicodeデコード動作を実現できます。
振る舞いをオーバーライドしたい場合は、コンストラクターの後に、可能なテキストフィールドのdictエントリがどのように見えるかを示します。
import MySQLdb
import MySQLdb.constants as const
connection = MySQLdb.connect(user = 'guest', db = 'test', charset = 'utf8')
print connection.converter[const.FIELD_TYPE.BLOB]
# [(128, <type 'str'>), (None, <function string_decoder at 0x7fa472dda488>)]
MySQLdb.constants.FLAG.BINARY == 128
。これは、フィールドにバイナリフラグがある場合はstr
になり、それ以外の場合はUnicodeデコーダーが適用されることを意味します。したがって、バイナリ値も変換しようとする場合は、最初のタプルをポップできます。
(上記の回答に返信したいが、評判が足りない...)
この場合、Unicodeの結果が得られない理由は次のとおりです。
>>> print c.fetchall()
(('M\xc3\xbcller',),)
は、MySQLdb 1.2.xからのバグです* _bin照合、参照:
http://sourceforge.net/tracker/index.php?func=detail&aid=1693363&group_id=22307&atid=374932 http://sourceforge.net/tracker/index.php?func=detail&aid=2663436&group_id= 22307&atid = 374932
この特定のケース(collationutf8_bin-または[anything] _bin ...)では、「raw」値、ここではutf-8(はい、これは一般的な修正がないので残念です。
およびdb.set_character_set( 'utf8')、use_unicode = Trueを意味しますか?