C: fseek egyet előre, egyet vissza

Fórumok

Sziasztok.

http://pastebin.com/m625db68f (C forrás)
http://pastebin.com/m29439b20 (egy plaintext file)

A program minden sorra megmondja, hogy számtani sor-e.
Tudom, nem elegáns es béna, de legalább működik, majd még finomítok rajta. Lécci szorítkozzunk a kérdésemre:

Az, hogy itt:

/*
A file adott, de lehet hogy van egy ures uj sor a vegen, ezt vedjuk ki

fseek() <--- ugorjunk 1 charnyit, az lehet hogy eof, es akkor break;
fseek() <--- ugorjunk ugyanannyit vissza;
*/

Ezt hogy tudnám lekódolni? RTFM-eztem az fseek-et, de nem nagyon értettem. Amíg magától pozícionálódik a file stream, addig jó vagyok, de amint nem, elbizonytalanodom. Szóval egy charnyit előre, megnézzük hogy EOF-e, és ha igen, breakeljük az egész fgetc-s ciklust, ha nem az, akkor valódi sor, fseek vissza, nem breakeljük.

A válaszokat köszi előre is!

Hozzászólások

Hello!

Szerintem neked az ftell() lesz a barátod. Evvel le tudod kérdezni a jelenlegi poziciót és utána az fseek-el vissza tudsz egyet lépni.
Üdv.

csak egy tipp:

man fgets
man fscanf

illetve nem a c a legjobb nyelv ehhez, de ezt te tudod

"breakeljük"

Sztem ne használj valami break vagy continue -t sulis feladatban, mert a mostani tanárok 99%-a legalább annyira utálja, mint a goto-t. Nekem mondjuk nincs vele bajom, ésszel lehet azt is használni, de mindegy.

Viszont nem lenne egyszerűbb egy bufferbe beolvasni az egész fájlt aztán ott matatni vele?

----------------
Lvl86 Troll

Hogy a konkrét kérdésedre válaszoljak: fseek() harmadik paramétere a barátod: fseek(fptr, -1, SEEK_CUR)

Viszont ha egy hallgatóm olyan programmal jönne oda hozzám, ami ciklusban minden karakter beolvasása után seekel egyet oda, egyet vissza, akkor nagyon gyorsan elhajtanám.

-1
Bár abban egyetértek, hogy talán szebb, ha nem seekel jobbra-balra, de én ezt maximum esztétikai dolognak tartom. Lehet jobbra-balra seekelés nélkül csúnyábbat írni, és nem is nehéz.
Miért fájna annyira egy seek oda-vissza? Oprendszer úgyis "bekesseli", nem olyan, mintha ide-oda tologatná a vincseszteren a fejet.
De ha a "csúnya" és a "szép" megoldás végrehajtási ideje között nincs nagyságrendbeli különbség, és a "csúnyában" is világosan (és követhetően) látszik a logika, akkor túlzásnak tűnik az a "nagyon gyorsan elhajtás".

Helyesség eldönthető, hatékonyság viszonyítható, gyorsaság = hatékonyság.
És lehet csúnya forrást írni, ami helyes és gyors.
Meg lehet szépet, ami olvasható és karbantarható. Ha csak szorzóval lassabb, s nem nagyságrenddel, akkor én személy szerint bármikor az utóbbival dolgozok szívesebben, s elég sok esetben még a nagyságrendi sebességkülönbség is beleférhet. Nyilván adott helyzettől függ.

A szépségnek nincs definíciója. Festményeken sem. De azért bárki el tudja dönteni egy képről, hogy az szép vagy sem. Ez szubjektív ugyan, mégis sok kép esetén van közös megegyezés.

A kód szépségénél is hasonló a helyzet. Aki már programozott valamennyit, az látja ezen a megoldáson, hogy csúnya. És nem csak azért csúnya, mert a többség azt mondja, hanem azért is, mert maga a szerző is így érzi, vagy ha most nem is, így fogja egy fél év múlva. :)

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

> Oprendszer úgyis "bekesseli"

Tudod az operációs rendszer külön taszkban fut.

Amikor te meghívsz valamit az operációsrendszerben, annak 2 context switch lesz az eredménye, ami lassú (egyszer az oprendszerre vált, egyszer meg vissza).

A context switch ugye nem túl lassú, de ha egy 200.000 soros fájlt olvasol be bájtonként, akkor garantált, hogy a programod context switch-ekkel fog foglalkozni (pontosan 400.000-rel), nem pedig a betöltéssel.

Én már gimnáziumban is tanultam, hogy nem olvasunk be fájlt bájtonként, pedig az még nem a tudományok csúcsa.

Ne haragudj, nem tudtam, hogy az operációs rendszer külön taszkban fut. Nyilván nem egy gimnáziumban tanultunk. Köszönöm, hogy kioktattál.

Félretéve a személyes hangvételt:
itt alapvetően nem a pufferelt olvasás és a karakterenkénti olvasás közötti különbségről volt szó, hanem a karakterenkénti olvasgatás és a "karakterenkénti olvasgatás ráadásul oda-vissza seekelés" közötti különbségről.

Az előttem szóló azt írta, hogy az fseek -1,+1 miatt hajtaná el, ráadásul nagyon gyorsan a diákjait. Én ezt túlzó reakciónak gondoltam, ennyi. Talán mert eddig csak diák voltam még, s a másik oldalon még nem.

Megértem, hogy nehéz, de vannak dolgok, amiket meg kell tanulni és meg kell érteni.

Tudod, nap mint nap olyanokkal küzdök, hogy van egy 1 millió soros kód, ami valahol lassú.
Napjaim mennek el néha, hogy megtaláljam miért és idegesítő, hogyha kiderül, hogy alapvető dolgokról van szó.

Ami az oktatást illeti, sajnos aki jó informatikus az nem valószínű, hogy tanárnak megy, az alacsony fizetés miatt. Amikor a tanár a fájl beolvasását tanítja, kötelessége lenne elmondani, hogy nem egyesével olvasunk. Ez olyan, minthogy mielőtt osztunk, ellenőrizzuk, hogy a bemenet nem nulla-e. Nyilván, aki sosem dolgozott informatikusként, azt nem zavarja, csak a mi szemünket bántja, ha érdemi munka helyett a gép marhaságokkal foglalkozik.

Ami a formát illeti, mérnök vagyok és szeretem az igényes munkát.
Valamilyen stílust fel kell venni és azt használni. Az 1 space-es igazítás szokatlan, de ha következetesen használod, akkor nem szólok érte semmit.

A karakterenkénti olvasás és a seek-elés nagyon közel áll egymáshoz. A seekelés is drága, ezért ha lehet kerülni kell. Ha egyenletesen beolvasol egy fájlt, az sokkal gyorsabb, mintha ide-oda ugrálnál. Arról nem is beszélve, hogy 5 karakter miatt nem ugrunk vissza a fájlolvasás pozíciójával, mert azt az 5 karaktert el tudjuk a memóriában is tárolni, ami több mint 10000-szer gyorsabb, mint az fseek.

Edes istenem... manapsag mar az osszes olvasofuggveny (read, fread, fscanf, etc) kepes jelezni visszateresi ertekben, hogy hulyeseget (EOF) kapott. Ezen felul van erre rendes errno is azt hiszem (most meg nem mondom, micsoda). Seekelgetni szerintem is akkora okorseg mint ide Lachaza. Felesleges, ertelmetlen, a rendszer ezt sokkal jobban tudja nalad, hidd el. Probald meg jobban kihasznalni a rendszer nyujtotta lehetosegeket, ez nem oprendszer, csak beadando.
--


()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

Az fgetc() int-et ad, nem veletlenul. A char c; nek is int-nek kene lenni.
==
`Have some wine,' the March Hare said in an encouraging tone.
Alice looked all round the table, but there was nothing on it but tea.

Ha textfeldolgozás:

while(!eof(file_pointer))
{
memset(buffer,0,buffer_length); // elvileg ez nem kell, én szoktam
fgets(buffer, max_line_lenght,file_pointer);
sorfeldolgozo(buffer);
}

megjegyzés: a sorvégi '0a' karakter bent lesz a buffer-ben (kivéve, ha az utolsó sor végén nincs 0a), azt egy "\0" fogja követni. A text hossza a strlen(buffer); fv-nnyel kérdezhető le.

Szerintem... De sok más technika is létezik. Pl, ha a
feldolgozandó anyag nem biztos, text, akkor fix méretű
blokkokba célszerű a fájlt beolvasni fread()-del.

> Sol omnibus lucet.

Az fgets() nem jó ötlet. Ha jól látom, akkor 5-ösével vannak a számok a sorokban, ezeknek a feldolgozásához nincs szükség rá. Ha akármennyi szám lehet egy sorban, akkor megint nem jó ötlet, mert korlátozza a feldolgozható sorok méretét, miközben a számok ellenőrzését végző algoritmus korlátlanul sok számmal is elbírna.

Szóval az fgets() sehogy sem illeszkedik a feladatokhoz.

(1) Ha ötösével vannak a számok akkor strtok()-t kell használni
a sorfeldolgozóba.

(2) ha textfájlt dolgozunk fel - értsd: értelmesen formázott
olvasható szöveget, akkor sejthető, hogy, mondjuk 100-150
karakternél nem lesz hosszabb ez a sor, különösen igaz ez
akkor, ha a textfájlt számítógépes program generálta. Egyébként
is, semmi nem gátol meg senkit abban, hogy a buffer mérete
4kB vagy 32 kB legyen. Az fgets(); nem okoz overflow, legfeljebb
nem lesz bent a bufferben a '0a' karakter abban az esetben, ha a
sor hossza > sizeof_buffer

Hozzátenném még, hogy 1 bájt beolvasásakor _NEM_ egy bájtot
ránt be a rendszer, hanem egy állitható méretű buffert
teljesen megtölt (a default méret 4096 bájt). Már csak ezért
is hülyeség (elnézést) a bájtonkénti olvasás. Tessék írni
egy normális szövegfeldolgozót, gyorsan megvan és nagyon
sok helyen használható. Én a saját programjaim konfigfájljait
mindig így dolgozom föl, ahogy azt a fenti példában mutattam.

(Tudom: élő kövület, meg ilyenek...)

> Sol omnibus lucet.

> (1) Ha ötösével vannak a számok akkor strtok()-t kell használni a sorfeldolgozóba.

Elég egy darab fscanf().

> sejthető, hogy, mondjuk 100-150 karakternél nem lesz hosszabb ez a sor

Ez egy gyakorlást célzó feladat, nem árt ha megcsinálja úgy is, hogy nem sejt előre semmit a sorméretről.

Akkor pedig read()-el olvastatok be, es figyelem a beolvasott adatok mennyiseget. De *eof() fuggvenyeket tuti nem hasznalok, mert az vagy jot ad vissza, vagy nem. Aztan a \n karakterek menteni sorokra bontassal mar lehet szopni, de az mar mas kategoria.

Mondjuk a *scanf() fuggvenyek nekem se jonnek be. Lehet utni erte.

A masik: igenis lehet dolgokat feltetelezni - megpedig az esszeruseg menten. Nyilvan nem botrankozik meg senki azon, ha azt feltetelezed, hogy nem fogsz 4k-s sorokat olvasni, hiszen ez egyebkent is eleg esszerutlen dolog. Van amit a feladat nem mond el, es neked kell kitalalni a ra vonatkozo szabalyokat. Persze, meg lehet oldani mindent ugy is, hogy az osszes elkepzelheto esetre felkeszulsz, csak az a baj, hogy ezt vajmi kevesse ertekelik, cserebe egy csomo idot elversz vele.
--


()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

Az eof fv-nyel akkor van "baj", ha az előző olvasás pont
az utolsó byton ért véget. Ekkor úgy tűnik, mintha nem
lennénk a fájl végén (hiszen nem akartunk még túlolvasni
rajta). ebben a kivételes esetben van egy olyan olvasás
a ciklusban, amely 0 bytot olvas be (a puffer egy üres
string lesz, egyúttal az eof flag bebillen).

Lehet azt is csinálni, hogy a fájlolvasás kezdetén
meghatározzuk a file méretét (fseek a fájl végére és ftell)
és célzottan annyi bájtot olvasunk be, amekkora a fájl.
Szöveg feldolgozásánál, főleg, ha soronként van tagolva,
ez kicsit macerás, mert keresgélni kell a sorvég_karakter
(amiből ráadásul több is lehet egy-egy berántott blokkban.)

Kombinálhatjuk a dolgokat azzal, hogy fgets()-szel
olvasunk és a fájlméretből mindig levonjuk a beolvasott
string méretét.

Szerintem...

> Sol omnibus lucet.

En a dolgot joval egyszerubben latom.
Csinalunk egy puffert, ami adott X meretu. ebbe olvasunk a read-dal, osszeszamoljuk benne a \n karaktereket, majd strtok-kal elkezdjuk kiolvasni a sorokat. Mivel ugye csak az elozoleg kiszamolt mennyisegu sort tudjuk beolvasni, igy van egy felso korlat. Ha kifutottunk a beolvashato sorok szamabol, akkor ujabb adatot olvasunk. Ha tulszaladnank a fajl vegen, akkor a biztonsag kedveert benyomunk a puffer vegere egy \n karaktert, hogy baj ne legyen. Az utolso iteracional meg ugyis kilepunk, tekintve hogy a rendszer vagy emlekezik az utoljara kiolvasott adatmennyisegre, vagy amugy is nulla sort olvasunk.
--


()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

Ha az altalam elmondott algoritmust implementalom, akkor az annyira nem bonyi. Persze, a sorszamolasnal biztos van szebb megoldas is a pufferkezelesre, mivel ez igy elegge feladatspecifikus, de a celnak megfelel. Generikusan nem tudnam megoldani, ez teny.
--


()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

Lehet, hogy nem jól értelmeztem, de az fseek 1-et előre egyet vissza abszurdnak tűnik.

Érdemes kipróblálni, hogy írsz egy programot, ami 1-byteonként beolvas egy 1 GByte-os fájlt. Utána átírod, hogy 2 kByte-onként olvassa be és levonod a következtetéseket (nagyságrendekkel lassabb byte-onként olvasni).

Épp legutóbb írtam egy programot, ami nagy fájlokat képes megjeleníteni. Itt lehet előre és hátra is scrollozni.

Kb. 50 kbyte-os blokkokat olvas be egyszerre a memóriába (cache).

- ha 20-k-s fájlokkal szórakozol, olvasd be memóriába
- ha 1 GByte-osokkal, akkor használj cache-t

Az fseek lassú, nem arra találták ki, hogy ide-oda ugrálj vele.

Ezt a kommentet még mindig nem vetted figyelembe, pedig fontos. Itt írnak egy okot is, hogy miért:

If the integer value returned by fgetc() is stored into a variable of type char and then compared against the integer constant EOF, the comparison may never succeed, because sign-extension of a variable of type char on widening to integer is implementation-defined.

azt is megtalalja, mert a n==1 utan szamol kulonbseget, ami akar lehet minusz is. persze ha 32bitnel nagyobb szamokbol kell szamtani sorozatot csinalni, akkor bukta van, de most ugyse az a feladat hogy hulyebiztos legyen.

--
A vegtelen ciklus is vegeter egyszer, csak kelloen eros hardver kell hozza!

Most volt időm megnézni a forráskódot is. :)

A tömbök indexei nincsenek leellenőrizve. A bemenet egy fájl, ami bármit tartalmazhat, de azt várja a program, hogy maximum 3 számjegyű lehet egy szám.

Ha én tanár lennék, írnám a mínusz pontokat...

Ooo... azert kerdes, hogy maga a feladat mennyire specifikalja le a dolgokat. Azert az ilyen feladatok ugy neznek ki, hogy pl. azt elmondjak, ha a bemeneti fajl formatuma kotott. Ilyenkor implicite nem kell figyelni arra, hogy milyen formatum jon be valojaban.

Nem biztos, hogy mindig altalanos es hulyebiztos megoldasokat kell adni, ez eleg erosen feladatfuggo. Kerdes, hogy a kerdezo mennyire pontosan ertette meg a feladat szoveget.
--


()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

Ezért szoktam a feldolgozandó szöveg elejére valami markert
tenni, ha a program ezt nem találja meg, akkor dobja a
fájlt. Jó, tudom nem minden esetben lehet ezt megtenni. De
azért kiváncsi lennék, hogy egy excel mit csinál mondjuk
egy bináris fájllal. Szóval valami előfeltételezés nem árt.

> Sol omnibus lucet.

Nézd, a kérdés az, hogy csak úgy programozgatni akartok, vagy megtanulni HASZNÁLHATÓ és KARBANTARTHATÓ programot írni.
Az első esetben teljesen felesleges ezzel nyúzni a diákokat, mert később csak nekünk igazi programozóknak lesz bajunk vele, ha munkát kapnak. A második esetben viszont vannak szabályok, amiket be kell tartani.

Ilyen szabály az indexek ellenőrzése, vagy az, hogy a változókat nem a1 - a224-ig nevezzük el, hanem értelmes nevet adunk neki, például: digitBuffer.

Tudom, mindent össze lehet csapni, meg oda is lehet hányni, de nekem mérnöknek ez bántja a szememet. Ha csinálsz valamit, akkor azt csináld meg rendesen, vagy sehogy.

Ez csak egy házi, azért ne tegyünk már úgy, mintha atomrakéta szoftvert kellene fejleszteni, mérnök úr ide vagy oda.
Valamit meg félreértesz, ez nem az én házim, semmi közöm hozzá. De ezzel a digitális aláírással, meg security hole témával kezdünk átesni a ló túloldalára. Még a végén A osztályú tanúsítványt is kell igényelni és egy legalább C típusú nemzetbiztonsági vizsgálat is kell, hogy egyáltalán belenézzek a kódba. Nem beszélve arról, hogy külső, független auditori vizsgálatnak is alávetik.

> Ha csinálsz valamit, akkor azt csináld meg rendesen, vagy sehogy.
Ó azért én megnézném, hogy milyen kódot adott ki a kezei közül a pályája elején az igen mélyen tisztelt mérnök úr. :D

Jo, nyilvan atestunk a lo tuloldalara. Viszont en kocakoderkent is igyekszem tudasomhoz minosegi kodot kiadni a kezem kozul - noha meg csak nem is feladatom barmilyen kod irasa. Meg a C64 Basic-es idokben sem ganyoltam tul sokat, csak amennyit a platform miatt egyebkent is kellett.
--


()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

Csab, reszben azert igaza van. Van egy hatar, amig meg erdemes elmenni egy adott projekt eseten a tokeletesitessel. Nyilvan, ha az ember prod helyre ir programot, akkor el lehet mogyorozni a biztonsagon, de ha csak egy beadando hazirol van szo, szerintem nincs ertelme egy kethonapos projektet krealni ra, hogy tutifullabszolutetotalisan biztonsagos legyen. Foleg, hogy a leadasi ido az egyik hatar, ameddig el lehet menni.

Ezzel nem azt mondom, hogy nem kell, hogy az ember vereve valjon a biztonsagos programozas, sot! Ameddig van ertelme az adott projekt eseteben, jo lehet elmenni, de csak addig, es nem tovabb. Nem kell minden loszarra felkeszulni, ha csak az a feladat, hogy egy sor szoveget vesszok menten vagdossunk sorokra. Persze, mindenre ugyse tudsz felkeszulni (mi van, ha elmegy az aram, es nincs UPS? :-).
--


()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

Nem arról van, szó, hogy mindent tökéletesen atomtámadásbiztosra kell írni.

De:
- sosem írunk olyan programot, amiről már 1 héttel később sem tudjuk mit csinál (változó nevek, kinézet)
- sosem olvasunk fájlból úgy, hogy a tömbhatárokat ne ellenőrizzük. Ezt jobb korán megszokni, mert később orrba-szájba fagyni fog minden, ha így programozgatunk.
- sosem pointerezünk úgy, hogy ne győződjünk meg arról, hogy a pointer lehet-e nulla
- a delete/free után mindig kinullázzuk a pointert. Akkor is ha nem oszt/nem szoroz a kód futásában
- nyilván a teljesítmény ebben a házifeladatban nem mérvadó, de ha minimális erőfeszítéssel el lehet kerülni, hogy a rendszer lassú legyen (itt el lehet), akkor úgy kell megírni

Nem elég az, hogy egy program hibanélkül lefut. Karbantarthatónak és áttekinthetőnek is kell lennie, hogy később bővíthető legyen. C-ben nehéz jól programozni.

Egyébként sajnálattal vettem észre, hogy nem ő az egyedüli egyetemista, aki 'valt' változónevet használ, ahelyett, hogy rendesen kiírná. Ezt így tanítják (a tanár is pongyola), vagy automatikusan alakul ki? Valahol nehéz elképzelni, hogy ha a diákok csak szépen megírt kódokat látnak, utána mind egytől-egyig valt2 változóneveket fognak használni.

Láttam egyetemi jegyzeteket arról, hogy hogyan kell a kódot generálni... Ahelyett, hogy ezt tanítanák, szerintem jobb lenne, ha megtanítanák rendesen programozni őket, a generált kód meg jöhet majd a munkahelyen...

Ezekkel en is egyetertek, pontrol pontra minddel (talan csak a pointer kinullazassal nem, ugyanis ha egy exit() hivas koveti az utolso free()-t, akkor annyira mindegy). Csak itt felmerultek ezen felul mas dolgok is, es epp a te kommented volt az utolso csepp abban a bizonyos poharban. Sry.
--


()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

A végén meg még elhisszük, hogy az egyetemekről a frissen végzett mérnök urak/hölgyek finomra csiszolt világnézettel jönnek ki, akik egyből a világ legminőségibb kódját állítják elő.

Azzal van inkább a baj, hogy egy egyszerű probléma megoldásához is atomrakéta kell nektek. A házifeladat az házifeladat, az atomrakéta az atomrakéta, ne keverjük a kettőt. Valószínűleg még erősen tanuló fázisban van a delikvens, aki az fseek-es megoldást alkalmazta a feladat megoldásához, ezért inkább azt kellene neki elmesélni, hogy bár lehet, hogy megoldja a problémát, de ezen azért még lehet javítani, ha figyelembe vesz olyan szempontokat, mint az erőforrás felhasználás, a futásidő, stb, amik bizony igen fontosak és kívánatosak lesznek egy komolyabb feladat/projekt megoldása során és amiről ne feledkezzen el még akkor sem, ha talál megoldást. Mert egy probléma megoldásához nem mindig csak egy létező út van. Ő talált egyet, taps-taps, de bizony van olyan út is, ami nem a susnyáson át vezet.

Itt félrevezető válaszokat adnak. A programban az első hiba a


  char c;

deklaráció. Az a baj vele, hogy c értéke sosem lesz EOF. Az EOF általában -1. fgetc return értéke int (lásd man). A int->char konverzió fordítási opciótól függően lehet előjeles és előjeltelen. A szokásosabb (és szerintem is jobb) választás az előjeltelen. Ebben az esetben a fájl végén c==255 lesz, és a ciklus addig fut tovább, amíg elsegfaultol.

Az előre-hátra lépkedés marhaság. Az if-ek normális sorbarakásával fel sem merül, hogy szükség volna rá. ungetc-re sincs szükség.

Ehelyett


  if( (e[j+1]-e[j+2]==kul) && (e[j+2]-e[j+3]==kul) && (e[j+3]-e[j+4]==kul) )

ciklus kell. Így nem elfogadható a házi feladat.
--
CCC3