【问题标题】:python decode partial utf-8 byte arraypython解码部分utf-8字节数组
【发布时间】:2016-10-14 13:34:39
【问题描述】:

我从不了解 UTF-8 规则的通道获取数据。因此,有时当 UTF-8 使用多个字节对一个字符进行编码时,我尝试将部分接收到的数据转换为文本,我在转换过程中会出错。由于接口的性质(没有任何结束的流),我无法找出数据何时已满。因此我需要处理部分 utf-8 解码。基本上我需要尽可能解码并存储部分数据。存储的部分数据将作为前缀添加到下一个数据。我的问题是python中是否有一些简洁的功能可以允许它?

[编辑] 只是为了确保你知道docs.python中的函数

 bytes.decode(encoding="utf-8", errors="ignore")

但问题是它不会返回错误在哪里,所以我不知道我应该保留多少字节。

【问题讨论】:

    标签: python utf-8


    【解决方案1】:

    您可以调用编解码器模块进行救援。它直接为您提供增量解码器,完全满足您的需求:

    import codecs
    
    dec = codecs.getincrementaldecoder('utf8')()
    

    您可以使用:dec.decode(input) 提供它,当它结束时,可以选择添加 dec.decode(bytes(), True) 以强制它清理任何存储的状态。

    测试变成:

    >>> def test(arr):
        dec = codecs.getincrementaldecoder('utf8')()
        recvString = ""
        for i in range(len(arr)):
            recvString += dec.decode(arr[i:i+1])
            sys.stdout.write("%02d : %s\n" % (i, recvString))
        recvString += dec.decode(bytes(), True) # will choke on incomplete input...
        return recvString == arr.decode('utf8')
    
    >>> testUtf8 = bytes([0x61, 0xc5, 0xbd, 0x6c, 0x75, 0xc5, 0xa5, 0x6f, 0x75, 0xc4, 0x8d, 0x6b, 0xc3, 0xbd, 0x20, 0x6b, 0xc5, 0xaf, 0xc5, 0x88])
    >>> test(testUtf8)
    00 : a
    01 : a
    02 : aŽ
    03 : aŽl
    04 : aŽlu
    05 : aŽlu
    06 : aŽluť
    07 : aŽluťo
    08 : aŽluťou
    09 : aŽluťou
    10 : aŽluťouč
    11 : aŽluťoučk
    12 : aŽluťoučk
    13 : aŽluťoučký
    14 : aŽluťoučký 
    15 : aŽluťoučký k
    16 : aŽluťoučký k
    17 : aŽluťoučký ků
    18 : aŽluťoučký ků
    19 : aŽluťoučký kůň
    True
    

    【讨论】:

      【解决方案2】:

      到目前为止,我想出了不太好的功能:

      def decodeBytesUtf8Safe(toDec):
          """
          decodes byte array in utf8 to string. It can handle case when end of byte array is
          not complete thus making utf8 error. in such case text is translated only up to error.
          Rest of byte array (from error to end) is returned as second parameter and can be
          combined with next byte array and decoded next time.
          :param toDec: bytes array to be decoded a(eg bytes("abc","utf8"))
          :return:
           1. decoded string
           2. rest of byte array which could not be encoded due to error
          """
          okLen = len(toDec)
          outStr = ""
          while(okLen>0):
              try:
                  outStr = toDec[:okLen].decode("utf-8")
              except UnicodeDecodeError as ex:
                  okLen -= 1
              else:
                  break
          return outStr,toDec[okLen:]
      

      您可以使用脚本对其进行测试:

      def test(arr):
          expStr = arr.decode("utf-8")
          errorCnt = 0
          for i in range(len(arr)+1):
              decodedTxt, rest = decodeBytesUtf8Safe(arr[0:i])
              decodedTxt2, rest2 = decodeBytesUtf8Safe(rest+arr[i:])
              recvString = decodedTxt+decodedTxt2
              sys.stdout.write("%02d ; %s (%s - %s )\n"%(i,recvString,decodedTxt, decodedTxt2))
              if(expStr != recvString):
                  print("Error when divided at %i"%(i))
                  errorCnt += 1
          return errorCnt
      
      testUtf8 = bytes([0x61, 0xc5, 0xbd, 0x6c, 0x75, 0xc5, 0xa5, 0x6f, 0x75, 0xc4, 0x8d, 0x6b, 0xc3, 0xbd, 0x20, 0x6b, 0xc5, 0xaf, 0xc5, 0x88])
      err = test(testUtf8)
      print("total errors %i"%(err))
      

      它会给你输出:

      00 ; aŽluťoučký kůň ( - aŽluťoučký kůň )
      01 ; aŽluťoučký kůň (a - Žluťoučký kůň )
      02 ; aŽluťoučký kůň (a - Žluťoučký kůň )
      03 ; aŽluťoučký kůň (aŽ - luťoučký kůň )
      04 ; aŽluťoučký kůň (aŽl - uťoučký kůň )
      05 ; aŽluťoučký kůň (aŽlu - ťoučký kůň )
      06 ; aŽluťoučký kůň (aŽlu - ťoučký kůň )
      07 ; aŽluťoučký kůň (aŽluť - oučký kůň )
      08 ; aŽluťoučký kůň (aŽluťo - učký kůň )
      09 ; aŽluťoučký kůň (aŽluťou - čký kůň )
      10 ; aŽluťoučký kůň (aŽluťou - čký kůň )
      11 ; aŽluťoučký kůň (aŽluťouč - ký kůň )
      12 ; aŽluťoučký kůň (aŽluťoučk - ý kůň )
      13 ; aŽluťoučký kůň (aŽluťoučk - ý kůň )
      14 ; aŽluťoučký kůň (aŽluťoučký -  kůň )
      15 ; aŽluťoučký kůň (aŽluťoučký  - kůň )
      16 ; aŽluťoučký kůň (aŽluťoučký k - ůň )
      17 ; aŽluťoučký kůň (aŽluťoučký k - ůň )
      18 ; aŽluťoučký kůň (aŽluťoučký ků - ň )
      19 ; aŽluťoučký kůň (aŽluťoučký ků - ň )
      20 ; aŽluťoučký kůň (aŽluťoučký kůň -  )
      total errors 0
      

      【讨论】:

      • 这是您问题的解决方案还是您问题的一部分。如果这是您尝试过的,那么它应该在您的问题中。
      • 我试过了。它似乎有效。但我对尺寸不满意。我觉得一些python大师可能会想出一些单行或至少可以一步运行而不是循环尝试的东西......所以它是有效的答案,它可能就在这里。我只是想要更好的答案:)
      • 试试看:codereview.stackexchange.com 你可能会得到更好的回应。祝你好运。
      猜你喜欢
      • 2018-06-18
      • 2020-07-17
      • 2013-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多