Python. Модуль struct. Робота з бінарними файлами




Модуль struct. Робота з бінарними файлами. Приклади збереження/читання упакованих двійкових даних


Зміст


Пошук на інших ресурсах:

1. Застосування засобів модуля struct для роботи з файлами

У мові Python модуль struct використовується для збереження та читання упакованих двійкових даних. Цей модуль містить ряд методів, які дозволяють отримати упакований об’єкт на основі рядка формату. Біль детально про методи модуля struct можна прочитати тут.

Перед використанням модуля struct його потрібно підключити директивою

import struct

При роботі з бінарними файлами використання модуля struct може бути застосоване у наступних випадках:

  • під час запису даних у файлі, вони попередньо упаковуються з допомогою методів pack() або pack_into();
  • при зчитуванні попередньо записаних упакованих даних, вони розпаковуються методами unpack() або unpack_into().

 

2. Приклади використання засобів модуля struct
2.1. Приклад запису/читання різнотипних даних у файл

У прикладі спочатку записуються, потім читаються дані, що розміщені у списку. Типи даних є різними: float, bool, char[].

# Бінарні файли. Запис/читання списку різнотипних даних.
# Використання можливостей модуля struct.

# 1. Підключити модуль struct
import struct

# 2. Заданий список різнотипних даних:
#   - 1.5 - тип float, у struct позначається 'f'
#   - True - тип bool, у struct позначається '?'
#   - 'abc def' - тип char[], у struct позначається 's'
L = [1.5, True, 'abc def']

# 3. Запис списку L у файл 'myfile4.bin'
# 3.1. Відкрити файл для запису
f = open('myfile4.bin', 'wb')

# 3.2. Записати список в упакованому форматі.
#     Для упакування даних використовується метод pack()
#     Розшифровка рядка '>f?7s':
#     - '>' - звортній порядок байт (старші байти слідують останніми);
#     - 'f' - тип float;
#     - '?' - тип bool;
#     - '7s' - тип char[] розміром 7 символів.
d = struct.pack('>f?7s', L[0], L[1], L[2].encode())

# 3.3. Записати упаковані дані d у файл
f.write(d)

# 3.4. Закрити файл
f.close();

# 4. Зчитати список з бінарного файлу 'myfile4.bin'
# 4.1. Відкрити файл для читання
f = open('myfile4.bin', 'rb')

# 4.2. Прочитати дані з файлу
d = f.read()

# 4.3. Розпакувати дані з допомогою методу unpack().
#      Дані розпаковуються у вигляді кортежу.
T = struct.unpack('>f?7s', d)

# 4.4. Конвертувати кортеж T у список L2
L2 = list(T)

# 4.5. Конвертувати рядок L2[2] у тип str
L2[2] = L2[2].decode()

# 4.6. Вивести список
print("L2 = ", L2) # L2 =   [1.5, True, 'abc def']

# 4.7. Закрити файл
f.close();

Результат роботи програми

L2 = [1.5, True, 'abc def']

 



2.2. Приклад запису/читання списку, що містить цілі числа

У прикладі продемонстровано запис списку у файл та читання списку з файлу. При читанні списку з файлу розмір записаного раніше списку наперед невідомий.

При записі списку відбувається поелементний запис на основі рядка формату ‘>i’.

У випадку читання, спочатку читається кількість елементів, а, потім вже читається увесь список за один раз з допомогою рядка

T = struct.unpack('>ni', d)

де

  • ‘>ni’ – рядок формату, в якому n – кількість записаних елементів;
  • d – двійковий об’єкт;
  • T – кортеж чисел, отриманих при розпакуванні об’єкту d.

 

# Бінарні файли. Модуль struct.
# Приклад запису/читання списку цілих чисел

# 1. Підключити модуль struct
import struct

# 2. Заданий список
L = [ 2, 4, 6, 8, 10 ]

# 3. Запис списку у файл
# 3.1. Відкрити файл для запису
f = open('myfile9.bin', 'wb')

# 3.2. Записати список у файл поелементно
# 3.2.1. Отримати упакований об'єкт даних на основі списку s
d = struct.pack('>i', len(L))

# 3.2.2. Записати об'єкт d у файл
f.write(d)

# 3.2.3. Записати елементи по одному - так теж можна
for item in L:
    d = struct.pack('>i', item)
    f.write(d)

# 3.3. Закрити файл
f.close()

# ---------------------------------------------
# 4. Читання списку з файлу
# 4.1. Відкрити файл для читання
f = open('myfile9.bin', 'rb')

# 4.2. Зчитати кількість елементів списку - перше число у файлі,
#      зчитується перші 4 байти - розмір типу int
d = f.read(4) # d - двійковий об'єкт
count = struct.unpack('>i', d)[0]

# 4.3. Зчитати весь список у двійковий об'єкт d
d = f.read() # читання відбувається з поточної позиції до кінця файлу

# 4.4. Сформувати рядок формату для читання усіх чисел за один раз
#     (числа можна також читати по одному в циклі)
s = '>' + str(count) + 'i'

# 4.5. Отримати кортеж з чисел на основі рядка формату
T = struct.unpack(s, d)

# 4.6. Конвертувати кортеж у список
L2 = list(T)

# 4.7. Вивести список для контролю
print("L2 = ", L2)

# 4.8. Закрити файл
f.close()

Результат роботи програми

L2 = [2, 4, 6, 8, 10]

 

2.3. Приклад запису/читання кортежу, що містить рядки

Якщо у бінарному файлі потрібно зберігати декілька рядків, то при збереженні кожного рядка обов’язково вказується його довжина, а потім сам рядок. Це зв’язано з тим, що рядки мають різну довжину.

# Бінарні файли. Модуль struct.
# Приклад запису/читання кортежу рядків.

# 1. Підключити модуль struct
import struct

# 2. Заданий кортеж рядків, які потрібно записати у файл.
T = ( 'abc', 'abcd', 'def', 'ghi jkl')

# 3. Запис у файл кортежу T
# 3.1. Відкрити файл для запису у бінарному режимі
f = open('myfile10.bin', 'wb')

# 3.2. Записати кількість елементів у кортежі
count = len(T)
d = struct.pack('>i', count) # отримати упаковані дані
f.write(d) # записати упаковані дані

# 3.3. Записати кожен рядок кортежу в циклі.
#     Оскільки кожен рядок має різну довжину,
#     то цю довжину потріно також записувати у файл.
for item in T: # цикл обходу елементів кортежу
    # взяти довжину рядка item
    length = len(item)

    # упакувати довжину рядка length
    d = struct.pack('>i', length)

    # записати у файл
    f.write(d)

    # упакувати рядок item: '>ns' - означає char[n]
    bt_item = item.encode() # конвертувати str=>bytes
    d = struct.pack('>' + str(length) + 's', bt_item)

    # записати у файл
    f.write(d)

# 3.4. Закрити файл
f.close()

# ------------------------------------------------------
# 4. Читання записаного кортежу з файлу
# 4.1. Відкрити файл для читання у двійковому режимі
f = open('myfile10.bin', 'rb')

# 4.2. Зчитати кількість елементів (рядків) у файлі
d = f.read(4) # Зчитати перші 4 байти - розмір типу int, d - упаковані дані
count = struct.unpack('>i', d)[0] # count - кількість елементів

# 4.3.Сформувати пустий кортеж-результат
T2 = ()

# 4.4. Цикл читання рядків
i = 0
while i<count:
    # зчитати довжину рядка
    d = f.read(4)
    length = struct.unpack('>i', d)[0] # length - довжина рядка

    # сформувати рядок формату
    sf = '>' + str(length) + 's'

    # зчитати length байт з файлу в об'єкт d
    d = f.read(length)

    # розпакувати рядок згідно рядка sf
    sb = struct.unpack(sf, d)[0] # sb - рядок типу bytes

    # конвертувати bytes=>str
    s = sb.decode()

    # Додати рядок до кортежу
    T2 = T2 + (s,)

    i = i+1

# 4.5. Вивести кортеж T2 для контролю
print("T2 = ", T2)

# 4.6. Закрити файл
f.close()

Результат роботи програми

T2 = ('abc', 'abcd', 'def', 'ghi jkl')

 

2.4. Приклад запису/читання словника в якому пара ключ:значення має тип int:str

 

# Бінарні файли. Модуль struct.
# Приклад запису/читання словника

# 1. Підключити модуль struct
import struct

# 2. Заданий словник
D = { 1:'Sunday', 2:'Monday', 3:'Tuesday', 4:'Wednesday',
      5:'Thursday', 6:'Friday', 7:'Saturday' }

# 3. Запис у файл кортежу T
# 3.1. Відкрити файл для запису у бінарному режимі
f = open('myfile11.bin', 'wb')

# 3.2. Записати кількість елементів у словнику
count = len(D)
d = struct.pack('>i', count) # отримати упаковані дані
f.write(d) # записати упаковані дані

# 3.3. Записати кожен рядок словника в циклі.
#     Оскільки кожен рядок може мати різну довжину,
#     то цю довжину потріно також записувати у файл.
for key in D: # цикл обходу елементів словника
    # записати ключ - ціле число
    dd = struct.pack('>i', key)
    f.write(dd)

    # записати рядок за ключем
    # взяти довжину рядка item
    length = len(D[key])

    # упакувати довжину рядка length
    dd = struct.pack('>i', length)

    # записати у файл довжину
    f.write(dd)

    # упакувати рядок D[key]: '>ns' - означає char[n]
    bt_item = D[key].encode() # конвертувати str=>bytes
    dd = struct.pack('>' + str(length) + 's', bt_item)

    # записати у файл
    f.write(dd)

# 3.4. Закрити файл
f.close()

# ------------------------------------------------------
# 4. Читання записаного кортежу з файлу
# 4.1. Відкрити файл для читання у двійковому режимі
f = open('myfile11.bin', 'rb')

# 4.2. Зчитати кількість елементів (рядків) у файлі
dd = f.read(4) # Зчитати перші 4 байти - розмір типу int, d - упаковані дані
count = struct.unpack('>i', dd)[0] # count - кількість елементів у словнику

# 4.3.Сформувати пустий словник-результат
D2 = dict()

# 4.4. Цикл читання рядків
i = 0
while i<count:
    # 4.4.1. Зчитати ключ key - ціле число розміром 4 байти
    dkey = f.read(4)
    key = struct.unpack('>i', dkey)[0] # розпакувати дані

    # 4.4.2. Зчитати кількість символів у рядку
    dlength = f.read(4) # 4 - ціле число типу int
    length = struct.unpack('>i', dlength)[0]

    # 4.4.3. Зчитати рядок, попередньо потрібно сформувати
    # рядок формату sf
    sf = '>' + str(length) + 's'
    ds = f.read(length) # зчитати з файлу length байт
    sb = struct.unpack(sf, ds)[0] # розпакувати рядок
    value = sb.decode()

    # 4.4.4. Додати пару key:value до словника D2
    D2[key] = value

    i = i+1

# 4.5. Вивести словник D2 для контролю
print("D2 = ", D2)

# 4.6. Закрити файл
f.close()

Результат роботи програми

D2 = {1: 'Sunday', 2: 'Monday', 3: 'Tuesday', 4: 'Wednesday', 5: 'Thursday', 6: 'Friday', 7: 'Saturday'}

 


Зв’язані теми