1. 程式人生 > >路邊拾遺之其他模塊(struct/csv)

路邊拾遺之其他模塊(struct/csv)

而在 mar 這一 mod enc 太多的 log 好的 utf8

struct模塊

最近在學習python網絡編程這一塊,在寫簡單的socket通信代碼時,遇到了struct這個模塊的使用,當時不太清楚這到底有和作用,後來查閱了相關資料大概了解了,在這裏做一下簡單的總結。

了解c語言的人,一定會知道struct結構體在c語言中的作用,它定義了一種結構,裏面包含不同類型的數據(int,char,bool等等),方便對某一結構對象進行處理。而在網絡通信當中,大多傳遞的數據是以二進制流(binary data)存在的。當傳遞字符串時,不必擔心太多的問題,而當傳遞諸如int、char之類的基本數據的時候,就需要有一種機制將某些特定的結構體類型打包成二進制流的字符串然後再網絡傳輸,而接收端也應該可以通過某種機制進行解包還原出原始的結構體數據。python中的struct模塊就提供了這樣的機制,該模塊的主要作用就是對python基本類型值與用python字符串格式表示的C struct類型間的轉化(This module performs conversions between Python values and C structs represented as Python strings.)。stuct模塊提供了很簡單的幾個函數,下面寫幾個例子。

1.基本的pack和unpack

struct提供用format specifier方式對數據進行打包和解包(Packing and Unpacking)。例如:
import struct
import binascii # 字符串轉換模塊

# 1.struct 基礎應用
values = (1, 'abc'.encode("utf8"), 2.7)  # python3 需要轉化成bytes類型
s = struct.Struct('I3sf')  # 定義format 格式  格式參考help(struct) #??大端存儲小端存儲???何以?
packed_data = s.pack(*values)  # 這個地方要是字符串必須用bytes類型
print(packed_data)  # 打印pack的bytes類型
print(len(packed_data))  # 長度為12字節
print(binascii.hexlify(packed_data))  # 打包後的十六位值的表示
unpacked_data = s.unpack(packed_data)
print(unpacked_data)
# 輸出結果
# b'\x01\x00\x00\x00abc\x00\xcd\xcc,@'
# 12
# b'0100000061626300cdcc2c40'
# (1, b'abc', 2.700000047683716)


代碼中,首先定義了一個元組數據,包含int、string、float三種數據類型,然後定義了struct對象,
並制定了format‘I3sf’,I 表示int,3s表示三個字符長度的字符串,f 表示 float。
最後通過struct的pack和unpack進行打包和解包。
通過輸出結果可以發現,value被pack之後,轉化為了一段二進制字節串,而unpack可以把該字節串再轉換回一個元組,
但是值得註意的是對於float的精度發生了改變,這是由一些比如操作系統等客觀因素所決定的。打包之後的數據所占用的字節數與C語言中的struct十分相似。
定義format可以參照官方

api提供的對照表:
技術分享圖片

2.字節順序

另一方面,打包的後的字節順序默認上是由操作系統的決定的,當然struct模塊也提供了自定義字節順序的功能,可以指定大端存儲、小端存儲等特定的字節順序,對於底層通信的字節順序是十分重要的,不同的字節順序和存儲方式也會導致字節大小的不同。在format字符串前面加上特定的符號即可以表示不同的字節順序存儲方式,例如采用小端存儲 s = struct.Struct(‘<I3sf’)就可以了。官方api library 也提供了相應的對照列表:
技術分享圖片

3.利用buffer,使用pack_into和unpack_from方法

使用二進制打包數據的場景大部分都是對性能要求比較高的使用環境。而在上面提到的pack方法都是對輸入數據進行操作後重新創建了一個內存空間用於返回,也就是說我們每次pack都會在內存中分配出相應的內存資源,這有時是一種很大的性能浪費。struct模塊還提供了pack_into() 和 unpack_from()的方法用來解決這樣的問題,也就是對一個已經提前分配好的buffer進行字節的填充,而不會每次都產生一個新對象對字節進行存儲。

    import ctypes

    # 2.
    value1 = (2, b"asxc", 5.0)
    value2 = (b"tteedd", 5.1)
    s1 = struct.Struct("I4sf")
    print(s1.size)
    s2 = struct.Struct("6sf")
    print(s2.size)
    prebuffer = ctypes.create_string_buffer(s1.size + s2.size)  # 初步理解創建一個buffer的字符數字內存地址
    print(prebuffer)
    s1.pack_into(prebuffer, 0, *value1)
    s2.pack_into(prebuffer, s1.size, *value2)  # 給這個buffer後面接數字位置  unpack的時候需要用位置 加struct的格式unpack
    print(prebuffer)
    print(s1.unpack_from(prebuffer, 0))
    print(s2.unpack_from(prebuffer, s1.size))
    # 輸出結果
    # 12
    # 12
    # <ctypes.c_char_Array_24 object at 0x000000000055ECC8>
    # <ctypes.c_char_Array_24 object at 0x000000000055ECC8>
    # (2, b'asxc', 5.0)
    # (b'tteedd', 5.099999904632568)

csv模塊

csv文件格式是一種通用的電子表格和數據庫導入導出格式。最近我調用RPC處理服務器數據時,經常需要將數據做個存檔便使用了這一方便的格式。

1.第一種方法使用reader函數,接收一個可叠代的對象(比如csv文件),能返回一個生成器,就可以從其中解析出csv的內容:比如下面的代碼可以讀取csv的全部內容,以行為單位:

 with open(r"E:\學習之路第一章\路邊拾遺技能請看\zjj.csv", 'r') as f:
     data = csv.reader(f, )  # CSV 模塊打開對象,得到一個文件叠代對象,註意叠代對象取值完成之後在取值為空了
     for row in data:
     print(row)
     # 輸出結果
     # ['id', 'name', 'age', 'addr']
     # ['1', '張三', '23', '北京']
     # ['2', '李四', '22', '上海']
     # ['3', '小黑', '18', '深圳']
     # ['4', '小紅', '33', '杭州']
     # ['1', '趙五', '23', '青島']

with open(r"E:\學習之路第一章\路邊拾遺技能請看\zjj.csv", 'r') as f:
    data = csv.reader(f, delimiter=':', quoting=csv.QUOTE_NONE)  # CSV 模塊打開對象,得到一個文件叠代對象,
# 指定讀取時的文件格式
    for row in data:
        print(row)
# 輸出結果        
# ['id,name,age,addr']
# ['1,張三,23,北京']
# ['2,李四,22,上海']
# ['3,小黑,18,深圳']
# ['4,小紅,33,杭州']
# ['1,趙五,23,青島']
    if row[0] == "id":  # 判斷如果第一行第0個等於"id" 則讀取第一行 序列為3的字符
         print(row[3])  # 打印文件的每一行組成一個列表
     rows = [row for row in data]  # 生成一個列表每行是一個子列表
     print(rows)
     # 讀取指定列的數據
     cloumn = [row[0] for row in data]  # row[0]表示第幾列數據
         print(cloumn)  # ['id', '1', '2', '3', '4']

2.文件寫入

讀文件時,我們把csv文件讀入列表中,寫文件時會把列表中的元素寫入到csv文件中。

import csv
row = ["1", "趙五", "23", "青島"]  # 生成一行需要插入的數據,可以從數據庫讀取組成元祖
with open(r"E:\學習之路第一章\路邊拾遺技能請看\zjj.csv", 'a', newline="") as f:
csv_writer = csv.writer(f)  # 生成一個writer的對象 csv下面的生成writer的方法
csv_writer.writerow(row)  # 用生成的writer這個對象 執行writerow方法寫入問價

註意:如果以rb的模式打開讀或者寫這樣操作文件比較方便。

路邊拾遺之其他模塊(struct/csv)