strstr vagy más keresőfüggvény

 ( Babar87 | 2010. április 13., kedd - 0:00 )

Üdvözlet!

Még kezdő vagyok C-ben és már órák óta nyűglődöm a köv programmal:

#include < stdio.h >
#include < string.h >
#define MAXHOSSZ 100

int main(int argc, char *argv[]){
char *mitkeres, tmp[MAXHOSSZ];
int n=0;
FILE *BE;

BE=fopen(argv[1], "r");
mitkeres=argv[2];

while(!feof(BE)){
fgets(tmp,99,BE);
while(strstr(tmp,mitkeres)){
n++;
}
}
printf("%d talalat!\n",n);
}

A segítségeteket kérném, hogyan is kell ezt helyesen megírni.

Előre is 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ő.

Attol fugg, vegtelen ciklussal akarod, vagy anelkul.
while(strstr(tmp,mitkeres)){
n++;
}
Ez ugyebar megnezi, hogy haystack-ben benne van-e a needle. Ha nincs (NULL a visszateresi ertek, ami logikai hamis), akkor megy tovabb.
Ha benne van, akkor logikai igazat ad (+elarulja, hogy a stringben merre van), es megnoveli n erteket 1-gyel. Utana megint megnezi, hogy ugyanabban a stringben ugyanaz a string meg mindig benne van-e ugyanott, ahol elozoleg megtalalta.. na ez vegtelen ciklus.

Ha a feladat annyi, hogy az adott sorban keresed a stringet (mint mondjuk a grep -c parancs), es arra vagy kivancsi, hogy benne van-e, akkor csereld le a while-t if-re.
Ha szamit az is, hogy egy sorban hanyszor van benne, akkor viszont jegyezd meg az strstr visszateresi erteket, es legkozelebb az utan probald meg keresni..
pl:
char *talalat,*eredmeny;
...
talalat=tmp;
while(eredmeny=strstr(talalat,mitkeres)){
n++;
talalat=eredmeny+1; // igy ha mondjuk "alma" a keresendo, akkor az "almafa" szoban az l-tol kezdve keresi a 2. almat..
}

Tovabbi javitanivalo:
while(!feof(BE)){
fgets(tmp,99,BE); // ez igy nem javasolt.. jobb kerulni a feof hasznalatat, es a while felteteleben beolvasni
// 99 helyett legyen inkabb MAXHOSSZ-1, elvegre azert van (a fordito ugyis kioptimalizalja, nem szamolja ki minden futaskor, de olvashatobb, es konnyebben modosithato)
Ja, es a tmp[MAXHOSSZ-1]='\0'; is legyen ott valahol, mert az fgets a szabvany szerint nem feltetlenul zarja le a stringet, es elmaszhat az strstr a memoriaban. (a program jelenlegi allapotaban mondjuk nem szamit)

hibakezeles:
BE=fopen(argv[1], "r");
mitkeres=argv[2];
// szepen elszall, ha nincs eleg argumentuma, nem tudja megnyitni a file-t, vagy hasonlo dolog tortenik

Igy ranezesre ennyi, nem teszteltem, lehet meg benne hiba.

--
I can't believe Steve Jobs's liver is replaceable but the battery in my iPhone is not. - sickipedia

Köszönöm a gyors segítséget, nagyon jól leírtad csak az én bénaságom miatt nem bírom rendes működésre bírni.
Íme az újabb kód:

#include < stdio.h >
#include < string.h >
#include < stdlib.h >
#define MAXHOSSZ 100

void main(int argc, char *argv[]){
char *mitkeres, tmp[MAXHOSSZ];
int n=0;
char *talalat,*eredmeny;
FILE *BE;

if(argc!=3) {printf("\nTul keves vagy tul sok parametert adott meg!\n"); exit(-1);}

BE=fopen(argv[1], "r");
mitkeres=argv[2];

while(fgets(tmp,MAXHOSSZ-1,BE)){
talalat=tmp;
if(eredmeny=strstr(talalat,mitkeres)){
n++;
talalat=eredmeny+1;
}
}
printf("%d talalat!\n",n);
}

Ha módodban áll fordítsd le, próbáld ki.
Nem tudtam honnan szedted az alma és almafa szavakat hisz a fájlt amit olvastatni akarok vele azt nem másoltam be :) (abba is ezeket a példákat írtam stb.)

Még hiba, hogy ha csak egy 'a' betűt írok azt is helyes találatnak veszi és a sorba a többi előfordulást nem nézi(gondolom vmiért nem jut el addig).

Alapvetoen azt kene eldonteni a minta karantaltan rovidebb e mint egy elore adott konstans (99 nalad) illetve lehet e a kozepen sortores. Ha ezek mind nem jelentenek problemat nagyjabol jonak tunik amit csinalsz Nyosigomboc javitasaival. Ha ez problema akkor az egesz megoldas bukott es mast kell kitalalani. Hint: keress egy mintailleszto algoritmust amit modositani tudsz olvasott fajlra random elerheto memoriaban levo szoveg helyet Pl a brute force helybol ilyen..
==
`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.

Bár nem tudom pontosan mit szeretnél, de ez így nem fog találatot adni, ha a puffer épp ott vágja el a sort, ahol lenne a találat. És akkor ha ez kb így néz ki, onnan jöhet a hibakezelés, de gondolom ez csak egy házifeladat :)

#include < stdlib.h >
#include < stdio.h >
#include < string.h >
#define MAXHOSSZ 256 // Legyen kerek
int main(int argc, char **argv)
{
	int n=0;
	FILE *fp=fopen(argv[1],"r"); // kisbetűvel
	char  tmp[MAXHOSSZ]={0x0};
	while(fp!=NULL && fgets(tmp, sizeof(tmp),fp)!=NULL) {
		if (strstr(tmp, argv[2])) n++ // argv[2]-nek szerintem nem kell váltózó
	}
	if(fp!=NULL) fclose(fp);
	printf("%d találat!\n", n);
	return 0;
}

--
http://sandor.czettner.hu

Teccik ez a megoldás :)
Nem házi feladat... vizsgára gyúrok :S Csak még nagyon messze vagyok a 2-estől

alma.txt tartalma: alma korte dinnye alma
Sortörés nélküli szöveg, szavak szóközzel elválasztva
Ez is csak 1 találatot ad vissza

Gondoltam mai tanulásnak megteszi egy ilyen progi megírása, de nemjött össze... ezért kértem a segítségeteket.

Persze, hogy egy találatot ad vissza, mivel soronként ellenőrzi, hogy szerepel-e az adott string és ha igen, n++ Azt nem nézi, hogy hányszor szerepel a sorban

--
http://sandor.czettner.hu

Tényleg:)

Csak én úgy próbáltam h egy sorban több előfordulást is találjon.

Ez a sor mit jelent pontosan? A végét nem tudom... ={0x0}
char tmp[MAXHOSSZ]={0x0};

char **argv //ide miért kell 2 csillag és miért nem kell [] ?

nulla értéket ad a tmp tömbnek. Majd jön egy nálam okosabb és megmondja, miért kell. Azt hiszem egyes fordítók nem teszik meg, ezért a pointer miatt lesz valami fals értéke a tömbnek. Engem csak rászoktattak :)

**argv == *argv[] - a két csillagos szebb.

--
http://sandor.czettner.hu

Ja beugrott... kinulázza a tömbelemeket, és azért van rá szűkség mert lokálisan van definiálva. (ha jól emlékszem)

Az strstr helyett ezt a függvényt kellene használni:

char *strstrnext(const char *s,const char *t){
static const char *st;
if(st==NULL)st=s;
else st++
return ((char *)st=strstr(st,t)); }

A gondom csak az vele, hogy a fordító ezt írja error-nak: cannot convert from 'const char *' to 'char *'
Conversion loses qualifiers

1-2 észrevétel, csak mert az algoritmusod (is) hibás :)

- A MAXHOSSZ bevezetésével megpróbálod elkerülni a "mágikus számok" használatát a kódban. Közben lejjebb a 99-et beégeted a kódba. Ha később a MAXHOSSZ-ot csökkented, bajok lesznek. Szóval: 99 helyett (MAXHOSSZ - 1) lenne. De az fgets(buf, n, file) hívás eleve maximum (n - 1) darab karaktert olvas be, szóval (MAXHOSSZ - 1) helyett simán MAXHOSSZ kell. Azért olvas be eleve eggyel kevesebbet, mert lezárja a végén egy '\0' végjellel - legalább ezzel nem kell törődnöd.

- "char *mitkeres,tmp[MAXHOSSZ];" - ez jó, csak sokak szerint nem túl szerencsés dolog, ha összevonsz több deklarációt, különösen akkor, ha különböző típusú változókat deklarálsz. Gondolja arra, hogy egyszer majd egész napi kódolás után fáradtan, rommá erőltetett szemekkel kell az ilyet megírnod vagy valaki másnak értelmeznie. Nagyon könnyű elrontani. Ráadásul a fejlesztést segítő eszközöket (IDE funkciók, stb.) is gyakran jobban ki lehet használni, ha egy sorba csak egy deklarációt írsz.

- Az keresést végző két egymásba ágyazott ciklus már teljesen rossz:

- Egyrészt az strstr() fv. mindig a megtalált minta _első_ előfordulásának helyével tér vissza, akárhányszor is hívod. Ha egyszer megtaláltad, legalább a (találat helye + 1) pozícióról kell folytatnod a keresést. Ahogy te írtad, úgy vagy megtalálja a mintát és végtelen ciklusban növelgeti n-et, vagy nem, és akkor szerencsés vagy. :)

- Másrészt, mivel a fájlt 99 karakternyi blokkokban dolgozod fel, az algoritmusod egyszerre egy ilyen blokkban vizsgálná (ha működne) a szöveget. Ha a keresett minta pl. 10 karakter hosszú, és a fájlban a 95. pozícióban kezdődik, akkor az első iterációs lépésben csak a minta elejét, a másodikban csak a minta végét látja, így sosem találja meg. Ezt a hibát nehéz lesz javítanod: alapvetően vagy az fseek() függvénnyel kell korrigálnod a beolvasáskor (ez gusztustalan), vagy egy pufferelt adatbeolvasási réteget kell implementálnod amiben még a minta hosszát is figyelembe kell venned (hát ez sem semmi), vagy teljesen dobnod kell az strstr használatát és magadnak kell megírnod a mintaillesztést, ekkor megint kellhet pufferelés, csak talán szofisztikáltabban megírható. Viszont ha adott, hogy mekkora lehet egy sor maximális hossza a fájlban, akkor semmi gond.

Szerk.: látom közben már sokan megírták a fentieket, bocs. :)

Az első második és negyek bekezdésben írtakra én is rájöttem és köszönöm h igazoltad. Ezek csupán a rutin hiánya miatt vannak.
Az utolsó bekezdés viszont érdekes, agyalok egy jobb megoldáson (az sose árt).
Ahogy zoner is írta nem 99, hanem sizeof() méretben olvas be. Az meg másik történet h a fájlméretet korlátozom vagy a puffert állítom fájlméretre...

Ha már egyszerűsítesz, akkor inkább a maximális sorméret rögzítését ajánlom, és ahhoz igazítani a pufferméretet. Így már gyakorlatilag triviális lenne a megoldás, és a fájlméretre nem jelentene korlátozást.