Stocarea datelor
Last Updated: Aug 15 2013 13:09, Started by
dani.user
, Apr 29 2013 17:43
·
0
#1
Posted 29 April 2013 - 17:55
Reprezentarea datelor
În IT lucrăm cu date însă cum sunt reprezentate aceste date? Cum ajungem de la 10010010101010101001 la documente, imagini sau chiar muzică? În viața de zi cu zi folosim numere în baza zece, numere ce sunt la rândul lor compuse din cifre. Baza zece înseamnă că avem 10 cifre disponibile (0, 1, 2, 3, 4, 5, 6, 7, 8, 9). Când dorim o valoare mai mare decât valoarea cifrei maxime (9), alăturăm mai multe cifre (de exemplu 29 e compus din două cifre: 2 și 9). Dacă prin folosirea unei singure cifre putem reprezenta 10 valori distince, prin folosirea a două cifre putem reprezenta 102 valori distincte (0-99), prin folosirea a trei cifre, 103 valori distincte (0-999) etc. Vedeți legătura? Ei, IT-ul nu știe să lucreze cu baza zece (jos, la nivel electronic), așa că face uz de o altă bază, cea mai simplă, baza 2. În baza 2 nu mai avem zece valori (0-9) ci doar două (0-1). Pentru a reprezenta valori mai mari, se alătură mai multe valori de 0-1, astfel încât: prin alăturarea a două valori se pot reprezenta 22 = 4 valori distince (00, 01, 10, 11), prin alăturarea a trei valori se pot reprezenta 23 = 8 valori distince (000, 001, 010, 011, 100, 101, 110, 111) etc. Unitatea de bază în IT este deci bit-ul (0 sau 1). Când lucrați însă cu memoria (RAM sau cea pe disc), nu veți putea lucra în mod direct chiar la nivel de bit; nu veți putea cere sistemului să va aloce 5 biți memorie, sau crea un fișier pe disc ce stochează doar 3 biți de informație. În viața de zi cu zi (de programatori) veți lucra cu un multiplu al bit-ului și anume octetul (byte). 1 byte = 8 biți Un byte poate stoca 28 = 256 valori distince (0-255), iar atunci când se alocă memorie sau se scrie ceva pe disc, se vor scrie n bytes (n număr natural nenul). În C pentru a stoca o valoare între 0 și 255 se va folosi unsigned char. unsigned char a = 15; unsigned char b = 105; Quote Dar dacă vreau să stochez o valoare negativă? Având la dispoziție 256 valori distincte ce pot fi stocate într-un byte, putem considera că jumătate dintre ele ar fi pozitive, iar jumătate negative. În acest caz apare însă nevoia unei convenții: noi hotărâm, de exemplu, că valorile 10000000 - 11111111 ar reprezenta valori negative, însă trebuie ca și ceilalți (cărora le dăm datele) să respecte aceeași convenție pentru a ne putea întelege. O astfel de convenție există deja și se numește complement de doi și ne permite ca, folosind 1 byte (8 biți), să stocăm 128 valori pozitive (0-127) și 128 valori negative (-128 - -1): char a = 15; char b = -115; Foarte des se face uz și de reprezentarea datelor utilizând baza 16: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F. O cifră în baza 16 putând reprezenta 16 valori distincte, pentru a reprezenta cele 256 stocabile într-un byte e nevoie de doar două cifre în baza 16 (162 = 256): 00 - FF. Pentru a preciza în C o valoare în baza 16 se va folosi prefixul "0x". unsigned char b = 0x69; (echivalent cu unsigned char b = 105;) De regulă, atunci când specificăm o valoare în sistem zecimal (b = 105), compilatorul o consideră drept valoare cu semn, iar când specificăm valoarea în sistem hexazecimal, compilatorul e sigur ca dorim o valoare fără semn. În memorie, valoarea 105 va fi reprezentată drept 01101001 indiferent că-i zicem b = 105 sau b = 0x69. Quote Bun, și dacă vreau sa stochez o valoare mai mare, gen anul 2013 Când un byte nu-i destul... mai adăugăm unul . Doi octeți (16 biți) vor putea stoca 216 = 65536 valori distincte: 0-65535 dacă se lucrează fără semn, respectiv -32768 - 32767 dacă reprezentăm valori cu semn. Short se va folosi în acest caz în C: short a = 2013; unsigned short b = 0xFFFF; Utilizând mai mult de 1 byte pentru a stoca o valoare, apare o dilemă: dacă vreau să stochez valoarea 1 o stochez drept 00000001 00000000 sau 00000000 0000001? Pentru mai multe detalii despre aceasta problema, a se studia Endianness. endian Quote Dar dacă vreau să stochez valoarea 100000? La prima vedere ați zice că s-ar folosi un tip de date pe 3 bytes, însă (deoarece 3 nu e o putere a lui 2) un acest tip de date nu există (evident s-ar putea emula folosi un array de 3 char, de exemplu, dar nu-i optim). Se va folosi un tip de date pe 4 bytes (int în cazul unui sistem pe 32 biți), ce permite stocarea a 232 = 4294967296 valori distincte. Dimensiunea tipurilor de date implicite nu este constantă, ci diferă funcție de sistem (tip de procesor). Pentru a verifica pe câți bytes este stocată valoarea dintr-un anumit tip de date, se poate folosi sizeof: int *a = (int*)malloc(10 * sizeof(int)); //aloc memorie pentru 10 valori int, fără să țin eu minte cât ocupă fiecare Quote Dacă vreau să stochez mai multe valori? Pentru a stoca mai multe valori, pe lângă evidenta soluție a declarării mai multor variable, mai există două soluții facile:
Interpretarea datelor Quote Dacă vad, de exemplu, valorile hexa 14 1D 20 CC EF AC A0 A9, de unde stiu ce reprezintă? De unde stiu că sunt două int-uri fără semn și nu un vector de 4 short fără semn? Răspunsul e simplu: nu știu. Atunci când cineva îmi oferă niște date (fie pe hârtie, fie pe un suport digital), acesta trebuie să-mi pună la dispoziție și modul în care ar trebui să interpretez acele date. În continuare vreau să prezint un exemplu mai practic: cum citesc dimensiunile unei poze .bmp? Fie poza .bmp din atașament. Dacă o deschid cu orice soft de vizualizare imagini, acesta îmi va arăta ca e vorba de o imagine de 1025x520 pixeli. Cum a citit însă programul acestă informație? Dacă deschid cu un editor hex fișierul în cauză, nu înțeleg mai nimic din ce-i acolo: Quote
42 4D 56 68 18 00 00 00 00 00 36 00 00 00 28 00 00 00 01 04 00 00 08 02 00 00 01 00 18 00 00 00 ... După ce încep însă să mă documentez despre structura unui astfel de fișier (http://en.wikipedia....BMP_file_format http://msdn.microsof...6(v=vs.85).aspx), se limpezesc apele. Headerul care mă interesează începe după primii 14 bytes: Quote
28 00 00 00 01 04 00 00 08 02 00 00 01 00 18 00 00 00 ... Urmează apoi un DWORD (4 bytes) ce reprezintă biSize, iar apoi urează cele două valori ce mă interesează (width și height), fiecare stocate tot pe căte 4 bytes. Astfel Width: 01 04 00 00; Height: 08 02 00 00. Tinând cont că sistemele de acasă folosesc little endian, cele două valori vor fi deci Width = 0x00000401 = 1025, Height = 0x00000208 = 520; adică exact cât indică și programele dedicate. Attached Files |
#2
Posted 29 April 2013 - 18:43
Stocarea șirurilor de caractere
Am văzut cum pot fi reprezentate numere în memoria calculatorului, iar acum se pune problema: cum reprezint o literă, un cuvând, o propoziție? Să o luăm încet și să vedem mai întâi cum putem reprezenta o literă. Varianta cea mai simplă ar fi asocierea fiecărei litere cu un număr: A = 0, B = 1, C = 2, etc. Cum, în alfabetul latin, nu avem decât câteva zeci de litere, un tip de date char ar fi suficient pentru a reprezenta o literă. Pentru mai multe litere, ar fi nevoie evident de un vector de char. Quote Atât, asta-i tot? Credeam că-i mai complicat... E... acesta a fost doar vârful iceberg-ului . Ca în cazul oricărei convenții, și în acest caz, prima problema de care ne-am lovi ar fi că, atunci când am transmite aceste valori unui alt sistem/program, acesta ar trebui să știe să le interpreteze în același mod, și nu, de exemplu, să considere Z = 0. Soluția la această problemă este să nu începem să inventăm noi corespondențe între litere și cifre, ci să le folosim pe acelea inventate acum mulți ani de alții și devenite standard (a auzit toată lumea de ele). Un astfel de standard este ASCII și, precum se vede în tabelul de pe site, folosește valori între 0 - 127 pentru a stoca diverse simboluri, printre care litere mici, mari, cifre, semne de punctuație etc. O astfel de valoare încape deci într-o variabilă pe 8 biti, de tip char (ne amintim că aceasta poate stoca o valoare între -128 și 127). Quote Bun, dar cum folosesc acest standard ASCII, stau cu tabelul în față și scriu cod de genul char a = 65? Nicidecum. Aici limbajul C/C++ ne vine în ajutor, și ne permite să scriem valoarea unui caracter între ghilimele simple, compilatorul știind apoi automat, pe baza standardului, despre ce valoare numerică e vorba. char a = 'A'; /* este echivalent cu */ char b = 65; Quote Acum că știu cum se stochează o literă, cum stochez un cuvânt întreg? Dacă un char stochează o literă, un vector de char va stoca un cuvânt întreg. char cuvant[6] = {'C', 'u', 'v', 'a', 'n', 't'}; Se observă înșă imediat că această variantă nu este tocmai comodă: trebuie scris fiecare caracter separat, trebuie aflat numărul total de caractere, etc. Soluția constă în scrierea întregului șir de caractere (cuvântul în acest caz) între ghilimele duble. char cuvant[] = "Cuvant"; char *cuvant = "Cuvant"; O diferență foarte importantă, și relativ ascunsă, între aceste două variante (declararea unui vector VS unui pointer) este că, în cazul variantei a 2-a, programul va crăpa dacă se încearcă modificarea vreunui caracter din zona respectivă de memorie. Pentru a evita acest lucru, cel mai bine înlocuim varianta a 2-a cu: (astfel compilatorul va da eroare dacă încercăm c[0] = 'c') const char *cuvant = "Cuvant"; O problemă s-a rezolvat (nu mai trebuie scris fiecare caracter în parte între ghilimele), însă mai rămâne una: de unde știm lungimea caracterului? Atunci când scriem o funcție ce procesează un vector de întregi, îi specificăm cumva și dimensiunea acelui vector (să știe când să se oprească), însă, în cazul unui și de caractere, ar fi cam incomod să tot păstrăm & transmitem alături câte o variabilă conținând dimensiunea. Soluția la această problemă constă în stocarea la finalul șirului de caractere a valorii 0. Nu cifra '0', ci valoarea 0. Astfel, când scriem char cuvant[] = "Cuvant"; defapt compilatorul întelege char cuvant[7] = {'C', 'u', 'v', 'a', 'n', 't', 0}; Astfel, fiecare funcție din librăria standard C (și nu numai), când parcurge valoare cu valoare șirul de caractere, se va opri când întălnește valoarea 0. Dacă șirul nu se termina cu acea valoare, se va citi o zonă interzisă din memorie, iar programul va crăpa. Această metodă nu este lipsită și de dezavantaje, determinarea lungimii șirului fiind o operație foarte costisitoare în cazul șirurilor lungi. Înainte de a trece mai departe, vreau să atrag atenția că există o diferență între a stoca un șir de caractere în memorie și a-l afișa pe ecran. Pentru a-l afișa pe ecran, sistemul trebuie să știe și cum să deseneze efectiv forma fiecărui simbol, iar unele valori au o semnificație specială (de exemplu spațiul sau rând nou). Quote M-am uitat peste tabelul asta, dar nu văd niciunde cum scriu cu diacritice? Aici începe să se complice treaba... Da, 127 de valori sunt suficiente atunci când scriem în engleză, dar, pe măsură ce dorim alte caractere, ajungem să simțim primele lipsuri. Quote Dar stai, char stocheaza defapt 256 de valori, am putea folosi celelalte 128 pentru a memora diacritice Întradevăr, aceasta este o posibilă soluție, dar se observă și în acest caz foarte repede limitările. Pentru a scrie un text cu alfabet chirilic poate fi ok, pentru a scrie unul cu litere grecești, din nou, dar, pentru a scrie un text combinat, iar nu mai ajung caracterele. Următoarea abordare e din nou una destul de logică: daca 8 biți n-ajung, hai să folosim 16. Astfel un caracter nu mai este stocat folosind char, ci folosind un short (pentru care s-a introdus wchar_t drept sinonim). Astfel s-ar putea reprezenta 65535 valori (mult mai bine, avem de unde alege), finalul șirului fiind marcat tot cu o valoare 0 (în acest caz însă un 0 reprezentat pe 16 biți, adică echivalentul a două valori char ambele 0). Compilatorul ne permită să întroducem un astfel de șir de caractere utilizând prefixul L. wchat_t a = L'Î'; wchat_t cuvant[] = L"Cuvânt"; Biblioteca standard C pune la dispoție funcții pentru a lucra și cu astfel de șiruri de caractere, ele având nume similar cu cele pentru șiruri ASCII: de exemplu wcslen() în loc de strlen(). Această abordare este una frecvent utilizată (de exemplu de Windows, intern), însă prezintă și ea un mare dezavantaj: dacă majoritatea caracterelor folosite sunt cele uzuale, ASCII, se irosește spațiu, memoria ocupată fiind dublă. Dacă numărăm toate caracterele cunoscute, ajungem la concluzia că numărul acestora depășeste 65535, și am avea nevoie deci de o reprezentare pe 4 bytes (int) pentru fiecare. În acest caz însă, dacă majoritatea caracterelor folosite sunt cele ASCII, s-a ocupa de 4x mai multă memorie decât ar fi necesar. Mai există încă o abordare, ce încearcă să elimine dezavantajele stocării folosind 16 biți, și anume stocarea variabilă folosind encoding-ul UTF-8. UTF-8 folosește 1 byte (8 biți) pentru stocarea caracterelor regăsite și în tabelul ASCII, și mai mulți bytes pentru celelalte caractere. Astfel un text scris în limba română, ce mai are pe ici pe colo câte ceva diacritice, va ocupa un spațiu mediu mult mai apropiat de 1 byte/caracter, decât de 2. Un text însă scris în chineză, va ocupa, în medie, mai mult de 2 bytes/caracter, soluția fiind astfel mai puțin eficientă decât varianta utilizării wchar_t. Un alt dezavantaj al utilizării UTF-8 este faptul că, pentru a citi, de exemplu, caracterul al 100-lea, nu putem simplu folosi text[99], deoarece se poate ca respectivul să fie doar al 80-lea caracter, înaintea lui fiind poziționate caractere ce necesită mai mulți bytes. Multe biblioteci operează cu șiruri de caractere în format UTF-8, așă că trebuie stăpânit și lucrul cu acestea. |
#3
Posted 29 April 2013 - 19:28
De la stocarea arbitrară într-un fișier la stocarea într-o bază de date SQL
Persistența datelor Până acum am văzut cum sunt ținute datele în memoria RAM a calculatorului, însă această memorie are un mare dezavantaj: este volatilă. Adica informațiile se pierde atunci cand memoria nu mai este alimentată (calculatorul este oprit). Cum majoritatea datelor trebuie păstrate și după ce oprim calculatorul, s-au inventat diverse dispozitive de stocare ce păstrează datele și atunci când nu sunt alimentate cu energie electrică: harddisk, dischete, stick-uri usb etc. Scrierea datelor în fișiere Majoritatea celor care au scris ceva C au ajuns destul de repede să scrie și în fișiere, utilizând celebrele funcții fopen(), fprintf(), fwrite(), etc. Deasemenea s-a observat că există două mari abordari: fișiere text sau fișiere binare. În continuare dorim să scriem următoarele date despre 3 elevi: nume, prenume, varsta.
Până acum totul pare simplu și am fi tentați să credem că "that's all there is to it", însă următoarele întrebări ne vor pune pe gânduri și vom realiza cât de ineficientă este defapt această abordare. Considerăm că avem vreo 10.000 înregistrări
Rumegând puțin la aceste întrebări constatăm că soluțiile posibile (având în vedere actuala structură a datelor) sunt foarte ineficiente și presupun fie analizarea înregistrare cu înregistrare fie chiar rescrierea întregului fișier. Soluția Soluția în acest caz o reprezintă utilizarea unui SGBD (sistem de gestiune a bazelor de date) și a limbajului SQL. SGBD-urile acționează ca un intermediar între noi și datele efective. Ele stochează datele sub formă de tabele iar noi înteracționăm cu ele prin interogări: le cerem să creeze un anumit tabel cu o anumită structură, să insereze anumite date în tabel, să ne ofere datele din tabel, etc. Un astfel de SGBD este SQLite, ajuns în prezent la versiunea 3.7.17 și disponibil pentru majoritatea platformelor. SQLite stochează datele într-un singur fișier pe disc și ne pune la dispoziție două variante pentru a interacționa cu acest fișier (nu, nu deschidem manual fișierul cu fopen și nici nu ne interesează structura sa internă, e treaba SGBD-ului cum își gestionează intern datele):
Crearea primei baze de date SQLite În continuare vom folosi utilitarul sqlite3(.exe) pentru a crea o bază de date conținând aceleași informații despre elevi ca mai sus.
Aceste exemple arată cât de flexibile pot fi interogările SQL. Pentru învățarea & aprofundarea acestui limbaj de interogare, un punct de pornire ar fi http://www.w3schools.com/sql/ . Accesarea datelor dintr-o aplicație C Va urma |
#6
Posted 14 June 2013 - 17:12
Salut!
Am facut niste investigatii si nu pot intelege de ce toate valorile din BMP sunt stocate in big endian. Am creat chiar un BMP cu GIMP, ca deh, am zis ca e dani.user mai cu mot, dar nu, si in BMP-ul meu toate valorile sunt stocate in big endian. biWidth, biHeight, biPlanes campurile astea le-am verificat, dar si "the size of the BMP file in bytes" (al doilea camp din Bitmap File Header) este tot big endian, ceea ce e putin aiurea, deoarece pe wiki zice despre bitmap file header: Quote All of the integer values are stored in little-endian format (i.e. least-significant byte first) Ca sa ma conving m-am uitat in sursa GIMP si spre surpriza mea am gasit o functie care face conversia din little in big endian: https://git.gnome.or...bmp-write.c#n81 Iar functia este este folosita ca sa scrie dimensiunea aia in bitmap file header despre care pe wiki se zice ca ar trebui sa fie little endian: https://git.gnome.or...mp-write.c#n431 Deci, da, totul e big endian, dar de ce? De ce sa te complici cu conversie in big (la scriere) si cu conversie din big in little (la citire) cand poti sa scrii direct in little si sa citesti direct in little? E ceva ce mi-a scapat mie? Avand in vedere ca si pe windows (deduc asta din imaginea lui dani.user) si pe linux se comporta la fel, banuiesc ca e un fel de conventie, dar de ce? |
#7
Posted 14 June 2013 - 20:24
In primul rand, functia aia FromL transforma un long de 4 bytes in format little endian. Daca te-ai uita mai atent ai vedea ca octetii mai putini semnificativi sunt pusi in fata.
Si in al doilea rand, endianess-ul nu este dat de sistemul de operare, ci de hardware. Edited by edy_3dz, 14 June 2013 - 20:26. |
#8
Posted 14 June 2013 - 21:03
@edy_3dz, multumesc, m-ai facut sa ma uit mai atent, mi-am dat seama ce greseam.
Eu ma uitam la fisierele alea si ziceam: "uite ma ca sunt big endian, ce porcarie", cand defapt erau little endian. Cu ipoteza aia gresita am plecat si am tinut-o exact invers pe tot parcursul problemei, de acolo si impresia ca FromL transforma in big endian si vazusem si FromS si am zis: asta face un short in big endian si cealalta un long in big endian. LE: dani.user, keep up the good work, nu am citit foarte atent, dar imi place ca am vazut ceva practic acolo (chestia cu BMP-ul pe care am explorat-o si eu). Edited by Paullik, 14 June 2013 - 21:19. |
#9
Posted 16 June 2013 - 20:42
In primul rand vreau sa iti multumesc in numele meu si in numele celor care nu o fac pentru aceste minunate lectii de programare . Imi super plac si au si un pi de simt al umorului in ele .
In al doilea rand vin cu o rugaminte: Ce spui , ne faci si noua un tutorial despre bazele de date relationale ? |
#10
Posted 16 June 2013 - 21:36
Mersi de aprecieri.
Un tutorial despre bazele de date relationale n-am deocamdata in plan, doar ce prezint aici despre cum interactionezi cu ele din C. |
|
#11
Posted 17 June 2013 - 19:45
Ok. Mersi pentru raspuns(raspunsuri) si pentru ceea ce faci pentru aceasta comunitate numita softpedia
|
#12
Posted 15 August 2013 - 13:09
Să mai revigorez acest topic, propun următorul PROIECT cu rolul de a vă plimba prin mai multe domenii, de a vă arată ce înseamnă lucrul cu date. El are deasemenea rolul de a vă introduce în diverse tehnologii și de a arată că acestea sunt importante, nu atât limbajele folosite.
O parte din cerinte se pot rezolva folosind ce am prezentat deja aici, altele cu ce s-a arătat pe alte topicuri ale acestei arii, iar altele necesită încă ”puțintică răbdare” sau studiu pe cont propriu. |
Anunturi
Bun venit pe Forumul Softpedia!
▶ 0 user(s) are reading this topic
0 members, 0 guests, 0 anonymous users