TI:Zapis liczb do pliku

Z edu
brain.fuw.edu.pl/edu / TI > Zapis liczb do pliku
KL grafika.png KAPITAŁ LUDZKI
NARODOWA STRATEGIA SPÓJNOŚCI
Universitas Varsoviensis orzelek trans 116x128.png
UNIA EUROPEJSKA
EUROPEJSKI
FUNDUSZ SPOŁECZNY
European flag.svg
Projekt Fizyka wobec wyzwań XXI w. współfinansowany jest przez Unię Europejską ze środków Europejskiego Funduszu Społecznego w ramach Programu Operacyjnego Kapitał Ludzki.

Sposoby dostępu do plików - przypomnienie

  • Korzystanie z obiektu file:
napis = "42"
f1 = open('plik_tekstowy', 'w') # 'w' oznacza, że będziemy pisać do pliku
f1.write(napis)
f1.close()       # 'wypchnięcie' zmian do pliku przez jego zamknięcie

Zapisaliśmy do pliku napis "42".

Teraz chcemy zapisać do pliku liczbę 42. Aby zapisać do pliku coś innego niż napis, przy pomocy funkcji write(), musimy najpierw zrobić z tego czegoś napis. Z liczby całkowitej z zakresu 0-127, którą chcemy zapisać na 8 bitach, robimy napis używając funkcji chr().

Z bardziej skomplikowanych typów robimy napis korzystając z funkcji struck.pack() modul struct

  • korzystanie z modułu numpy:
    • do danych binarnych — fromfile
    • do danych ASCII — loadtxt
    • wczytywanie plików matlabowych — loadmat:

Podstawy o reprezentacji danych

  • Liczby naturalne są zapisywane w postaci ciągu zer i jedynek oddających ich reprezentację binarną
  • Liczby całkowite wymagają jeszcze określenia w jakiś sposób znaku, na przykład poprzez uznanie pierwszego bitu za znak. Dlatego też typy integer oraz unsigned integer mają różne zakresy wartości. Najczęściej stosowanym sposobem zapisu liczb ze znakiem jest tzw kod uzupełnieniowy
  • "Ułamki" wymagają już reprezentacji zmiennoprzecinkowej. Reprezentacja zmiennoprzecinkowa przedstawia się następująco:
x = ( − 1)SM⋅2E − bias gdzie S to bit znaku; liczba jest ujemna, gdy bit znaku jest równy 1, w przeciwnej sytuacji ma on wartość 0.

Typowe wartości przesunięcia (bias) dla koprocesora x87 (występującego w procesorach x86) wynoszą:

  • 127 (7FH) w formacie 32-bitowym
  • 1023 (3FFH) w formacie 64-bitowym
  • 16383 (3FFFH) w formacie 80-bitowym
Reprezentacja zmiennoprzecinkowa
  • Kolejność w jakiej odczytujemy kolejne bajty i składamy w liczbę zależy od architektury danego komputera:
    • little endian (spotykane także cienkokońcowość) to forma zapisu danych, w której mniej znaczący bajt (zwany też dolnym bajtem, low-order byte) umieszczony jest jako pierwszy. Procesory, które używają formy little endian, to między innymi wszystkie z rodziny x86. Jest ona odwrotna do używanego na co dzień sposobu zapisu liczb. Procesor zapisujący 32-bitowe wartości w pamięci, przykładowo 4A3B2C1D pod adresem 100, umieszcza dane zajmując adresy od 100 do 103 w następującej kolejności:
100 101 102 103
... 1D 2C 3B 4A ...
    • big endian (spotykane także grubokońcowość) to forma zapisu danych, w której najbardziej znaczący bajt (high-order byte) umieszczony jest jako pierwszy. Procesory, które używają formy big endian, to między innymi SPARC, PowerPC 970, IBM System/360, Siemens SIMATIC S7. Jest ona analogiczna do używanego na co dzień sposobu zapisu liczb. Procesor zapisujący 32-bitowe wartości w pamięci, przykładowo 4A3B2C1D pod adresem 100, umieszcza dane, zajmując adresy od 100 do 103 w następującej kolejności:
100 101 102 103
... 4A 3B 2C 1D ...
  • Przyjrzyjmy się reprezentacji liczb:
>>> y = struct.pack("i", 1)
>>> y
'\x01\x00\x00\x00'

Napis 0x.. oznacza zapis szesnastkowy jednego bajtu. Zapis 0xx_{1}x_{2} = x_{2} * 16^0 + x_{1} * 16. x_{i} należy do zbioru {0,1,...,9,a,b,...,f}, gdzie przyjmujemy:

  • a = 10
  • b = 11
  • c = 12
  • d = 13
  • e = 14
  • f = 15

Widzimy, że liczba 1 została zapisana na 4 bajtach = 32 bitach. Pierwszy bajt, bajt o najniższym adresie, jest najmniej znaczący, jak przystało na architekturę little endian — gdybyśmy chcięli ją zapisać na kartce na 32 bitach, zrobilibyśmy to odwrotnie — pierwsze 31 bitów to byłyby zera, dopiero ostatni byłby jedynką. Tak byłoby też w systemie Big Endian.

>>> import struct
>>> x = struct.pack("i", -1)
>>> x
'\xff\xff\xff\xff'

Przy zapisaniu liczby -1 musimy już w jakiś sposób uwzględnić znak. Widzimy, że nie jest tu stosowane zapisywanie znaku na pierwszym bicie, lecz kod uzupełnieniowy

>>> z = struct.pack("h", 1)
>>> z
'\x01\x00'

"h" oznacza typ short, zajmujący 16 bitów.

>>> chr(1)
'\x01'

chr() zapisuje liczbę na 8 bitach.

Zadania

  • Zapoznaj się z działaniem funkcji chr(), ord(), struct.pack/unpack, numpy.fromfile, bin(), int()
  • Utwórz plik file_string, do którego zapiszesz liczbę 42 poleceniem f.write('42'), oraz plik file_bin, do ktorego zapiszesz liczbę 42 jako integer korzystając z polecenia struct.pack("i",42). Otwórz plik edytorem tekstu. Jaka jest różnica i dlaczego?
  • Zapisz liczbę 42 do pliku, tak, aby zajmowała 64 bity w formacie little endian
  • Zapisz liczbę 42 do pliku, tak, aby zajmowała 64 bity w formacie big endian. Jaka będzie wartość takiej liczby zinterpretowanej jako little endian?
  • Zapisz liczbę 42 do pliku jako 64 bitową liczbę zmiennoprzecinkową.
  • Zapisz do pliku liczbę 0.1 jako double, korzystając z polecenia struct.pack("d",0.1). Następnie otwierając plik do odczytu binarnego, odczytaj wartość: znaku, wykładnika i mantysy (tzn napisz funkcję, która zwraca krotkę (znak, wykładnik, mantysa)). (pierwszy bit to znak, kolejne 11 bitów to wykładnik, kolejne 52 bity to mantysa (Liczba zmiennoprzecinkowa))
  • Utwórz plik o nazwie "binarny", otwórz go do zapisu i zapisz sekwnencję bajtów oznaczającą zdanie "Hello world". Psłuż się do pomocy tablicą ASCII z wikipedii. Otwórz plik edytorem tekstowym i zwróć uwagę na to, że nie widzisz tekstu, tylko "krzaczki". Otwórz następnie powyższy plik do odczytu w formie binarnej, i zobacz co się dzieje, jak korzystasz z funkcji fromfile podając jej różne typy (np. int8, int16, int32).
Osobiste