Několik poznámek k Vaším pracem, které jste mi během posledního týdne poslali. Nejprve vzorové postačující řešení:
#include <stdio.h>
#include <math.h>
#include <locale.h> // kvůli češtině
int main(int argc, char **argv)
{
setlocale(LC_ALL,"UTF-8"); // počeštění
double a,b,c,d;
// nebudeme resit vstupy;
a=0;
b=26;
c=4;
if (a==0) {
if (b==0) {
if (c==0) {
printf("Toto není rovnice, je to rovnost\n");
return 0; // nema smysl pokračovat ve výpočtu
} else {
printf("Toto není rovnice, je to nerovnost\n");
return 0;
}
} else {
printf("Lineární rovnice: x=%f\n",(-c/b));
return 0;
}
} else {
d=b*b-4*a*c; // teprve tady má smysl něco počítat
if (d>=0) {
printf("Pravý kořen: %f\n",((-b+sqrt(d))/2/a)); // i tak lze dělit
printf("Levý kořen: %f\n",((-b-sqrt(d))/2/a));
return 0;
}
}
printf("Nemá řešení v oboru reálných čísel\n"); // je to divné, ale až sem se v jiné situaci nedostanete
return -1; // je to vlastně chybový výstup
}
Sledujte další instrukce. Na Facebooku můžeme diskutovat, paralelně poběží pracovní chat (asi phpfreechat nebo tox, to se ještě uvidí) a samozřejmě videa.
psáno předtím, než jsem četl vaše práce
Dovolil jsem si drobnou provokaci: neuvedl jsem, jaká data máte zpracovávat a ani jsem se neodkazoval na vaše předchozí schopnosti a znalosti. V podstatě jsem chtěl především otestovat vaši invenci a schopnost improvizace. Následuje vzorové řešení:
Využil jsem své rozsáhlé hudební sbírky, vyseparoval jsem metadata z nahrávek a sestavil soubor v podobě sady záznamů o šesti položkách:
Title;Artist;Album;Year;Length;Genre ...In The Doghouse;Dog Eat Dog;All Boro Kings;1994;349;Pop (Everything I Do) I Do It For You;Bryan Adams;The Best Of Me;1999;394;Rock (I Just) Died in Your Arms;Cutting Crew;The Best of Cutting Crew;2005;264;Pop (I'll Never Be) Maria Magdalena;Sandra;Lost and Found: 1979-1987;1985;234;Pop (Let Me Be Your) Teddy Bear;Elvis Presley;Elv1s 30 #1 Hits;2002;109;Rock & Roll (Now And Then There's) A Fool;Elvis Presley;Elv1s 30 #1 Hits;2002;162;Rock & Roll (Tag);Frankie Goes To Hollywood;Welcome to the Pleasuredome;1984;35;New Wave (White Man) In Hammersmith Pal;Clash;The Story Of The Clash - Volum;1988;242;Rock
a tak dále a tak podobně. Mihli jste vzít v podstatě libovolná CSV data nebo excelovou tabulku do CSV exportovanou. Zde je právě kvůli Excelu a jeho omezenosti jako separátor použit středník, tomu odpovídá i řešení.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
typedef struct mp3 {
char nazev[255];
char umelec[255];
char album[255];
unsigned int rok;
unsigned int delka;
char zanr[50];
struct mp3 *dalsi;
} MP3;
Takto je definovaná centrální struktura. Všimněte si, že je jen kopií záznamu v CSV plus odkaz zdánlivě na sebe samou, ve skutečnosti prostě prázdný ukazatel.
FILE * vstup; FILE * vystup; char * radek; size_t delka;
char * token;
int i;
MP3 * jeden, * prvni, * zobraz;
int main(int argc, char **argv)
{
size_t delkaradku=0;
setlocale(LC_ALL,"cs_CZ.UTF-8");
vstup=fopen("cviceni.csv","r");
fseek(vstup,0L,SEEK_END);
delka=ftell(vstup);
fseek(vstup,0L,SEEK_SET);
if ((radek=(char *) malloc(delka))==NULL) { printf("Malo pameti\n"); return -1; }
Proč alokovat tolik: je to krajní varianta, kdy je v souboru jediný řádek.
if ((prvni=(MP3 *) malloc(sizeof(MP3)))==NULL) { printf("Malo pameti\n"); return -1; }
jeden=prvni; // toto je identita, ne přiřazení!
while (delkaradku<2) {
if (fgets(radek,delka,vstup)==NULL) return -1;
delkaradku=strlen(radek);
}
Trochu komplikované, ale účinné: požere prázdné řádky na začátku souboru (pokud existují) a nespustí vlastní načítání dřív, než najde prcní validní data.
if (radek[strlen(radek)-1]=='\n') radek[strlen(radek)-1]='\0'; // vymaže konec řádku -- řetězec je pole plné délky vyplněné zprava nulami
token=strtok(radek,";");
for (i=0;i<6;i++) {
if (token == NULL) break;
switch (i) {
case 0: strcpy(prvni->nazev,token); break;
case 1: strcpy(prvni->umelec,token); break;
case 2: strcpy(prvni->album,token); break;
case 3: prvni->rok=atoi(token); break;
case 4: prvni->delka=atoi(token); break;
default:strcpy(prvni->zanr,token); break;
}
token=strtok(NULL,";");
}
Toto je druhý chyták a test uvažování: pokud má záznam pevnou délku, netřeba doplňovat prázdné položky. Tím si ušetříte spoustu práce. Jinak by se samozřejmě musela tokenizace dělat jinak, například do inicializovaného pole řetězců a teprve z něj kus po kuse testovat, zda není některá položka prázdná. V CSV to máte ošetřeno vlastní strukturou.
prvni->dalsi=NULL;
while (fgets(radek,delka,vstup)) {
if (strlen(radek)==1) continue;
if ((jeden->dalsi=(MP3 *) malloc(sizeof(MP3)))==NULL) { printf("Malo pameti\n"); return -1; }
jeden=jeden->dalsi; // TOTO JE TO ZÁSADNÍ MÍSTO! tím se přejde na další záznam
if (radek[strlen(radek)-1]=='\n') radek[strlen(radek)-1]='\0';
token=strtok(radek,";");
//printf ("na adrese %p: \n\n",jeden);
for (i=0;i<6;i++) {
if (token == NULL) break;
switch (i) {
case 0: strcpy(jeden->nazev,token); break;
case 1: strcpy(jeden->umelec,token); break;
case 2: strcpy(jeden->album,token); break;
case 3: jeden->rok=atoi(token); break;
case 4: jeden->delka=atoi(token); break;
default:strcpy(jeden->zanr,token); break;
}
token=strtok(NULL,";");
}
jeden->dalsi=NULL;
}
Tohle je poslední látka: je třeba mít deklarován první záznam a běžný záznam. pokud první ztratíte, ztratili jste hlavu hada a nenajdete ani jeden záznam. Obsah proměnné prvni je naprosto zásadní.
V tomto okamžiku je většina práce hotova: soubor byl po řádcích načten, řádky tokenizovány, tokeny zapsány do složek struktury, struktura uložena do paměti. Teď ji jen vypíšeme a binárně uložíme:
free(radek); // je to tak lepší, původní soubor může být opravdu veliký
fclose(vstup);
vystup=fopen("cviceni.bin","wb");
zobraz=prvni;
while (zobraz->dalsi!=NULL) {
fwrite(zobraz,1,sizeof(*zobraz),vystup);
Tady je prosím celý binární zápis. Jak bylo řečeno posledně: binární čtení a zápis se provádí párem příkazů fread/fwrite, kterým se zadává
Tedy: žádné konverze do dvojkové soustavy a podobné zbytečnosti, jen prostý dump všech fragmentů paměti, v nichž se nachází uložená data.
fflush(vystup);
printf("adresa: %p\nnázev: %s\numělec: %s\nalbum: %s\nrok: %d\ndélka: %d\nžánr: %s\nadresa dalšího záznamu: %p\n\n",zobraz,zobraz->nazev,zobraz->umelec,zobraz->album,zobraz->rok,zobraz->delka,zobraz->zanr,zobraz->dalsi);
zobraz=zobraz->dalsi;
}
fclose(vystup);
return 0;
}
Tento způsob ukládání textových dat je silně nehospodárný, jak lze vidět z fragmentu hexdumpu výstupního souboru:
2f1940 00 00 00 00 00 00 00 00 00 00 00 00 00 00 54 68 >..............Th< 2f1950 65 20 47 72 69 6e 63 68 20 57 68 6f 20 53 74 6f >e Grinch Who Sto< 2f1960 6c 65 20 43 68 72 69 73 74 6d 61 73 00 00 00 00 >le Christmas....< 2f1970 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 2f1980 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 2f1990 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 2f19a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 2f19b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 2f19c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 2f19d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 2f19e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 2f19f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 2f1a00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 2f1a10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 2f1a20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 2f1a30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 2f1a40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 2f1a50 d0 07 00 00 f7 00 00 00 53 6f 75 6e 64 74 72 61 >........Soundtra< 2f1a60 63 6b 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >ck..............< 2f1a70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 2f1a80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 2f1a90 50 c3 ca b9 14 56 00 00 57 68 65 72 65 20 44 69 >P....V..Where Di< 2f1aa0 64 20 59 6f 75 20 53 6c 65 65 70 20 4c 61 73 74 >d You Sleep Last< 2f1ab0 20 4e 69 67 68 74 00 00 00 00 00 00 00 00 00 00 > Night..........< 2f1ac0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 2f1ad0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
Všimněte si těch nul: to jsou ty nadbytečné délky jednotlivých řetězců ve struktuře. Pokud deklarujete 255 znaků, obsadí se 255 bajtů a jen se zleva vyplní textem, zbytek jsou nuly. To je to, co se děje při alokaci. Binární dumpy ovšem mají jednu podstatnou výhodu: dají se takřka v reálném čase dekomponovat na jednotlivé záznamy prostým uložením do paměti. To s textovými daty neuděláte.