previous next Up Title Contents Index

17.6.4 palanen ja static


Vaikka edellä strtok näyttikin hyvältä, on siinä muutamia puutteita:
*
mikäli erotinmerkit ovat peräkkäin, esimerkiksi
	Audi||20
tulee 1. palaseksi "Audi" ja toiseksi "20" eikä kolmatta enää saada (tosin tämä saattaa olla toivottavakin ominaisuus katkottaessa välilyönnein erotettuja jonoja!)
*
erotinmerkkien loputtua funktio palauttaa NULL- osoittimen, käytännössä voisi olla parempi palauttaa osoitin tyhjään merkkijonoon, jolloin tulosta voitaisiin käyttää ilman ylimääräistä if- lausetta
Teemme strtok funktiota vastaavan funktion nimeltä palanen (mjonot.h), jossa meidän kannaltamme puutteelliset kohdat on korjattu. Aluksi tulee ongelma siitä, miten ensimmäisen kutsukerran jälkeen osataan jatkaa seuraavan erotinmerkin etsimistä. Aliohjelman täytyy "muistaa" mitä on tehty viimeksi. Normaalisti aliohjelmasta poistuttaessa aliohjelman sisäiset muuttujat häviävät. Mikäli muuttuja esitellään static etuliitteellä, säilyy muuttuja myös aliohjelmasta poistumisen jälkeenkin (eikä ole enää automaattinen muuttuja):

ali\mjonot.c - merkkijonon paloittelu

	/****************************************************************************/
	char                      /*                                                */
	*palanen(                 /* Osoitin merkkijonon palaseen.                  */
	  char *jono             ,/* s   Pätkittävä jono, turmeltuu!                */
	  char *erottimet        ,/* s   Merkit joiden kohdalta katkaistaan.        */
	  int  *jaljella          /* t   Paljonko jonoa on vielä jäljellä (- 1 loppu)*/
	) 
	/* 
	** Funktiolla pätkitään merkkijonoa osiin.  1. kutsukerralla välitetään
	** tutkittava jono ja tämän jälkeen seuraavilla NULL osoitin.
	** Funktio vaihtaa löytämänsä erotinmerkit null- merkeiksi!
	**
	** Muuttuu:   jono
	** Algoritmi:       
	** Esimerkki:       012345678901234
	**            jono="Aku|Ankka||12" erottimet="|"
	**            1. kutsu palanen(jono,"|",&j) - > "Aku"  , j=10
	**            2. kutsu palanen(NULL,"|",&j) - > "Ankka", j=4
	**            3. kutsu palanen(NULL,"|",&j) - > ""     , j=3
	**            4. kutsu palanen(NULL,"|",&j) - > "12"   , j=0
	**            5. kutsu palanen(NULL,"|",&j) - > ""     , j=- 1  
	- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
	{
	  static char *st="";
	  static int p1=0,p2=0,pit=0;
	
	  if (jono) {      /* 1. kutsukerta, alustetaan apumuuttujat */
	    st  = jono;
	    pit = strlen(jono);
	    p1  = 0;
	  }
	  else
	    p1  = p2+1;    /* Muilla kerroilla jatketaan siitä mihin viim. jäätiin. */
	
	  if ( p1 > pit ) {
	    *jaljella = - 1;
	    return st+pit; /* Tyhjä merkkijono, kun osoitetaan jonon null- tavuun.   */
	  }
	
	  p2 = p1+strcspn(st+p1,erottimet);
	  st[p2] = 0;
	  *jaljella = pit- p2;
	
	  return st+p1;
	
	}

Funktio strcspn palauttaa ensimmäisen indeksin, josta löytyy erottimet merkkijonon jokin merkki. Mikäli merkkiä ei löydy, palautetaan jonon pituus.

Funktion toimintaidea on seuraava:


	2. Kutsu:  p = palanen(NULL,"|",&j);
	 Kutsuun tultaessa: (pit == 19, p1 == 0, p2 == 7)
  +---st          +---st+p2+1
  v               v
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4
 +-------------------------------------------------+ 
 | |V|o|l|v|o| | | | |1|2|3|0|0| ||| |1| | | | | | |
 +--------------0-----------------------0----------+ 
                  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6
	                    
	Etsitään |- merkkiä alkaen paikasta 8.  Löytyy jonoon st+p2+1
	nähden paikasta 8.  Siis alkuperäiseen jonoon nähden
	paikasta 16.  
	
	Muutetaan arvot: (pit == 19, p1 == 8, p2 = 8+8)
  +---st          +---st+p1       +----st+p2
  v               v               v
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4
 +-------------------------------------------------+ 
 | |V|o|l|v|o| | | | |1|2|3|0|0| | | |1| | | | | | |
 +--------------0-----------------0-----0----------+ 
                  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6

	palautetaan st+p1 ja *jaljella = 19- 16 = 3                
Näin olemme saaneet funktion, jota voidaan huoletta kutsua myös merkkijonon loppumisen jälkeenkin:

tiedosto\tuot_pal.c - rivin pillkominen palanen- aliohjelman avulla

	int rivi_tuotteeksi(char *rivi, Tuote_tyyppi *tuote)
	{
	  char *p; int j;
	  p = palanen(rivi,"|",&j); 
	  poista_tyhjat(p); kopioi_jono(N_S(tuote- >nimike),p);
	
	  p = palanen(NULL,"|",&j); 
	  if ( sscanf(p,"%lf",&tuote- >hinta) != 1 ) return 1;
	
	  p = palanen(NULL,"|",&j);
	  if ( sscanf(p,"%d",&tuote- >kpl) != 1 ) return 1;
	
	  return 0;
	}

Tehtävä 17.156 Parempi palanen

Kirjoita palanen - aliohjelmasta versio, joka ei pilaa alkuperäistä merkkijonoa.

Tehtävä 17.157 Vielä parempi palanen

Voiko palanen - aliohjelma olla yhtäaikaa kahdessa eri käytössä, eli voiko sillä esimerkiksi paloitella rinnakkain kahta eri merkkijonoa?
Mikäli ei voi, niin esitä ratkaisu miten voitaisiin. Mikäli voi, niin perustele miksi.


previous next Up Title Contents Index