Thema indholdsfortegnelse -- Tastaturgenvej: 'u'  Forrige tema i denne lektion -- Tastaturgenvej: 'p'  Næste slide i denne lektion -- Tastaturgenvej: 'n'Input/Output og Filer
45.  Formateret output og input

Formateret output er kendetegnet af at værdier af forskellig typer kan udskrives med en stor grad af kontrol over deres tekstuelle præsentation. Vi så første gang på printf i afsnit 4.1.

Formateret input involverer læsning og omformning af tekst til værdier i forskellige scalare typer. Scalartyper blev diskuteret i afsnit 18.1.

45.1 Formateret output - printf familien (1)45.4 Formateret input - scanf familien (2)
45.2 Formateret output - printf familien (2)45.5 Formateret input - scanf familien (4)
45.3 Formateret input - scanf familien (1)
 

45.1.  Formateret output - printf familien (1)
Indhold   Op Forrige Næste   Slide Aggregerede slides    Stikord Programindeks Opgaveindeks 

Vi taler om en familie af printf funktioner, idet der findes næsten identiske printf funktioner som virker på standard output, på en fil, og på tekststrenge. Disse adskilles af forskellige prefixes (det der indleder navnet): f, s og intet prefix.

Suffixet f (det der afslutter navnet) er en forkortelse for "formated".

Familien af printf funktioner tilbyder tekstuel output af værdier i forskellige typer, og god kontrol af output formatet

Familien omfatter funktioner der skriver på en bestemt fil, på standard output, og i en streng

Det overordnede formål med kald af printf kan formuleres således:

  • Pretty printing

  • Konvertering af værdier fra forskellige typer til tekst

  • Kontrol af det tekstuelle format

I dette materiale vil vi på ingen måde forsøge at dække alle detaljer om printf. C by Dissection har en del detaljer. I de næste afsnit peger vi på nogle af de væsentligste detaljer.

 

45.2.  Formateret output - printf familien (2)
Indhold   Op Forrige Næste   Slide Aggregerede slides    Stikord Programindeks Opgaveindeks 

I dette afsnit vil vi anskueliggøre bestanddelene af kontrolstrengen i printf. I figur 45.1 viser vi en nedbrydning af kontrolstrengen. For at føre diskussionen rimelig konkret anvender vi et konkret eksempel: %-#08.3hd.

Figur 45.1    Nedbrydning af kontrolstrengen for %-#08.3hd. Denne figur er inspireret fra 'A C Reference Manual' af Harbison & Steele

Forklaringen af %-#08.3hd, som vist ovenfor, følger her:

  • Conversion letter: d for heltalsformatering

  • Flags: - left justify, # use variant (ikke anvendelig her), 0 zero padding

  • Min. field width: 8. Mindste plads som afsættes

  • Precision: 3 Mindste antal cifre der udskrives - bruger 0 paddings om nødvendig

  • Size modifier: h short

Her følger et antal udvalgte, væsentlige observationer om funktioner i printf familien:

  • Returnerer antallet af udskrevne tegn

  • Man kan kontrollere højre og venstre justering i felt

    • Et - flag betyder venstrejustering

  • Field width og precision kan indlæses som input

    • Angivelse med *

 

45.3.  Formateret input - scanf familien (1)
Indhold   Op Forrige Næste   Slide Aggregerede slides    Stikord Programindeks Opgaveindeks 

I samme stil som diskussionen af printf i afsnit 45.1 foretager vi her en overordnet gennemgang af funktioner i scanf familien.

De familiemæssige forhold for scanf modsvarer familieforholdene for printf, som omtalt i starten af afsnit 45.1.

Familien af scanf funktioner tilbyder tekstuel input (parsing, scanning) af værdier i forskellige typer

Familien omfatter funktioner der læser fra en bestemt fil, fra standard input, og fra en streng

Det overordnede formål med scanf er følgende:

  • Fortolkning og parsing af tekst

  • Konvertering af tekstudsnit til værdier i forskellige typer

  • Assignment af værdier til parametre overført pr. reference - call by reference parametre

Parsning er vanskeligere end pretty printing

Ligesom printf, er der mange detaljer som knytter sig til de mulige typer, hvortil scanf kan konvertere

 

45.4.  Formateret input - scanf familien (2)
Indhold   Op Forrige Næste   Slide Aggregerede slides    Stikord Programindeks Opgaveindeks 

Vi opregner i dette afsnit en række nyttige begreber, som gør det lettere at forstå anvendelser af scanf.

  • Kontrolstreng med et antal direktiver

  • Tre slags direktiver

    • Almindelige tegn: Matcher de angivne tegn

    • White space: Matcher så mange mellemrum, tabs, og linieskift som mulig

    • Konverteringsspecifikation: %...c:
      Matcher input efter givne regler og konverteres til en værdi som angivet af c

  • Scan field:

    • Et sammenhængende område i inputtet som forsøges konverteret med en given konverteringsspecifikation.

    • Scan width: En angivelse i konverteringsspecifikationen af den maksimale længde af et scan field

    • Består af et område uden white space (på nær for %c)

Bid mærke i følgende observationer om scanf:

  • Returværdi

    • EOF hvis input 'løber tør' - input failure

    • Antallet af gennemførte konverteringer (indtil evt. matching failure)

  • Assignment suppression *

    • Matching - men ingen assignment til pointervariable

    • Tæller ikke som en konvertering

  • Scan set af formen [cde]

    • Indlæsning af en streng fra alfabetet bestående af 'c', 'd' og 'e'.

  • Scan set af formen [^cde]

    • Indlæsning af en streng afsluttet af et tegn fra alfabetet bestående af 'c', 'd' og 'e'.

Anvendelse af scansets i stedet for et bestemt kontroltegn, ala c eller d, er nyttig i mange situationer. Eksemplerne i afsnit 45.5 er centreret om scansets.

 

45.5.  Formateret input - scanf familien (4)
Indhold   Op Forrige Næste   Slide Aggregerede slides    Stikord Programindeks Opgaveindeks 

Det første eksempel, vist i program 45.1, er et eksempel fra C by Dissection. Vi læser en fil, hvis navn er overført som programparameter (se afsnit 31.3). Alle tomme liner, og alle blanke indrykninger elimineres. Det afkortede indhold udskrives på standard output.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>

int main(int argc, char *argv[]) {

  FILE *ifp = fopen(argv[1],"r");

  char line[300];
  int i = 0;

  while (fscanf(ifp," %[^\n]", line) == 1){
    printf("%s\n", line);
    i++;
  }

  printf("\n\ni = %i\n",i);

  fclose(ifp);
  
  return 0;
}
Program 45.1    Et program der læser ikke-blanke liner fra en fil og udskriver disse på standard output.

Den afgørende detalje i program 45.2 er fremhævet med rødt. Vi ser et scanset, og ikke mindst et white space directive (et mellemrum før %[^\n] ). Scansettet %[^\n] vil få fscanf til at læse indtil et newline mødes. Næste tur i while-løkken, nærmere betegnet næste kald af fscanf i løkken, vil pga. white space directivet overspringe alt white space. Dette er det newline der stoppede det forrige kald af fscanf, tomme linier, og slutteligt blanke tegn i indrykningen på den næste ikke tomme line. While-løkken kører sålænge fscanf foretager en netop succesfuld konvertering. Alt dette er en lille smule tricket!

Vi ser nu på et (antal versioner af et) program der separerer alfabetiske tegn og numeriske tegn i input, der læses fra standard input med scanf. Det er stadig en pointe for os at illustrere scan sets.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>

int main(void) {

  char alph[25], numb[25];
  int numres;

  printf("Enter input, such as \"aaa111bbb222\"\n");

  while (scanf("%[abcdefghijklmnopqrstuvwxyz]", alph) == 1){
    numres = scanf("%[0123456789]", numb);

    if (numres != 1) 
       printf("MISMATCH");
    else
       printf("Alphabetic: %s\n"
              "Numeric: %s\n\n",
              alph, numb);
  }
  
  return 0;
}
Program 45.2    Et andet program der adskiller alfabetiske og numeriske afsnit i en tekst - læser fra stdin.

Lad os forklare hvad vi ser i program 45.2. Det røde kald af scanf læser igennem et alfabetisk afsnit af inputtet. Læsningen stoppes ved første tegn, som ikke er i det røde scanset. Det blå scanf læser tilsvarende igennem numeriske tegn, og stopper ved først tegn som ikke er et ciffer. Læg mærke til hvordan vi bruger returværdierne af scanf. Hvis der er en streng vekselvirkning mellem alfabetiske afsnit og numeriske afsnit af inputtet, startende med et alfabetisk afsnit og afsluttet af et numerisk afsnit kører programmet til ende. Hvis inputtet starter med et numerisk afsnit terminerer while-løkken umiddelbart (uden af give output overhovedet). Hvis et alfabetisk afsnit ikke umiddelbart efterfølges af et numerisk afsnit får vi et "MISMATCH".

Herunder ser vi på en variant af program 45.3 som læser fra den blå strengkonstant med sscanf i stedet for at læse fra standard input (en fil) med scanf eller fscanf. Startbogstavet 's' i sscanf betyder, at vi læser fra en streng.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

int main(void) {

  char *str = "abc135def24681ghi3579";
  char alph[25], numb[25];
  int numres;

  while (sscanf(str,"%[abcdefghijklmnopqrstuvwxyz]", alph) == 1){
    numres = sscanf(str,"%[0123456789]", numb);

    if (numres != 1) 
       printf("MISMATCH");
    else
       printf("Alphabetic: %s\n"
              "Numeric: %s\n\n",
              alph, numb);
  }
  
  return 0;
}
Program 45.3    Et andet program der adskiller alfabetiske og numeriske afsnit i en tekst - læser fra en streng - virker ikke.

Hvis vi prøvekører program 45.3 må vi konstatere, at det ikke virker. Hvorfor ikke? Vi har input i strengen str. De to røde kald af sscanf læser fra str.

Pointen i program 45.3 er at sscanf læser fra starten af strengen i hvert kald. Der er altså ingen nuværende position som bringes fremad for hvert kald af sscanf. Når vi læser fra filer med scanf eller fscanf læser vi altid relativ til den nuværende position i filen. Denne position tælles op i takt med vi læser. Men sådan er det altså ikke, når vi læser fra en tekststreng!

Herunder, i program 45.4, har vi repareret på problemet. Vi "bringer fremdrift i strengen" ved at tælle pointeren op på de røde steder i program 45.4. Med denne ændring virker programmet tilfredsstillende.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>

int main(void) {

  char *str = "abc135def24681ghi3579";
  char alph[25], numb[25];
  int numres;

  while (sscanf(str,"%[abcdefghijklmnopqrstuvwxyz]", alph) == 1){
    str += strlen(alph);

    numres = sscanf(str,"%[0123456789]", numb);
    str += strlen(numb);

    if (numres != 1) 
       printf("MISMATCH");
    else
       printf("Alphabetic: %s\n"
              "Numeric: %s\n\n",
              alph, numb);
  }
  
  return 0;
}
Program 45.4    Et andet program der adskiller alfabetiske og numeriske afsnit i en tekst - læser fra en streng - virker!.

Med disse eksempler afsluttes vores diskussion af formateret output og input.

Genereret: Onsdag 7. Juli 2010, 15:13:26
Thema indholdsfortegnelse -- Tastaturgenvej: 'u'  Forrige tema i denne lektion -- Tastaturgenvej: 'p'  Næste slide i denne lektion -- Tastaturgenvej: 'n'