Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: IRC-login tilastointi C:llä

Sivun loppuun

jebe [03.06.2006 07:34:25]

#

Teen harjoitustyönäni irssin logien analysointisoftaa ja olen huomannut, että homma onkin aiempaa vaativampi.

Logi on aina muotoa:
hh:mm:ss<nimimerkki> asia
tai
hh:mm:ss<@nimimerkki> asia

hh = tunnit
mm = minuutit
ss = sekunnit

Miten saisin "poimittua" tuolta logista aina juuri oikean tiedon ja täten käsiteltyä sitä. Olen tutustunut jo C-kielen perusteisiin ja tiedostonkäsittelyyn joten "teoria" on hallussa.

Haluaisin tehdä tilastoja mm. siitä kuka on puhunut eniten, mikä on aktiivisin aika kun on puhuttu jne jne. Tarkoitus olisi tulostaa html-tiedosto missä tilasto näkyisi. En halua valmista ratkaisua vaan haluan oppia miten tämä tehdään, mutta haluaisin kuitenkin apua koodin muodossa, koska niistä esimerkeistä ymmärrän parhaiten.

Tälläistä olen saanut aikaan mutta tämä ei toimi.

#include <stdio.h>

/*
IRC-Login tilastointi
*********************
Logi (eli tiedosto) on muotoa:
hh:mm:ss<@nimimerkki> asia
tai
hh:mm:ss<+nimimerkki> asia
tai
hh:mm:ss<nimimerkki> asia

Vastaava rivi toistuu n kertaa (~tuhansia kertoja)
*/

int main ()
{

   FILE *tiedosto;
   int sek, min, tunnit;
   char ircviesti[500];
   char nick[12];
   int merkki;

   tiedosto = fopen ("input", "r");
   if (tiedosto == NULL)
   {
         printf ("Tiedoston avaaminen epäonnistui.\n");
         return 0;
   }

   fscanf(tiedosto, "%i:%i:%i", &sek,&min,&tunnit);
   /* skipataan < */
   tiedosto++;
   if(tiedosto==43) /*voicet tähän*/;
   if(tiedosto==64) /* opit tähän */;
   fscanf(tiedosto, "%s>", &nick);
   fgets(ircviesti, 127, tiedosto);

   printf("%s", ircviesti);

   fclose(tiedosto);
   return 0;
}

sooda [03.06.2006 08:17:49]

#

Eihän tiedostoa tuolla tavalla voi lukea (tiedosto++, eikä voi verrata sitä). Itse hakisin riviltä jotain tyyliin "%i:%i:%i%c%c%s", jossa on ensin aika, ja sitten kaks merkkiä, joista jälkimmäistä sitten verrataan että onko @ tai +. Loppu %s on sitten loppurivi josta parsitaan enempikuin-merkin jälkeinen osa.

Metabolix [03.06.2006 09:56:43]

#

Minä korvaisin tuosta soodan formaatista vielä ensimmäisen %c:n <-merkillä. %s-parametria en käyttäisi, koska se on hieman riskialtis muistin kannalta. Sillekin voi laittaa maksimipituuden, esimerkiksi %10s.

int hh, mm, ss;
char c;
char loppurivi[RIITTAVAN_ISO_LUKU];
char *nimi, *viesti, merkki[2] = {0, 0};

/* Aikaleima ja < */
fscanf(tiedosto, "%i:%i:%i<", &hh, &mm, &ss);

/* Loppu rivi, mutta ei enempää kuin taulukon koko */
fgets(tiedosto, SE_SAMA_ISO_LUKU, loppurivi);

/* Poimitaan @ tai +, tai jos ei ole, laitetaan nolla.
** Samalla laitetaan nimi osoittamaan nimen alkuun */
merkki[0] = loppurivi[0];
if (merkki[0] != '@' && merkki[0] != '+')
{
  merkki[0] = 0;
  nimi = loppurivi;
}
else
{
  nimi = &loppurivi[1];
}

/* Etsitään nimen loppu, ja laitetaan 0-merkki.
** Viesti asetetaan siitä sopivan matkan päähän. */
viesti = strstr(loppurivi, ">");
viesti[0] = 0;
viesti = viesti + 2;

printf("Kello %i:%i:%i %s%s sanoi: \"%s\"", hh, mm, ss, merkki, nimi, viesti);

En kokeillut, mutta noin kuitenkin. Funktio strstr on string.h-otsikossa. Tekstin loppuun jää rivinvaihtomerkki, se voi olla hyvä poistaa. On syytä laittaa riittävän iso puskuri, jotta koko viesti mahtuu. Maksimipituus sillä taitaa olla jossain 512 merkin paikkeilla, tarkista vielä. On toki mahdollista lukea fgetc-funktiolla merkki kerrallaan ja laajentaa taulukkoa dynaamisesti tarpeen tullen.

jebe [05.06.2006 02:12:03]

#

hmm, enpä saanut tuolla ohjeella(kaan) toimimaan.

Tiedosto mitä luen on:

FILE *tiedosto;

ja luen sitä:

tiedosto = fopen("tiedostonimi.log" "r");

meneekö nyt kaikki ihan oikein?

Metabolix [05.06.2006 09:03:36]

#

Lue, mitä kääntäjäkin noista sanoo, ja opettele ne alkeet, joita et selvätikään nyt osaa. Funktion parametreja erottamaan kuuluu pilkku.

jebe [07.06.2006 20:28:44]

#

Juu, typo löytyikin.

Seuraava ongelma:

Onko vinkkiä miten toteutan seuraavan:

Mulla on matriisi (12 merkkiä leveä eli loppumerkin kanssa 13) ja n riviä pitkä.
Matriisissa on stringejä. Siitä pitäisi saada tehtyä seuraava:

Mitä stringiä löytyy eniten (aktiivisin nick), mitä toiseksi eniten, mitä kolmanneksi eniten, jne jne. Onko pseudokoodia tms muuta vinkkiä miten tuo kannattaisi toteuttaa.

Metabolix [07.06.2006 20:57:04]

#

Tee rakenne, jossa on osoitin nimen ensimmäiseen esiintymään ja sen lukumäärä. Varaa taulukkoon aluksi yksi sellainen, aseta se ensimmäiseen nimeen ja laita lukumääräksi yksi. Sitten käy läpi loput nimet. Jos nimi löytyy jo, lisää määrää, muuten varaa lisää tilaa ja lisää nimi listaan.

struct TNick {
  char * Nimi;
  int Maara;
};

/* Vertailufunktio lajittelua varten */
int TNick_Vertailu(const void * A, const void * B)
{
  struct TNick *Ap, *Bp;
  Ap = (struct TNick *)A;
  Bp = (struct TNick *)B;
  return Bp->Maara - Ap->Maara;
}

/* Jossain muussa ajassa ja paikassa... */
struct TNick *Nick = (struct TNick *) malloc(sizeof(struct TNick));
Nick[0].Nimi = Rivit[0];
Nick[0].Maara = 1;
Nickeja = 1;

for (i = 1; i < Riveja; ++i) {
  for (j = 0; j < Nickeja; ++j) {
    if (strcmp(Rivit[i], Nick[j].Nimi) == 0) {
      break;
    }
  }
  /* Eikö löytynyt? */
  if (j == Nickeja) {
    ++Nickeja;
    Nick = (struct TNick *) realloc(Nick, Nickeja * sizeof(struct TNick));
    Nick[j].Nimi = Rivit[i];
    Nick[j].Maara = 1;
  }
  /* Löytyipäs! */
  else {
    ++Nick[j].Maara;
  }
}

qsort(Nick, Nickeja, sizeof(struct TNick), TNick_Vertailu);

printf("Eniten: <%s>, %i kpl.\n", Nick[0].Nimi, Nick[0].Maara);

jebe [08.06.2006 22:53:32]

#

Tämmösen sain aikaiseksi. Mikähän tässä on vialla?

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/* INPUT-tiedoston sisältö

22:34:21< nick1> lirum lorem lipsum larum
22:34:36< nick1> lirum lorem lipsum larumlirum lorem lipsum larum
22:34:52< nick2> lirum lorem lipsum larumlirum lorem lipsum larumlirum lorem lipsum larum
22:35:51< nick3> lirum lorem lipsum larum
--- Day changed Tue May 30 2006
*/

#define RIVINLEVEYS 600

typedef struct xxx
		{
		char nikki[12];
		int lukum;
		} Tietue ;

Tietue nicklista[1000]={"",0};

int main (void)
{

	int i=0;
	int tunnit;
	char pus[RIVINLEVEYS-1];						// IRC-protokollan mukainen standardirivi on max. 500 merkkiä
	char nick[13];									// ircd:ssä maksimi nickin pituus on 12 merkkiä
	FILE *tiedosto;
	tiedosto = fopen ("input", "r");				// Tiedoston nimi jota analysoidaan

	while (!feof(tiedosto))							// .. loppuun saakka
		{
		fgets(pus,RIVINLEVEYS,tiedosto);
													// Otetaan ne rivit "ulos" mitä ei haluta käsitellä
			if (!(pus[4]=='D') &&					// Ainoan kerran kun logissa on pus[4]:ssä 'D' on silloin kun päivä vaihtuu.
				!(pus[9]=='!') &&					// Ainoan kerran kun logissa on pus[9]:ssä '!' on silloin kun tulee viesti serveriltä.
				!(pus[9]=='*') &&					// Ainoan kerran kun logissa on pus[9]:ssä '*' on silloin kun /me.
				!(pus[4]=='L'))						// Ainoan kerran kun logissa on pus[9]:ssä 'L' on silloin kun logi avataan tai suljetaan.

				{
				sscanf(pus, "%d:",&tunnit);			// Luetaan tunnit muuttujaan tunnit (etenee puskurissa siihen saakka kun tulee :)
				sscanf(pus+10,"%[^>]",&nick);		// Siirrytään puskurissa (rivillä) 10 merkkiä eteenpäin (ajan ja moden ohi).
													// Otetaan nick talteen muttei oteta merkkiä > tallennetaan nick-muuttujaan.

					//!strcmp(a,b)
					if (((strcmp(nick, nicklista[i].nikki)) == 0))

					{
					nicklista[i].lukum++;
					}
					else
					{
					strcpy(nicklista[i].nikki,nick);
					nicklista[i].lukum++;
					}
					i++;

				}
		}
	fclose(tiedosto);




	printf ("Annoit seuraavat tiedot: \n\n");
	for (i=0;i<10;i++) {
	printf("%-30s %d \n",nicklista[i].nikki, nicklista[i].lukum);
	}

return 0;
}


/* Tulostaa tälläistä:
nick1                          1
nick1                          1
nick2                          1
nick3                          1
                               0
                               0
                               0
                               0
                               0
                               0
*/
/* Kun pitäisi tulostaa tämmöistä:
nick1                          2
nick2                          1
nick3                          1
                               0
                               0
                               0
                               0
                               0
                               0
*/

Metabolix [08.06.2006 23:33:20]

#

Ensinnäkin kommentit olisi kiva kirjoittaa omille riveilleen. Pitää olla järjettömän leveä ikkuna, että tuosta saa mitään tolkkua.

En jaksa tuota koodia yrittää ymmärtää. Yksi ohjelmoinnin perusasioista on se, että kaikkea ei kuulu laittaa samaan funktion. Tee siis ensin funktio, jossa luet tiedoston muistiin järkevään muotoon, niin itse analyysivaiheesta tulee paljon selvempi. Tuo on nyt sellaista sotkua, että... Jos nyt kuitenkin oikein tulkitsin, niin sinulta puuttuu tuolta kokonaan se kriittinen for-silmukka. Pitäisi siis käydä kaikki i:n arvot läpi nollasta viimeiseen löytyneeseen.

Mieti, miten tuo todella toimii nyt. Ihmettelen, jos löydät siitä todella järkeä. Miksi ++i on tuollaisessa kohdassa? Nythän et vertaa käsiteltävää edes mihinkään löydetyistä, vaan vain siihen, johon olet sen laittamassa, kun se ei olekaan sama.

jebe [09.06.2006 08:21:51]

#

Juu, oon tehnyt kyllä monta funktiota ja tuo on vain osa minun ohjelmastani. En vain nyt osannut tehdä sitä for-silmukkaa mikä tuossa pitäisi olla.

Voisiko joku auttaa? Tässä nykyinen koodini:

Kirjoitin kommenttiin mitä pitäisi tulla.
(mod. poistettu yllä olevan kanssa samanlainen koodi)

os [09.06.2006 11:03:34]

#

jebe kirjoitti:

sscanf(pus+10,"%[^>]",&nick);

sscanf ei tue tuollaista ilmausta ja nick on jo pointteri, joten &-merkkiä ei kuulu tuossa käyttää.

Laita vaikka:

i = 0;
do nick[i] = fgetc(tiedosto);
while(nick[i++]!='>' && i<13);
nick[i-1] = '\0';

Metabolix [09.06.2006 11:53:36]

#

Mikset voi lukea noita aiemmin kirjoitettuja neuvoja? Olen laittanut jo tiedostonlukuesimerkin ja nickienlaskuesimerkin, ja vaikka ensimmäisessä olikin yksi pieni virhe ja yksi C:n jännä ominaisuus häiritsemässä, niin eipä paljon vaadita, että laittaa nuo omiin funktioihinsa ja käyttää niitä. Vain muistinkäsittely puuttuu siitä ensimmäisestä. Hieman muokkasin parista kohti formaattia ja tein kokonaisen ohjelman, jolle pastesin pätkän keskustelua suoraan irkistä, ja aivan hyvin se noilla toimi.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta