InfoC adventi naptár
scanf %n
A scanf()
-nek van egy érdekes konverziója,
amelyik igazából nem is konverzió. A %n
hatására, amelynek megfelelő helyen egy int*
-ot
kell elhelyeznünk a paraméterlistán, a scanf()
a megadott változóba írja, hogy addig a pontig hány
karaktert dolgozott fel. Hatására konverzió nem történik,
és karakter sem olvasódik be, csak a karakterek számát írja
a változóba.
Ez hasznos lehet, ha egy sztringet nem tudunk egyszerre,
egészében feldolgozni. Tegyük fel azt, hogy van egy fájlunk,
amelyben a sorok formátuma lent látható.
Tudjuk azt, hogy az esemény leírása egy vagy több szó is lehet.
Ha több szóból áll az esemény, akkor idézőjelek ""
között van megadva, ha csak egy szó, akkor pedig azok nélkül.
év-hónap-nap óra:perc eseményleírás hibakód 2010-11-30 12:45 egyszavas 404 2010-12-01 16:27 "tobb szobol allo" 201
A scanf()
-fel tudunk szóközig olvasni (%s
),
és tudunk idézőjelig is (%[^"]
). Csak azt nem tudjuk
eldönteni előre, hogy melyik fog kelleni, és ezért nem lehet
olyan formátumsztringet írni, amelyik az egész sort egyszerre
képes lenne szétbontani.
Ilyenkor jön jól a %n
. Beolvassuk a sztringet addig a
pontig, amikor döntenünk kell, hogy zárójelet látunk vagy nem. Egy
%n
-t rakva a formátumsztring végére a scanf()
megmondja, a beolvasás hányadik karakternél állt meg; így a döntést
meghozva (idézőjeles sztring vagy nem) onnan tudjuk folytatni.
#include <stdio.h> int main() { /* ezeket dolgozzuk fel */ char *sztringek[]= { "2010-11-30 12:45 egyszavas 404", "2010-12-01 16:27 \"tobb szobol allo\" 201" }; /* ebbe */ int ev, honap, nap, ora, perc; char szoveg[100]; int szam; int i; /* ennek magyarazatat lentebb */ int j; /* csak sima ciklusvaltozo */ for (j=0; j<2; j++) { char *feldolgoz=sztringek[j]; /* beolvassuk az elejet, meg meg egy spacet. * az eredmenyek mennek a szamokba; * es a beolvasott karakterek szama i-be. */ sscanf(feldolgoz, "%d-%d-%d %d:%d %n", &ev, &honap, &nap, &ora, &perc, &i); /* es most a kovetkezo karakter (feldolgoz[i]) * a kovetkezo sztring elso karaktere, ha nem idezojeles. * ha idezojeles, akkor pedig maga az idezojel. * ennek megfeleloen beolvassuk, vagy a kovetkezo * spaceig, vagy a "bezaro" idezojelig. * mindket esetben beolvassuk az utana kovetkezo * spacet is, es utana megint megjegyezzuk az innen * beolvasott karakterek szamat. */ feldolgoz+=i; if (*feldolgoz=='"') sscanf(feldolgoz, "\"%[^\"]\" %n", szoveg, &i); else sscanf(feldolgoz, "%s %n", szoveg, &i); /* es most jon a vege. */ /* megin kihagyunk annyi karaktert, amennyit mar * beolvastunk az elobb. */ feldolgoz+=i; sscanf(feldolgoz, "%d", &szam); printf("%d-%d-%d %d:%d [%s] %d\n", ev, honap, nap, ora, perc, szoveg, szam); } return 0; }
A printf()
tudja ugyanezt, csak nem a beolvasott, hanem a %n
-ig
bezárólag kiírt karakterek számát adja meg egy cím szerint átadott int
változóban
Emiatt egyébként különösen fontos, hogy egy felhasználótól származó sztringet
printf("%s", s);
utasítással írjunk ki, ne a printf(s);
sorral. A sztring ugyanis
tartalmazhat %
karaktert, amelyet a printf()
értelmezni fog; ha
%n
-t tartalmaz, akkor pedig még írni is fog majd valahova a memóriába. Így a lenti, bal
oldali kód hibás. Ha a felhasználó pl. azt írja be, hogy „%d”, akkor a printf()
a
paraméterei között keres egy számot is. A helyes változat jobb oldalt látható.
char s[100]; fgets(s, 100, stdin); printf(s);
char s[100]; fgets(s, 100, stdin); printf("%s", s);