python檔案處理、路徑處理、序列化和反序列化
檔案IO常用操作
一般說IO操作,指的是檔案IO,如果指的是網路IO,會直接說。
把檔案儲存到磁碟上的這個過程,叫做落地。
column | column |
---|---|
open | 開啟 |
read | 讀取 |
write | 寫入 |
close | 關閉 |
readline | 行讀取 |
readlines | 多行讀取 |
seek | 檔案指標操作 |
tell | 指標位置 |
open開啟操作
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True,opener=None)
f = open("file名字") #檔案物件
print(f.read()) #讀取檔案
f.close() #關閉檔案
開啟一個檔案,返回一個檔案物件(流物件)和檔案描述符。開啟檔案失敗,則返回異常。
基本使用: 建立一個檔案test,然後開啟它,用完關閉。
檔案操作中最常用的就是讀和寫。檔案訪問的模式有兩種:文字模式和二進位制模式。
注:windows中使用codepage內碼表。可以認為每一個內碼表就是一張編碼表 cp936和gbk等價。
mode模式
描述字元 | |
---|---|
r | 預設的,表示只讀開啟 |
w | 只寫開啟,有的話就清除重新寫 |
x | 建立並寫入一個新檔案 |
a | 寫入開啟,如果檔案存在,則追加 |
b | 二進位制模式 |
t | 預設的,文字模式 |
+ | 讀寫開啟一個檔案,給原來只讀、只寫的增加缺失的功能 |
open預設是隻讀模式r開啟已經存在的檔案。
r 只讀開啟檔案,如果使用write方法,會拋異常。 如果檔案不存在,丟擲FileNotFoundError異常。
w 表示只寫方式開啟,如果讀取則丟擲異常 如果檔案不存在,則直接建立檔案 如果檔案存在,則清空檔案內容。
x 檔案不存在,建立檔案,並只寫方式開啟,檔案存在,丟擲FileExistsError異常。
a 檔案存在,只寫開啟,追加內容 檔案不存在,則建立後,只寫開啟,追加內容
r是隻讀,wxa都是隻寫。 wxa都可以產生新檔案,w不管檔案存在與否,都會生成全新內容的檔案;a不管檔案是否存在,都能在開啟的檔案尾部追加;x必須要求檔案事先不存在,自己造一個新檔案。
+為r、w、a、x提供缺失的讀或寫功能,但是,獲取檔案物件依舊按照r、w、a、x自己的特徵。 +不能單獨使用,可以認為它是為前面的模式字元做增強功能的。
t和b:
文字模式t 字元流,將檔案的位元組按照某種字元編碼理解,按照字元操作。open的預設mode就是rt。
二進位制模式b 位元組流,將檔案就按照位元組理解,與字元編碼無關。二進位制模式操作時,位元組操作使用bytes型別。
t/b不能單獨存在,要和a/w/x/r配合使用。
seek檔案指標
檔案指標,指向當前位元組位置。
mode = r,指標起始在0 ,mode = a 指標起始在EOF。
tell():顯示指標當前位置。
seek(offset[, whence]) 移動檔案指標位置。offest偏移多少位元組,whence從哪裡開始。
-
文字模式下 whence 0 預設值,表示從頭開始,offest只能正整數 whence 1 表示從當前位置,offest只接受0,whence 2 表示從EOF開始,offest只接受0
# 文字模式 f = open('test4','r+') f.tell() # 起始 f.read() f.tell() # EOF f.seek(0) # 起始 f.read() f.seek(2,0) f.read() f.seek(2,0) f.seek(2,1) # offset必須為0 f.seek(2,2) # offset必須為0 f.close()
-
二進位制模式下 whence 0 預設值,表示從頭開始,offest只能正整數 whence 1表示從當前位置,offest可正可負,whence 2 表示從EOF開始,offest可正可負。
# 二進位制模式 f = open('test4','rb+') f.tell() # 起始 f.read() f.tell() # EOF f.write(b'abc') f.seek(0) # 起始 f.seek(2,1) # 從當前指標開始,向後2 f.read() f.seek(-2,2) # 從EOF開始,向前2 f.read() f.seek(-20,2) # OSError f.close()
二進位制模式支援任意起點的偏移,從頭、從尾、從中間位置開始。 向後seek可以超界,但是向前seek的時候,不能超界,否則拋異常。
buffering緩衝區
-1 表示使用預設大小的buffer。如果是二進位制模式,使io.DEFAULT_BUFFER_SIZE值,預設是<font color= red>4096或者8192</font>。如果是文字模式,如果是終端裝置,是行快取方式,如果不是,則使用二進位制模式的策略。
- 0 只在二進位制模式使用,表示關buffer
- 1 只在文字模式使用,表示使用行緩衝。意思就是見到換行符就flush
- 大於1 用於指定buffer的大小
buffer 緩衝區
緩衝區一個記憶體空間,一般來說是一個FIFO佇列,到緩衝區滿了或者達到閾值,資料才會flush到磁碟。
flush() 將緩衝區資料寫入磁碟 close() 關閉前會呼叫flush()。
io.DEFAULT_BUFFER_SIZE 預設緩衝區大小,位元組。
buffering | 說明 |
---|---|
buffering = -1 | t和b,都是io.DEFAULT_BUFFER_SIZE |
buffering = 0 | b 關閉緩衝區 t 不支援 |
buffering = 1 | b 就一個位元組 t 行緩衝,遇到換行符才flush |
buffering > 1 | b模式表示行緩衝大小。緩衝區的值可以超過io.DEFAULT_BUFFER_SIZE,直到設定的值超出後才把緩衝區flush 。 |
t模式,是io.DEFAULT_BUFFER_SIZE位元組,flush完後把當前字串也寫入磁碟 |
一般來說:
- 文字模式,一般都用預設緩衝區大小
- 二進位制模式,是一個個位元組的操作,可以指定buffer的大小
- 一般來說,預設緩衝區大小是個比較好的選擇,除非明確知道,否則不調整它
- 一般程式設計中,明確知道需要寫磁碟了,都會手動呼叫一次flush,而不是等到自動flush或者close的時候
其他引數
編碼:windows下預設GBK(0xB0A1),Linux下預設UTF-8(0xE5 95 8A)
errors :編碼錯誤將被捕獲 None和strict表示有編碼錯誤將丟擲ValueError異常;ignore表示忽略
newline:文字模式中,換行的轉換。可以為None、'' 空串、'\r'、'\n'、'\r\n' 。
- None表示'\r'、'\n'、'\r\n'都被轉換為'\n';
- '' 表示不會自動轉換通用換行符;其它合法字元表示換行符就是指定字元,就會按照指定字元分行寫。
- '\n'或''表示'\n'不替換;其它合法字元表示'\n'會被替換為指定的字元
closefd:關閉檔案描述符,True表示關閉它。False會在檔案關閉後保持這個描述符。fileobj.fileno()檢視。
檔案描述符:Linux一切皆檔案,檔案開啟後都會有一個位於的檔案描述符,在計算機系統中是一個有限的資源。0,1,2,標準輸入,標準輸出,標準錯誤輸出。
對於類似於檔案物件的IO物件,一般來說都需要在不使用的時候關閉、登出,以釋放資源。
IO被開啟的時候,會獲得一個檔案描述符。計算機資源是有限的,所以作業系統都會做限制。就是為了保護計算機的資源不要被完全耗盡,計算資源是共享的,不是獨佔的。
一般情況下,除非特別明確的知道資源情況,否則不要提高資源的限制值來解決問題。
read()
read(size=-1)
size表示讀取的多少個字元或位元組;負數或者None表示讀取到EOF
readline(size=-1)
一行行讀取檔案內容。size設定一次能讀取行內幾個字元或位元組。
readlines(hint=-1)
讀取所有行的列表。指定hint則返回指定的行數。
write()
write(s),把字串s寫入到檔案中並返回字元的個數 writelines(lines),將字串列表寫入檔案。
close()
flush並關閉檔案物件。
檔案已經關閉,再次關閉沒有任何效果。
其他
名稱 | 說明 |
---|---|
seekable() | 是否可seek |
readable() | 是否可讀 |
writeable() | 是否可寫 |
closed() | 是否已經關閉 |
上下文管理
1、異常處理
當出現異常的時候,攔截異常。但是,因為很多程式碼都可能出現OSError異常,還不好判斷異常就是應為資源限制產生的。
f = open('test')
try:
f.write("abc") # 檔案只讀,寫入失敗
finally:
f.close() # 這樣才行
使用finally可以保證開啟的檔案可以被關閉。
上下文管理
使用with ... as 關鍵字
上下文管理的語句塊並不會開啟新的作用域
with語句塊執行完的時候,會自動關閉檔案物件
StringIO操作
io模組中的類
from io import StringIO
記憶體中,開闢的一個文字模式的buffer,可以像檔案物件一樣操作它
當close方法被呼叫的時候,這個buffer會被釋放
from io import StringIO
# 記憶體中構建
sio = StringIO() # 像檔案物件一樣操作
print(sio.readable(), sio.writable(), sio.seekable())# True True True
sio.write("magedu\nPython")
sio.seek(0)
print(sio.readline()) #magedu
print(sio.getvalue()) # 無視指標,輸出全部內容 magedu Python
sio.close()
好處
一般來說,磁碟的操作比記憶體的操作要慢得多,記憶體足夠的情況下,一般的思路是少落地,減少磁碟IO的過程,可以大大的提高程式的執行效率。
BytesIO操作
io模組中的類
from io import BytesIO
記憶體中,開闢的一個二進位制模式的buffer,可以像檔案物件一樣操作它
當close方法被呼叫的時候,這個buffer會被釋放
from io import BytesIO # 記憶體中構建
bio = BytesIO()
print(bio.readable(), bio.writable(), bio.seekable()) #True True True
bio.write(b"magedu\nPython")
bio.seek(0)
print(bio.readline()) # b'magedu\n'
print(bio.getvalue()) # 無視指標,輸出全部內容 b'magedu\nPython'
bio.close()
file-like物件
類檔案物件,可以像檔案物件一樣操作。
socket物件,輸入輸出物件(stdin、stdout)都是類檔案物件
from sys import stdout, stderr
f = stdout
print(type(f)) #<class 'ipykernel.iostream.OutStream'>
f.write('magedu.com') #magedu.com
路徑操作
os.path模組
3.4版本之前
from os import path
p = path.join('d:/','tmp')
print(type(p), p) #<class 'str'> d:/tmp
print(path.exists(p)) #判斷是否存在該路徑 True
print(path.split(p)) # (head,tail) ('d:/', 'tmp')
print(path.abspath('.')) # 列印當前的絕對路徑 C:\Users\vampire\python
p = path.join('D:/', p, 'test.txt') # 'd:/tmp\\test.txt'
print(path.dirname(p)) # 目錄名
print(path.basename(p)) #基名,就是檔名
print(path.splitdrive(p)) #二元組 ('d:', '/tmp\\test.txt')
p1 = path.abspath(".") #“檔案路徑”
print(p1, path.basename(p1))
while p1 != path.dirname(p1):
p1 = path.dirname(p1)
print(p1, path.basename(p1))
```
C:\Users\vampire\python python
C:\Users\vampire vampire
C:\Users Users
C:\
```
pathlib模組
提供Path物件來操作。包括目錄和檔案。
匯入模組:from pathlib import Path
目錄操作初始化:
p = Path() # 當前目錄 WindowsPath('.')
p.absolute()# WindowsPath('C:/Users/vampire/python')
p = Path('a','b','c/d') # 當前目錄下的 WindowsPath('C:/Users/vampire/python/a/b/c/d')
p = Path('/etc') # 根下的etc目錄
路徑拼接和分解
操作符/
Path物件 / Path物件
Path物件 / 字串 或者 字串 / Path物件
分解
parts屬性,可以返回路徑中的每一個部分
p3.absolute() #WindowsPath('C:/Users/vampire/python/c/a')
p3.absolute().parts #('C:\\', 'Users', 'vampire', 'python', 'c', 'a')
joinpath
joinpath(*other) 連線多個字串到Path物件中
p = Path() # WindowsPath('.')
p = p / 'a' # WindowsPath('a')
p.absolute() # WindowsPath('C:/Users/vampire/python/a')
p1 = 'b' / p # WindowsPath('C:/Users/vampire/python/b/a')
p2 = Path('c') # WindowsPath('C:/Users/vampire/python/c')
p2.absolute() # WindowsPath('C:/Users/vampire/python/c')
p3 = p2 / p1 # WindowsPath('c/b/a')
p3.absolute() # WindowsPath('C:/Users/vampire/python/c/b/a')
print(p3.parts) #
p3.absolute().parts # ('C:\\', 'Users', 'vampire', 'python', 'c', 'b', 'a')
p3.joinpath('etc','init.d',Path('httpd'))
獲取路徑
str 獲取路徑字串
bytes 獲取路徑字串的bytes
p = Path('/etc')
print(str(p), bytes(p))
# \etc b'\\etc'
父目錄
parent 目錄的邏輯父目錄
parents 父目錄序列,索引0是直接的父
p = Path('/a/b/c/d')
print(p.absolute()) #C:\a\b\c\d
print(p.parent.parent) #\a\b
for x in p.parents:
print(x)
#\a\b\c
#\a\b
#\a
#\
目錄的組合部分
name、stem、suffix、suffixes、with_suffix(suffix)、with_name(name)
name 目錄的最後一個部分
suffix 目錄中最後一個部分的副檔名
stem 目錄最後一個部分,沒有後綴
suffixes 返回多個副檔名列表
with_suffix(suffix) 有副檔名則替換,無則補充副檔名
with_name(name) 替換目錄最後一個部分並返回一個新的路徑
p = Path('mysqlinstall/mysql.tar.gz')
print(p.name) #mysql.tar.gz
print(p.suffix) #.gz
print(p.suffixes) # ['.tar', '.gz']
print(p.stem) # mysql.tar
print(p.with_name('mysql-5.tgz')) #\mysqlinstall\mysql-5.tgz
print(p.with_suffix('.png')) #\mysqlinstall\mysql.tar.png
p = Path('README') # README
print(p.with_suffix('.txt')) # README.txt
判斷方法
is_dir()
是否是目錄,目錄存在返回True
is_file()
是否是普通檔案,檔案存在返回True
is_symlink()
是否是軟連結
is_socket()
是否是socket檔案
is_block_device()
是否是塊裝置
is_char_device()
是否是字元裝置
is_absolute()
是否是絕對路徑
resolve()
返回一個新的路徑,這個新路徑就是當前Path物件的絕對路徑,如果是軟連結則直接被解析
absolute()
獲取絕對路徑
exists()
目錄或檔案是否存在
rmdir()
刪除空目錄。沒有提供判斷目錄為空的方法
touch(mode=0o666, exist_ok=True)
建立一個檔案
as_uri()
將路徑返回成URI,例如'file:///etc/passwd'
mkdir(mode=0o777, parents=False, exist_ok=False)
parents,是否建立父目錄,True等同於mkdir -p;False時,父目錄不存在,則丟擲FileNotFoundError
exist_ok引數,在3.5版本加入。False時,路徑存在,丟擲FileExistsError;True時,FileExistsError被忽略
iterdir()
迭代當前目錄
匹配
match(pattern)
模式匹配,成功返回True。
Path('a/b.py').match('*.py') # True
Path('/a/b/c.py').match('b/*.py') # True
Path('/a/b/c.py').match('a/*.py') # False
Path('/a/b/c.py').match('a/*/*.py') # True
Path('/a/b/c.py').match('a/**/*.py') # True
Path('/a/b/c.py').match('**/*.py') # True
stat() 相當於stat命令 ,lstat() 同stat(),但如果是符號連結,則顯示符號連結本身的檔案資訊
pathlib模組下的檔案操作
Path.open(mode='r', buffering=-1, encoding=None, errors=None, newline=None)
使用的方法類似內建函式open,返回一個檔案物件。
3.5增加的新函式
Path.read_bytes()
以'rb'讀取路徑對應檔案,並返回二進位制流。看原始碼
Path.read_text(encoding=None, errors=None)
以'rt'方式讀取路徑對應檔案,返回文字。
Path.write_bytes(data)
以'wb'方式寫入資料到路徑對應檔案。
Path.write_text(data, encoding=None, errors=None)
以'wt'方式寫入字串到路徑對應檔案。
p = Path('my_binary_file')
p.write_bytes(b'Binary file contents')
p.read_bytes() # b'Binary file contents'
p = Path('my_text_file')
p.write_text('Text file contents')
p.read_text() # 'Text file contents'
from pathlib import Path
p = Path('o:/test.py')
p.write_text('hello python')
print(p.read_text())
with p.open() as f:
print(f.read(5))
csv檔案
逗號分隔值Comma-Separated Values。
CSV 是一個被行分隔符、列分隔符劃分成行和列的文字檔案。
CSV 不指定字元編碼。
行分隔符為\r\n,最後一行可以沒有換行符
列分隔符常為逗號或者製表符。
每一行稱為一條記錄record
欄位可以使用雙引號括起來,也可以不使用。如果欄位中出現了雙引號、逗號、換行符必須使用雙引號括起來。如果欄位的值是雙引號,使用兩個雙引號表示一個轉義。
表頭可選,和欄位列對齊就行了。
手動生成csv檔案
from pathlib import Path
p = Path('D:/tmp/test.csv')
parent = p.parent
if not parent.exists():
parent.mkdir(parents=True,exist_ok =True) #exist_ok 用在python3.5之後,如果檔案目錄存在,True則壓制異常。
csv_body = '''\
id,name,age,comment
1,zs,18,"I'm 18"
2,ls,20,"this is a ""test"" string."
3,ww,23,"你好
計算機
"
'''
p.write_text(csv_body)
csv模組
def reader(iterable, dialect='excel', *args, **kwargs)
返回一個reader物件,是一個行迭代器
預設使用excel方言,如下:
delimiter 列分隔符,逗號
lineterminator 行分隔符\r\n
quotechar 欄位的引用符號,預設為"雙引號
-
雙引號的處理
- doublequote 雙引號的處理,預設為True。如果碰到資料中有雙引號,而quotechar也是雙引號,True則使用2個雙引號表示,False表示使用轉義字元將作為雙引號的字首。
- escapechar 一個轉義字元,預設為None
- writer = csv.writer(f, doublequote=False, escapechar='@') 遇到雙引號,則必須提供轉義字元
-
quoting 指定雙引號的規則
QUOTE_ALL 所有欄位
QUOTE_MINIMAL特殊字元欄位,Excel方言使用該規則
QUOTE_NONNUMERIC非數字欄位
QUOTE_NONE都不使用引號。
def writer(fileobj, dialect='excel', *args, **kwargs)
返回DictWriter例項,主要的方法有writerow,writerows。
import csv
p = Path('d://tmp/tesr.csv')
with open(str(p)) as f:
reader = csv.reader(f) #返回一個迭代物件
print(next(reader)) #不回頭
print(next(reader))
for line in reader:
print(line)
rows = [
[4,'tom',22,'tom'],
(5,'jerry',24,'jerry'),
(6,'justin',22,'just\t"in'),
"abcdefghi",
((1,),(2,))
]
row = rows[0]
with open(str(p), 'a',newline="") as f: #newline為了不換行
writer = csv.writer(f)
writer.writerow(row) #一次寫一條
writer.writerows(rows) #將所有的一次寫入
ini檔案
一般作為配置檔案。
ini檔案:
[DEFAULT]
a = test
[mysql]
default-character-set=utf8
[mysqld]
datadir =/dbserver/data
port = 33060
character-set-server=utf8
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
中括號裡面的部分稱為section,譯作節、區、段。
每一個section內,都是key=value形成的鍵值對,key稱為option選項。
這裡的DEFAULT是預設section的名字,必須大寫。
configparser模組
configparser模組的ConfigParser類就是用來操作。
可以將section當做key,section儲存著鍵值對組成的字典,可以把ini配置檔案當做一個巢狀的字典。預設使用的是有序字典。
read(filenames, encoding=None)
#讀取ini檔案,可以是單個檔案,也可以是檔案列表。可以指定檔案編碼。
sections() #返回section列表。預設section不包括在內。
add_section(section_name) #增加一個section。
has_section(section_name) #判斷section是否存在
options(section) #返回section的所有option,會追加預設section的option
has_option(section, option) #判斷section是否存在這個option
get(section, option, *, raw=False, vars=None[, fallback])
#從指定的段的選項上取值,如果找到返回,如果沒有找到就去找DEFAULT段有沒有。
getint(section, option, *, raw=False, vars=None[, fallback])
getfloat(section, option, *, raw=False, vars=None[, fallback])
getboolean(section, option, *, raw=False, vars=None[, fallback])
#上面3個方法和get一樣,返回指定型別資料。
items(raw=False, vars=None)
items(section, raw=False, vars=None)
#沒有section,則返回所有section名字及其物件;如果指定section,則返回這個指定的section的鍵值對組成二元組。
set(section, option, value)
#section存在的情況下,寫入option=value,要求option、value必須是字串。
remove_section(section)
#移除section及其所有option
remove_option(section, option)
#移除section下的option。
write(fileobject, space_around_delimiters=True)
#將當前config的所有內容寫入fileobject中,一般open函式使用w模式。
程式碼示例:
from configparser import ConfigParser
from pathlib import Path
filename = Path("d://tmp/mysql.ini")
newfilename = Path("d://tmp/mysql111.ini")
cfg = ConfigParser()
read_ok = cfg.read(str(filename))
print(read_ok)
print(cfg.sections())
print(cfg.has_section("mysql"))
print("-"*30)
for k,v in cfg.items(): #未指定section
print(k,type(k))
print(v,type(v))
print(cfg.items(k))
print("~~~~~~~~~~~~~~~~~~")
print("-"*30)
for k,v in cfg.items("mysqld"): #指定section
print(k,type(k))
print(v,type(v))
print("~~~~~~~~~~")
tmp = cfg.get("mysqld","port")
print(tmp, type(tmp))
print(cfg.get("mysqld", "a"))
print(cfg.get("mysqld", "python" , fallback= "linux")) #按照型別,fallbac:給與預設值
tmp = cfg.getint("mysqld", "port")
print(type(tmp), tmp)
cfg.add_section("test")
cfg.set("test","test1","1")
cfg.set("test","test2","2")
with open(newfilename,"w+",newline="") as f:
cfg.write(f)
print(cfg.getint("test" , "test1"))
cfg.remove_option("test", "test1")
# cfg.remove_section("test")
# print("x" in cfg["test2"])
#字典操作
cfg["test3"] = {"c":"1000"} #沒有落地,在記憶體中修改
print("x" in cfg["test"])
print("c" in cfg["test3"])
# 其他內部方式
print(cfg._dict) # 返回預設的字典型別,預設使用有序字典
for k, v in cfg._sections.items():
print(k, v)
for k,v in cfg._sections['mysqld'].items():
print(k,v)
#重新寫入檔案
with open(newfilename, 'w') as f:
cfg.write(f)
序列化和反序列化
要設計一套協議,按照某種規則,把記憶體中資料儲存到檔案中,檔案是一個位元組序列,所以必須把資料轉換成<font color=red >位元組</font>序列,輸出到檔案。這就是序列化。反之,從檔案的位元組序列恢復到記憶體。就是反序列化。
serialization:序列化
將記憶體中物件儲存下來,變成一個個位元組 --> 二進位制deseiralization:反序列化
將檔案中的一個個位元組恢復成記憶體中物件 <--二進位制
序列化儲存到檔案就是持久化,可以將資料序列化後持久化,或者網路傳輸;也可以將檔案中或者網路中接收到的位元組序列反序列化。
pickle庫
python中的序列化,反序列化模組。
dumps 物件序列化為bytes物件 dump 物件序列化到檔案物件,就是存入檔案
loads 從bytes物件反序列化 load 物件反序列化,從檔案讀取資料
序列化的應用
一般來說,本地序列化的情況,應用較少。大多數場景都應用在網路傳輸中。
將資料序列化後通過網路傳輸到遠端節點,遠端伺服器上的服務將接收到的資料反序列化後,就可以使用了。
但是,要注意一點,遠端接收端,反序列化時必須有對應的資料型別,否則就會報錯。尤其是自定義類,必須遠端的有一致的定義。
現在,大多數專案,都不是單機的,也不是單服務的。需要通過網路將資料傳送到其他節點上去,這就需要大量的序列化、反序列化過程。
但是,問題是,Python程式之間還可以都是用pickle解決序列化、反序列化,如果是跨平臺、跨語言、跨協議pickle就不太適合了,就需要公共的協議。例如XML、Json、Protocol Buffer等。
不同的協議,效率不同、學習曲線不同,適用不同場景,要根據不同的情況分析選型。
Json
Json(JavaScript Object Notation,JS物件標記)是一種輕量級的資料交換格式。它基於 ECMAScript (w3c組織制定的JS規範)的一個子集,採用完全獨立於程式語言的文字格式來儲存和表示資料。網址: http://json.org/
Json的資料型別
值:雙引號引起來的字串,數值,true和false,null,物件,陣列,這些都是值
字串:有正負,有整數,浮點數。
物件:無序的鍵值對的集合,格式{key:value...},key必須是一個字串,需要雙引號包圍這個字串,value可以是任意合法的值。
陣列:有序的值的集合 格式[val1,,,,valn]
{
"person": [
{
"name": "tom",
"age": 18
},
{
"name": "jerry",
"age": 16
}
],
"total": 2
}
Json模組
Python支援少量內建資料型別到Json型別的轉換
Python型別 | Json型別 |
---|---|
True | true |
False | false |
None | null |
str | string |
int | integer |
float | float |
list | array |
dict | object |
常用方法
Python型別 | Json型別 |
---|---|
dumps | Json編碼 |
dump | Json編碼並存入檔案 |
loads | Json解碼 |
load | Json解碼,從檔案讀取資料 |
一般Json編碼的資料很少落地,資料都是通過網路傳輸,傳輸的時候,要考慮壓縮它,節省流量。本質來說它就是個文字,就是個字串。
MessagePack
MessagePack是一個基於二進位制高效的物件序列化類庫,可用於跨語言通訊。
它可以像JSON那樣,在許多種語言之間交換結構物件。
相容 json和pickle。
MessagePack簡單易用,高效壓縮,支援語言豐富。
所以,用它序列化也是一種很好的選擇。
安裝:$pip install msgpack-python
常用方法:
packb 序列化物件。提供了dumps來相容pickle和json。
unpackb 反序列化物件。提供了loads來相容。
pack 序列化物件儲存到檔案物件。提供了dump來相容。
unpack 反序列化物件儲存到檔案物件。提供了load來相容。
import pickle
import json
import msgpack
d = {"person":[{"name":"tom","age":18},{"name":"jerry","age":16}],"total":2}
j = json.dumps(d)
print(j, type(j), len(j)) # 請注意引號的變化
print(len(j.replace(' ',''))) # 72 bytes 注意這樣替換的壓縮是不對的
print("-"*30)
p = pickle.dumps(d)
print(p)
print(len(p)) # 101 bytes
print("-"*30)
m = msgpack.dumps(d)
print(m)
print(len(m)) # 48 bytes
print("-"*30)
u = msgpack.unpackb(m)
print(type(u), u)
u = msgpack.loads(m, encoding='utf-8')
print(type(u), u)
{"person": [{"name": "tom", "age": 18}, {"name": "jerry", "age": 16}], "total": 2} <class 'str'> 82
72
------------------------------
b'\x80\x03}q\x00(X\x06\x00\x00\x00personq\x01]q\x02(}q\x03(X\x04\x00\x00\x00nameq\x04X\x03\x00\x00\x00tomq\x05X\x03\x00\x00\x00ageq\x06K\x12u}q\x07(h\x04X\x05\x00\x00\x00jerryq\x08h\x06K\x10ueX\x05\x00\x00\x00totalq\tK\x02u.'
101
------------------------------
b'\x82\xa6person\x92\x82\xa4name\xa3tom\xa3age\x12\x82\xa4name\xa5jerry\xa3age\x10\xa5total\x02'
48
------------------------------
<class 'dict'> {b'person': [{b'name': b'tom', b'age': 18}, {b'name': b'jerry', b'age': 16}], b'total': 2}
<class 'dict'> {'person': [{'name': 'tom', 'age': 18}, {'name': 'jerry', 'age': 16}], 'total': 2}