Beolvasás fájlból soronként

 ( hajduarpad | 2007. október 9., kedd - 19:12 )

Hello!

A következő lenne a kérdésem:
C-ben
hogy tudok egy fájlból soronként beolvasni ?

Köszönöm!

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

fgets

line [xyz];
FILE *tf;

fgets(line,xyz,tf);

ez így jó szerintem ja persze xyz helyére egy szám kell ami minimum akkora mint a beolvasott sor hossza sscanf-al fel is tudod dolgizni

sscanf( line, "leírás ahopgy a beolvasott sor kinéz ezt nem tudom szebben", &cím)

köszönöm, remélem sikerülni fog :)

Kiemelés a NetBSD manual oldalai közül (de gondolom, más UNIX rendszernél is hasonlóan hívható):

"FGETS(3) NetBSD Library Functions Manual FGETS(3)

NAME
fgets, gets -- get a line from a stream

LIBRARY
Standard C Library (libc, -lc)

SYNOPSIS
#include stdio.h

char *
fgets(char * restrict str, int size, FILE * restrict stream);

char *
gets(char *str);

DESCRIPTION
The fgets() function reads at most one less than the number of characters
specified by size from the given stream and stores them in the string
str. Reading stops when a newline character is found, at end-of-file or
error. The newline, if any, is retained, and a `\0' character is
appended to end the string.

The gets() function is equivalent to fgets() with an infinite size and a
stream of stdin, except that the newline character (if any) is not stored
in the string. It is the caller's responsibility to ensure that the
input line, if any, is sufficiently short to fit in the string.

RETURN VALUES
Upon successful completion, fgets() and gets() return a pointer to the
string. If end-of-file or an error occurs before any characters are
read, they return NULL. The fgets() and gets() functions do not distin-
guish between end-of-file and error, and callers must use feof(3) and
ferror(3) to determine which occurred.

ERRORS
[EBADF] The given stream is not a readable stream.

The function fgets() may also fail and set errno for any of the errors
specified for the routines fflush(3), fstat(2), read(2), or malloc(3).

The function gets() may also fail and set errno for any of the errors
specified for the routine getchar(3).

SEE ALSO
feof(3), ferror(3), fgetln(3)

STANDARDS
The functions fgets() and gets() conform to ANSI X3.159-1989
(``ANSI C89'').

CAVEATS
The following bit of code illustrates a case where the programmer assumes
a string is too long if it does not contain a newline:

char buf[1024], *p;

while (fgets(buf, sizeof(buf), fp) != NULL) {
if ((p = strchr(buf, '\n')) == NULL) {
fprintf(stderr, "input line too long.\n");
exit(1);
}
*p = '\0';
printf("%s\n", buf);
}

While the error would be true if a line > 1023 characters were read, it
would be false in two other cases:

1. If the last line in a file does not contain a newline, the
string returned by fgets() will not contain a newline either.
Thus strchr() will return NULL and the program will terminate,
even if the line was valid.

2. All C string functions, including strchr(), correctly assume
the end of the string is represented by a null (`\0') charac-
ter. If the first character of a line returned by fgets()
were null, strchr() would immediately return without consider-
ing the rest of the returned text which may indeed include a
newline.

Consider using fgetln(3) instead when dealing with untrusted input.

SECURITY CONSIDERATIONS
Since it is usually impossible to ensure that the next input line is less
than some arbitrary length, and because overflowing the input buffer is
almost invariably a security violation, programs should NEVER use gets().
The gets() function exists purely to conform to ANSI X3.159-1989
(``ANSI C89'').

NetBSD 4.0 June 4, 1993 NetBSD 4.0
"

Ugyan ez hasonlo, de en bash-ban szeretnem az alabbit:
Lefut egy ping parancs, vegignezi a halot, melyik gep megy.
Az eredmenyt kiirja egy file-ba, az alabbi formaban:
192.168.0.1 0
192.168.0.2 0
192.168.0.3 1
192.168.0.4 0
.
.
.
Ahol a "0" nem pingelheto, az '1' pedig pingelheto.
Namost, felora mulva ujra vegignezi a gepeket, es amelyiknek _valtozott_ az
allapota (tehat akar ki-, akar bekapcsoltak), azt jelzi.
awk-kal probalkoztam, de nemigen jon ossze. Biztos egyszeru lenne a megoldas,
de hiaba gyotrom az agyam, nem latom :(
Van valakinek otlete?

ha x és y a két file akkor:

cat x y | sort -u | awk '{ print $1 }' | uniq -d

Üdv.

Sejtettem, hoyg halal egyszeru lesz :)))
Mindjart utanagondolkodok es kiprobalom. Vagyis, majd csak holnap...
Nagyon koszi!
(persze, mert en abbol indultam ki, hogy el van mentve az allapot, es a script runtime nezi az elozo elmentett allapotot, vagyis soronkent olvassa a filet...Erre nem gondoltam :)

Kozben nezegettem Pere Laszlo konyvet is, a "diff" sem egy rossz otlet.
Viszont: abban tudnal segiteni, hogy voltakeppen hogyan lehetne a legegyszerubben
egy file-bol sorokat (netan egy bizonyos sort, nem "grep", hanem sorszam szerint) beolvasni?

egy file-bol sorokat (netan egy bizonyos sort, nem "grep", hanem sorszam szerint) beolvasni

sed-del tudsz a sorszamra regexpet irni, tehat ha az elso es a 12. sor kell, akkor:


seq 20 | sed -e '/^\(1\|12\)$/ !d'

Ez rohadtul nem mukodik, de sed-del valoban meg lehet oldani. Nem kell regexp sem hozza:

sed -n '53p;28p;36p' akarmi.txt

Ki fogja neked irni a 28. meg a 36. es az 532. sor tartalmat, nyilvan a sorrendet meg lehet adni logikusabban, es tetszolegesen sok vagy keves sort megadhatsz Xp formaban. Ha tobb kiirando sor van, akkor mar kell a ; is, mint a fenti peldaban.

a cat -n ellátja a sorokat sorszámmal ...

"egy file-bol sorokat (netan egy bizonyos sort, nem "grep", hanem sorszam szerint) beolvasni?"
head és tail egymásra csövezve

Aha, tail-t hasznaltam, de csak arra, hogy a logot folyamatosan figyeljem.
Sose neztem a man-jat jobban. head-rol meg csak annyi remlik, hogy kiirja egy file elso sorat. De erdekes otlet.

head az első x sort listázza, tail pedig az utolsó y-t.
Ha pl. a head-del listáztatod az első 21-et, majd az eredményt rácsövezed tailra, hogy csak 1 sort adjon, akkor pont a 21. sort kapod.

Ennél komplexebb feladatokra szerintem perl-t jobb használni, pl. foreach(<>){....} királyul használható stdin-en iterálásra.

Iterálni szövegfájlokban bashben is lehet:

while read
do
# a $REPLY tartalmazza a beolvasott sort
done <"$FILE"

Illetve elv van vmi egyszerű módszer szűrők írására. (Azaz vmi 1 sor ami auto megválasztja a read bemenetét attól függően h a delikvens az stdinre tolja az infót vagy egy paraméterben megadott fájlnevet.)
Majd előkeresem a jegyzetemet és leírom.

---
Szerk: így célszerű szűrőt írni:

cat "$@" |\
while read
do
# ...
done

Így ha kapott fájlneve(ke)t paraméternek, azokat dolgozza fel a megadás sorrendjében, ellenkező esetben az stdint nézi. Nyilván ha a szűrő vár a fájlneveken kívül más paramétert, akkor azt érdemes megnézni az elején és shiftelni, hogy ne okozzon gondot a catnek.

kl223

Szornyu, hogy mikor megertem, milyen logikusan egyszeru lesz, de elotte hiaba torom rajta a fejem, nem akar eszembe jutni a megoldas :) Es tenyleg konnyeden lehet igy.

Vagy igy:

awk '{ L++; if ( elsosor<=L && L<=utolsosor ) print; }' be >ki

az if (...) feltetelt termeszetesen barmire at lehet irni :] Hm, lehet hogy van az awk-nak beepitett sorszamlaloja is, akkor nem kell kezzel L++-ozni, de erre most hirtelen nem emlekszem.

s/L/NR/g

termeszetesen van

Koszi mindket otletet!

Sot, most mar koszi _minden_ otletet :)
Lesz min tunodnom. De akkor legalabb azt latom, hogy tenyleg nincs ra egyszeru parancs.
Pedig nyugodtan lehetne egy cat/tail-szeru parancs, ami bizonyos sort ir ki egy file-bol.
Azok is milyen egyszeru parancsok, megis mennyi mindenre hasznalhatok csobe kotve.

De akkor legalabb azt latom, hogy tenyleg nincs ra egyszeru parancs.


sed '1!d' # csak az elso sort irja ki
sed '3,7!d' # a 3.-tol 7.-ig levo sorokat irja

nembirtam ki szorri:)

Oke, pontositok: egyszeru parancs alatt azt ertem, amihez nem kell sed, awk vagy perl :)
Minthogy mindharom proggi bizonyos mertekben shell, tehat ugy gondoltam, hogy a bash shellben nincs ra parancs. De a tail | head pont azt csinalja, szoval megiscsak meg lehet csinalni sed/awk/perl nelkul.

A sed/awk/perl semennyivel nem inkabb shell, mint a head es a tail. De legyen itt neked valoban shell, minden funkcioja shell-beli (ehhez nagyon hasonlot mar kaptal font):

i=1 ; while : ; read a ; [ "$i" -eq AMI_NEKED_KELL_SOR ] && echo "$a" ; i=$(( $i + 1 )) ; done

Ha pedig tobb kulonbozo sort akarsz, akkor a [ .. ] && echo reszt csereld ki erre:

case "$i" in EGYIK_FONTOS_SORSZAM | MASIK_FONTOS_SORSZAM | stb ) echo "$a" ;; esac

No ez shell.

nem rossz, nem rossz :)

De, most nezem, hogy az. Szoval pontositok:
i=1; while read a; do

Innentol mar minden ugyanaz. (Ez a forma azert kell, hogy ki is lepjen a ciklusbol.)

Jo, en a "nem rossz" kifejezessel arra gondoltam, hogy innen mar tovabb tudok gondolkodni,
ha hiba van benne, akkor talan megtalalom. A futast nem ellenoriztem le :)

Elnezest, hogy megint kunyeralok scriptet :) , de itt tokolodok rajta mar par oraja.
Szoval, adott ket file:
1. meret ./nev
2. nev;meret;meret2;meret3

A nevek stimmelnek, a mereteket kellene osszehasonlitani.
Tehat, az elso file-bol olvas egy sort, (awk-kal probalkoztam), $1 a meret, $2 a nev.
vegiggrepeli (ez tunt a legegyszerubb, biztos van jobb, hatekonyabb is) a masodik filet
es ahol megtalalja a "nev"-et, onnan kiveszi a meretet es osszahsaonlit $2-vel (vagyis az elso file-bol szarmazo merettel)
Namost, en mar ott elakadtam, hogy hogyan vegyem ki a"nev"-et az elso file-bol. Vagyis, ez mukodik:
awk '{FS=";" ; print $2 " " $1 }'
Tehat szepen kiir minden sort forditott sorrendben. De lovesem sincs, hogyan lehetne (gyanitom, sehogy) awk-ba bash utasitast szurni. Igy gondoltam, atadom egy bash valtozonak a cuccot:
i=`awk '{FS=";" ; print $1 }'`
Ez viszotn nemcsak az aktualis sorbol, hanem az osszesbol atpakolta a $1 valtozot, szepen egymas utan...Innen megakadtam.

Amikkel probalkoztam:

while read
do
i=`awk '{FS=";" ; print $1 }'`
if `grep $i x2 2> /dev/null`
then echo $i
fi
echo $i
#j=`awk '{FS=";" ; print $1 }'`
#echo $j
done < x1

> while read

while read meret nev ; do
  echo $meret $nev

Segithetnel kicsit, mert nem ertem a feladatodat (a szkriptedet meg se probalom erteni). Viszont amit te akarsz, arra a "join" nevu parancsot talaltak ki - szerintem. Szoval kell ket fajl, amiben soronkent talalhatunk azonos adatokat.
F1:
meret1 nev1
meret2 nev2
stb.
F2:
nev1;meret1;masikmeret1;harmadikmeret1
nev2;meret2;masikmeret2;harmadikmeret2
stb.

1) a join-hoz ugyanolyan modon sorba kell rendezni a ket fajlt:

sort -k2 F1 > F1.uj # elso fajlt sorbarendezi a 2. mezoje alapjan
sort -t";" -k1,1 F2 > F2.uj # 2. fajlt pedig az elso mezo alapjan

1b) mivel csak azonos felepitesu fajlok jok a join -nak, az egyiket (most az 1.-t) atalakitjuk - szokoz helyett ";" legyen az elvalaszto itt is, ehhez az elso "sort" atiranyitasa helyere rakunk egy csovet, azaz ez lesz:

sort -k2 F1 | tr -s ' ' ';' > F1.uj

(a tr-nel szokoz van a "-s" opcio utan a 2 aposztrof kozott.

1c) ha az eredeti fajlok nem kellenek, akkor lehet a "sort" "helyben rendezeset" hasznalni (azaz atiranyitas helyett "-o F2" pl.)

2) most mar egyforma a ket fajl, mehet a join-nak. Mivel nem tudom mit akarsz, igy most csak annyi tortenik, hogy minden olyan sorbol, ahol a nevek megegyeznek, lesz egyetlen kimeneti sor, amiben ";"-vel elvalasztva ott van minden adatod:

join -t ';' -1 2 -2 1 -o '1.2 1.1 2.3 2.4' F1.uj F2.uj

(magyarazat: ";" az elvalaszto, az 1. fajl 2. mezoje ES a 2. fajl 1. mezoje kell, hogy megegyezzen, az output pedig a az 1. fajl 2. mezoje, ezt koveti a az 1. fajl 1. mezoje, majd a 2. fajl 3. es 4. mezoje.

Es mivel a join csobol is elfogadja az egyik fajlt, az egesz osszesen 2 sor:

sort -t";" -o F2 -k1,1 F2
sort -k2 F1 | tr -s ' ' ';' | join -t ';' -1 2 -2 1 -o '1.2 1.1 2.3 2.4' - F2

Koszi szepen. Az 1.b-ig oke, az ertheto volt (tehat a tr es sort).
A scriptem a probalkozasaim :)

Tehat megprobalom megegyszer elmodani, mit szeretnek (talan erheto lesz, remelem, igyekszem):

Adott egy file, MySQL exportja. Ez tartalmazza a T. usereknek eladott tarhely nevet es meretet. Ez egy szam, asszem harom erteke lehet (50, 100, 500 vagy valahogy igy). Namost, az elhatarozas az, hogy rendbe szedjuk, xy tul sokat foglal (tudom, lehetne quota is, de nincs).
Tehat "du" kigyujtottem a konyvtarakat (ezert lett a masodik file-ban a
"meret ./nev" formatum).
Adott a ket file, at lehetne nezni kezzel is, de 1500 bejegyzes eseten ez kenyelmetlen.
Tehat gondoltam, irok ra egy scriptet, ami fogja az egyik filet, soronkent olvassa , majd a masik file-bol kinezi az azonos nevet es osszehasonlitja, hogy xy nevu user tenylegesen annyi helyet foglal-e, amennyi engedelyezve van a szamara. Tehat

file1:
nev;meret (van itt tobb meret is, de az nem fontos, az adatbazis foglalast mar rendbe tettuk, mert keves embernek kellett, a meret3 meg a levelezes lenne, de az masik szerveren van, tehat az elso meret a fontos). Ez jott SQL kimenetkent.

file2:
meret ./nev (ez egy "du -m --maxdepth=1" kimenete. Sorba rendezni, vagy a mezoket felcserelni fel tudom, netan a "tr" paranccsal a mezoelvalasztot cserelni)

Tehat, elso filet soronkent olvassa, az ott szereplo nevet kikeresi a masodik file-bol es az elso file-ban a nevhez tartozo meretet osszehasonlitja a masodik file-ban szereplo nevhez tartozo merettel.

Speciel, igy belegondolva, egyszerubb lenne, ha a masodik filet is importalnam egy SQL adatbazisba, ugy mar konnyen lehetne lekerdezest futtatni. Csak szeretnem cron-ba tenni, hogy havonta atnezze es ertesitsen, ha valaki nagyon tullepi a hatart...

Remelem, erhetoen fogalmaztam. Koszi a segiteni akarast!

Rendben. Fejből, teszt nélkül írom. És az itt szereplő fájlleírást használom (ez mintha nem pont az lenne, ami először volt - vagy csak én olvastam félre).

a) 2. fájlt gatyába rázzuk (kiszedjül a ./ -t, és átrendezzük) :

sed -e 's, \./,,' f2 | sort -k2 > f2.uj

b) az 1.-t is megigazítjuk. A feladat ismeretében hasznosabb, ha itt is szóközök választják majd el a mezőket, ezt is megtesszük.

tr ';' ' ' < f1 | sort -k1,1 > f1.uj

c) es akkor most feldolgozzuk az eredmenyt, es ahol az 1. fajlban szereplő méret nagyobb, mint a 2.-ban szereplő (ha jól értem), akkor visítunk. Változatlanul join-nal csinálunk kimenetet, csak ezt még továbbvisszük és megvizsgáljuk.

join -1 1 -2 2 -o '1.1 1.2 2.1' f1.uj f2.uj | while read nev engedelyezettmeret meglevomeret ; do
[ "$engedelyezettmeret" -lt "$meglevomeret" ] &&
   echo "$nev helyfoglalasa ($meglevomeret) nagyobb az engedelyezettnel ($engedelyezettmeret)"
done

KOszi szepen.
"c) es akkor most feldolgozzuk az eredmenyt, es ahol az 1. fajlban szereplő méret nagyobb, mint a 2.-ban szereplő (ha jól értem), akkor" forditva. De ezt meg tudtam forditani :)
Egyebkent meg nem mukodik (semmi eredmenyt nem ad), ahogy elneztem a "./" jelet nem csereli le. De ezt megoldom, itt a forumon es Buki Andras konyvebol mar tanultam annyit, hogy ez meg se kottyanjon :) Remelhetoleg csak az lesz benne a bibi.

Koszi a "join" parancsot, megint egy altalam ismeretlen jatekos :)

UPDATE: vmi a join paranccsal nem stimmel.
join -1 1 -2 2 -o '1.1 1.2 2.1' f1.uj f2.uj > joined.txt
A joined.txt 0 byte-os file lesz.
Amugy a cseret igy oldottam meg:
cat f2 | awk '{FS="./" ; print $2 " " $1 }'

Utananezek, mit rontok el.

UPDATE2: Oke, megvan :) man join segitett. Tehat esetemben ez a helyes:
join -1 2 -2 1 -o '2.1 2.2 1.1' f2.uj f1.uj > joined.txt
Vagy rogton hozzafuzve a Te kimenetedet, ott lesz az eredmeny, rogton ki
is derult, hogy vagy 30 user tullepi a keretet :)

Ezer koszi megegyszer!

Orulok, hogy mukodik. Megjegyzeseim:

a) meg egyszer megneztem mit irtam, es a sed parancs amit irtam, hibas. Ahogyan irtam, ugy szerintem el kellene dobnia a ' ./' sztringet mindenestul - azaz a kimenetben mindennek egy szoban kene allnia, termeszetesen a korrekt (amire gondoltam) az a kovetkezo:

sed -e 's, ./, ,'

(azaz a cserenel bele kene tenni egy darab szokozt - ez az, ami kimarad)t. Mindazonaltal ez sem jo, es igy mar ertheto is, hogy miert nem csinalt semmit. Megneztem ugyanis, a du (amivel ha jol emlekszem, generaltad a konkret fajlt) nem szokozt, hanem tabulatort tesz a ket mezo koze - ezert is nem cserelt a parancsom semmit semmire. Szoval a korrekt megoldasban a lecserelendo szovegben Tab, a csere szovegben meg Space van, azaz immar helyesen:

sed -e 's,<TAB>./,<SPACE>,'

b) a join parancsod (es a test feltetele) pedig annyiban ter el az enyemtol, hogy pont forditva adtad meg a ket fajlt (mint en), ezert a join-nal minden fajlhivatkozbas-an az 1 es 2 fel lett cserelve, illetve a "test" feltetelvizsgalata is megfordult - eleg logikusan.

No szerintem tema lezarva.

b) igen, amikor elolvastam a join man-jat, en is rajottem :)
a) koszi szepen megeccer. Ezt igy meg lehet adni a sed-nek?
./,
Marcsak azert, mert a Buki-konyvben ez szerepel:
[ \t]+
Vagyis a szokoz az ures (zarojelben) a Tab pedig \t

Vegulis mindegy, nagyon jol mukodott, ezutan havonta le is lesz futtatva
(es a cegnek sikerult gyartani havi +50e pluszt, ha mindenki szepen
befizeti a tulfoglalast :)

Megeccer koszi, reszemrol is lezarva.

Bocs félreérthető voltam. Így nem lehet megadni, csak így olvashatóbb volt a kód.