91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

在Python中使用JSON時需要注意哪些編碼問題

發布時間:2021-03-23 14:59:49 來源:億速云 閱讀:138 作者:Leah 欄目:開發技術

今天就跟大家聊聊有關在Python中使用JSON時需要注意哪些編碼問題,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。

Python 中的字符編碼

在 Python3 中, 字符 在內存中是使用 Unicode 存儲的, 常規的字符使用 兩個字節 表示, 一些很生僻的字符就需要 四個字節. 默認使用 Unicode 存儲是什么意思呢, 那就是例子來解釋一下, 在 Python Shell 中輸入以下字符串 '\u4e2d\u6587', 觀察其輸出:

In [51]: '\u4e2d\u6587'
Out[51]: '中文'

輸出的為 中文 兩個字. 其實 \u4e2d 和 \u6587 分別表示 中 和 文 的 Unicode 編碼(術語稱為 碼點)的 十六進制 表示, 在 Python3 中以 \u 開頭的字符串被解析為 Unicode 字符, 然后通過其十六進制 碼點 解析出具體的字符, 所以 中文 的內存表示即為 \u4e2d\u6587.

獲取字符 Unicode 碼點

標準庫提供了 ord 函數輸出一個字符的 Unicode 碼點, 使用 chr 函數將 碼點 轉換成 字符, 下面是示例:

In [54]: ord('中')
Out[54]: 20013

In [56]: chr(20013)
Out[56]: '中'

輸出的 碼點 是使用 十進制 表示的, 可以使用以下代碼將整數格式化成十六進制字符串:

'{0:04x}'.format(20013)

使用 json.dumps

有了前面的鋪墊, 就可以來說說 json.dumps 了. 下面以一個例子展開:

In [121]: json.dumps('中文', ensure_ascii=True)
Out[121]: '"\\u4e2d\\u6587"'

In [122]: json.dumps('中文', ensure_ascii=False)
Out[122]: '"中文"'

可以看到, 在 ensure_ascii 為 True 的情況下, 中文 被編碼成了 Unicode 碼, 為 False 才能正常顯示, 但是這跟 ASCII 有什么關系呢? 來看一下官方文檔 對這個參數的解釋:

如果 ensure_ascii 是 true (即默認值),輸出保證將所有輸入的非 ASCII 字符轉義。如果 ensure_ascii 是 false,這些字符會原樣輸出。

現在稍微明白了, 在 ensure_ascii 為 True 的情況下, 如果字符串中存在 非ASCII 字符就將其轉義, 根據結果可以知道這個字符被轉義為 Unicode 碼并格式化成了一個字符串, 注意 "\\u4e2d\\u6587" 與 "\u4e2d\\u6587" 是不同的, 前者是長度為 12 的字符串, 后者會被 Python 直接解析為 中文, 長度為 2. 這也就是我一開始出現的問題, 直接將轉義的字符串在網絡上傳輸可能會無法被識別. 比如 中文 被轉義成 \\u4e2d\\u6587, 而服務器如果不知道它是被轉義過的字符串, 那它就是一個長度為 12 的普通字符串, 肯定會識別出錯. 而將 ensure_ascii 設為 False 就不會進行轉義, 使用原始字符.

識別轉義字符

如果服務器收到數據后發現是被轉化過的, 那怎么識別呢? 其實被轉義字符串與使用 unicode_escape 對字符串進行編碼再使用 utf-8 進行解碼的結果一致, 代碼如下:

In [129]: msg
Out[129]: '中文'

In [130]: msg.encode('unicode_escape').decode('utf-8')
Out[130]: '\\u4e2d\\u6587'

所以識別只要反過來使用 utf-8 編碼再使用 unicode_escape 解碼就可以了.

轉義是如何進行的

現在來看一下 json 到底是怎么對字符進行轉義的. 在 json.dumps 源碼中仔細調試的話會發現, 它調用的是 JSONEncoder.encode 方法, 而 encode 中的代碼片段如下:

if self.ensure_ascii:
  return encode_basestring_ascii(o)
else:
  return encode_basestring(o)

它會根據 ensure_ascii 的值選擇調用函數. 而 encode_basestring_ascii 的值是 (c_encode_basestring_ascii or py_encode_basestring_ascii), 也就是默認是用 C 實現的版本, 其次使用 Python 實現的版本, 既然有 Python 版本, 當然要看一下是怎么實現的, py_encode_basestring_ascii 可以直接使用 from json.encoder import py_encode_basestring_ascii 導入, 直接在其內部就可以調試. 下面是其源碼:

def py_encode_basestring_ascii(s):
  """Return an ASCII-only JSON representation of a Python string

  """
  def replace(match):
    s = match.group(0)
    try:
      return ESCAPE_DCT[s]
    except KeyError:
      n = ord(s)
      if n < 0x10000:
        return '\\u{0:04x}'.format(n)
        #return '\\u%04x' % (n,)
      else:
        # surrogate pair
        n -= 0x10000
        s1 = 0xd800 | ((n >> 10) & 0x3ff)
        s2 = 0xdc00 | (n & 0x3ff)
        return '\\u{0:04x}\\u{1:04x}'.format(s1, s2)
  return '"' + ESCAPE_ASCII.sub(replace, s) + '"'

從最后的 return 可以看到它實際上是 正則替換 最后在前后添加 雙引號. ESCAPE_ASCII 的定義如下:

ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')

其中 ([\\"] 用于匹配 \\ 和 ", 而 [^\ -~] 表示 \ -~ 取反(這里的反斜杠貌似是對空格進行轉義, 我不是很理解, 不進行轉義依舊可以匹配到), 在 ASCII 表里, 空格字符 對應十進制是 40, ~ 是 176, 這是所有的可打印字符, 取反就是所有編碼不在 40 ~ 176 的字符, 所以中文就會被匹配到, 下面為 ASCII表:

在Python中使用JSON時需要注意哪些編碼問題

對于匹配到的字符, 會傳入回調函數 replace 做轉義. replace 函數中的 ESCAPE_DCT 為:

ESCAPE_DCT = {
  '\\': '\\\\',
  '"': '\\"',
  '\b': '\\b',
  '\f': '\\f',
  '\n': '\\n',
  '\r': '\\r',
  '\t': '\\t',
}

會對常用字符進行轉義, 如果失敗就獲取它的 Unicode 碼點, 然后判斷是否為小于 0x10000 即是否為 兩字節 字符(兩字節最大為0xFFFF) , 如果是就格式化為 Unicode 碼, 如果不是就使用 四字節 表示.

看完上述內容,你們對在Python中使用JSON時需要注意哪些編碼問題有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

专栏| 双流县| 东乡族自治县| 克拉玛依市| 密云县| 忻州市| 瓦房店市| 四平市| 五常市| 东港市| 大厂| 千阳县| 桂阳县| 乐清市| 荣昌县| 舟山市| 绵竹市| 临漳县| 安溪县| 囊谦县| 大安市| 丰顺县| 和平区| 德惠市| 孟州市| 蒙自县| 称多县| 岫岩| 裕民县| 江山市| 喀喇沁旗| 遵化市| 肇源县| 泾源县| 岑巩县| 会同县| 石城县| 怀柔区| 汝州市| 乌海市| 大足县|