0

何らかの奇妙な理由で、ubuntu 12 から ubuntu 14 に切り替えた後、Python コードが機能しなくなりました。データを unpickle できなくなりました。latin1エンコーディングに変換して、データをcouchdbデータベースに保存しました。

latin1 を使用しているのは、これが cPickled バイナリ データを格納し、couchdb データベースから取得するために使用できる唯一のエンコーディングであることを少し前に読んだためです (もうリンクはありません)。json のエンコーディングの問題を回避するためのものです (couchdbkit はバックグラウンドで json を使用します)。

Latin1 は、256 文字を 256 文字にマップすることになっていましたが、これは正確にバイト単位です。現在、システムのアップグレード後、Python は有効な値が 128 個しかないかのように文句を言い、UnicodeDecodeError をスローするようです (以下を参照)。

  • 古いpythonバージョンは2.7.3でした
  • 古いcouchdbバージョン1.6.1
  • 古いcouchdbkitは0.5.7でした

  • 新しいpythonバージョンは2.7.6です

  • 新しいcouchdbバージョン1.6.1(変更なし)
  • 新しいcouchdbkitは0.6.5です

これらすべての詳細が必要かどうかはわかりませんが、私が使用するいくつかの宣言を次に示します。

#deals with all the errors when saving an item
def saveitem(item):  
    item.set_db(self.db)
    item["_id"] = key  
    error = True
    while error:
        try:    
            item.save()
            error = False
        except ResourceConflict:
            try:
                item = DBEntry.get_or_create(key)
            except ResourceConflict:
                pass
        except (NoMoreData) as e:
            print "CouchDB.set.saveitem: NoMoreData error, retrying...", str(e)
        except (RequestError) as e:
            print "CouchDB.set.saveitem: RequestError error. retrying...", str(e)

#deals with most of what could go wrong when adding an attachment
def addattachment(item, content, name = "theattachment"):
    key = item["_id"]
    error = True
    while error:
        try:
            item.put_attachment(content = content, name = name) #, content_type = "application/octet-stream"
            error = False
        except ResourceConflict:
            try:
                item = DBEntry.get_or_create(key)
            except ResourceConflict:
                print "addattachment ResourceConflict, retrying..."
            except NoMoreData:
                print "addattachment NoMoreData, retrying..."

        except (NoMoreData) as e:
            print key, ": no more data exception, wating 1 sec and retrying... -> ", str(e)
            time.sleep(1)
            item = DBEntry.get_or_create(key)
        except (IOError) as e:
            print "addattachment IOError:", str(e), "repeating..." 
            item = DBEntry.get_or_create(key)
        except (KeyError) as e:
            print "addattachment error:", str(e), "repeating..." 
            try:
                item = DBEntry.get_or_create(key)
            except ResourceConflict:
                pass
            except (NoMoreData) as e:
                pass

次に、次のように保存します。

        pickled = cPickle.dumps(obj = value, protocol = 2)
        pickled = pickled.decode('latin1')
        item = DBEntry(content={"seeattachment": True, "ispickled" : True},
            creationtm=datetime.datetime.utcnow(),lastaccesstm=datetime.datetime.utcnow())
        item = saveitem(item)
        addattachment(item, pickled)

そして、これが私が開梱する方法です。データは ubuntu 12 で書き込まれました。ubuntu 14 で解凍できません:

def unpackValue(self, value, therawkey):
    if value is None: return None
    originalval = value
    value = value["content"]
    result = None
    if value.has_key("realcontent"):
        result = value["realcontent"]
    elif value.has_key("seeattachment"):
        if originalval.has_key("_attachments"):
            if originalval["_attachments"].has_key("theattachment"):
                if originalval["_attachments"]["theattachment"].has_key("data"):
                    result = originalval["_attachments"]["theattachment"]["data"]
                    result = base64.b64decode(result)
                else:
                    print "unpackvalue: no data in attachment. Here is how it looks like:"
                    print originalval["_attachments"]["theattachment"].iteritems()
        else:
            error = True
            while error:
                try:
                    result = self.db.fetch_attachment(therawkey, "theattachment")
                    error = False
                except ResourceConflict:
                    print "could not get attachment for", therawkey, "retrying..."
                    time.sleep(1)
                except ResourceNotFound:
                    self.delete(key = therawkey, rawkey = True)
                    return None

        if value["ispickled"]:
            result = cPickle.loads(result.encode('latin1'))
    else:
        result = value

    if isinstance(result, unicode): result = result.encode("utf8")
    return result

result = cPickle.loads(result.encode('latin1'))は ubuntu 12 では成功しますが、ubuntu 14 では失敗します。次のエラー:

UnicodeDecodeError: 'ascii' コーデックは位置 0 のバイト 0xc2 をデコードできません: 序数が範囲外です (128)

ubuntu 12でそのエラーは発生しませんでした!

新しいバージョンのcouchdbkitとpythonを維持しながら、ubuntu 14でデータを読み取るにはどうすればよいですか? それはバージョン管理の問題ですか?なぜそのエラーが発生するのですか?

4

1 に答える 1

1

It appears that there is some change -- possibly in couchdbkit's API -- which makes result a UTF-8 encoded str whereas before it was unicode.

Since you want to encode the unicode in latin1, the work-around is to use

cPickle.loads(result.decode('utf8').encode('latin1'))

Note that it would be better to find where result is getting UTF-8 encoded and either preventing that from happening (so you still have unicode as you did under Ubuntu 12) or changing the encoding to latin1 so that result will already be in the form you desire.

于 2014-11-30T14:30:14.023 に答える