Gå til innhold

[Løst] Sjekke om input er et tall


Anbefalte innlegg

Hei!

 

Jeg skriver et program i C som skal lese en inputfil med en bestemt form. Denne inputfilen skal lese inn en rekke tall som den bruker i videre beregninger. Det er derfor veldig viktig at det som står i inputfilen er et tall. Jeg er på utkikk etter en måte å validere denne inputfilen og sjekke at det som leses inn faktisk er tall.

 

Måten jeg leser fra filen på er ganske enkelt:

 

fscanf(fp, "%f %f %f\n", &lek->D[i], &lek->Br[i], &lek->L[i]);

 

Jeg tenkte å løse det slik at dersom det som leses inn ikke er et tall så avsluttes programmet, eller at brukeren må skrive inn et gyldig tall.

 

Jeg har tenkt ut to måter å løse dette på. Enten må jeg finne/skrive en funksjon som sjekker om inputen er float eller int. Eller at jeg setter en grense mellom 0 og max (Det er ikke snakk om store tall) og sjekker om inputen er mellom disse to. Siste løsning er selvsagt enkel, men den mest fleksible hadde selvsagt vært første. Det vil si noe slikt:

 

while(is_numeric(lek->Br[i]) != 1) {
printf("Tallet er ikke gyldig! Skriv inn nytt: ");
scanf("%f", lek->Br[i]); //evt exit(EXIT_FAILURE);
}

 

Finnes dette? Jeg vet ctype.h har en del funksjoner, men jeg finner ikke ut av hvilken som løser mitt problem.

 

Christian_

Lenke til kommentar
Videoannonse
Annonse

Jeg er på utkikk etter en måte å validere denne inputfilen og sjekke at det som leses inn faktisk er tall.

 

Måten jeg leser fra filen på er ganske enkelt:

 

fscanf(fp, "%f %f %f\n", &lek->D[i], &lek->Br[i], &lek->L[i]);

 

Jeg tenkte å løse det slik at dersom det som leses inn ikke er et tall så avsluttes programmet, eller at brukeren må skrive inn et gyldig tall.

 

Så inputen er interaktiv?

 

Jeg har tenkt ut to måter å løse dette på. Enten må jeg finne/skrive en funksjon som sjekker om inputen er float eller int.

 

Vil du ha float eller int? Hvis du vil ha begge, er "3" float eller int? Hva med "3.0"?

 

Eller at jeg setter en grense mellom 0 og max (Det er ikke snakk om store tall) og sjekker om inputen er mellom disse to. Siste løsning er selvsagt enkel, men den mest fleksible hadde selvsagt vært første. Det vil si noe slikt:

 

while(is_numeric(lek->Br[i]) != 1) {
printf("Tallet er ikke gyldig! Skriv inn nytt: ");
scanf("%f", lek->Br[i]); //evt exit(EXIT_FAILURE);
}

 

Finnes dette?

 

Ja, men ikke slik du har lagt opp.

 

Problemet med fscanf() med venner er at du ikke aner hvor eksakt i inputstrømmen den stoppet. Dvs. at det er vanskelig å rulle inputstrømmen til en kjent tilstand, sett fra programmets side.

 

En kanonisk måte å gjøre inputvalidering på er:

 

1) Lese input i faste blokker (f.eks. 1 linje av gangen).

2) Validere en blokk av gangen. Hvis valideringen feiler, så vet du at inputstrømmen er i en veldefinert tilstand og du, i verste fall, kaster kun den defekte blokken.

 

I dette tilfellet er det kanskje mest aktuelt å lese linjevis -- fgets() for å ta tak i linjen, og sscanf() for å tolke linjen. Du kan bruke strtod/strtof, men hvorfor ikke la sscanf() gjøre jobben? Spesielt hvis det er flere tall per linje?

Lenke til kommentar

Posta litt før jeg så du holdt på i C og ikke C++, men men =) Og denne editoren likte ikke tabs -.- (ref kode utsende)

 

En tungvint måte å gjøre det litt "robust" men veldig manuelt på.

// -------------------- LES INN TALL ------------------------------------------
int lesTall(char* msg, int min, int max, bool returnIsZero) {
int i, j;	   // For loop iterratorer
int tempTall;	  // Et siffer som trekkes ut fra teksten
int result;	   // Det endelige tallet
char buffer[sTRLEN+1];	// Buffer for innlesning av input
bool goodInput;	  // Om input var tall
while (true) {	  // Loop intill vi har et godkjent tall
 result = 0;
 goodInput = true;
 cout &--#60;&--#60; msg;	 // Skriver ut meldingen
 cin.getline(buffer, STRLEN); // Så les inn input
 // Noen steder i programmet så har brukeren mulighet til å trykke enter
 // og det skal telle som verdien null. Her så sjekkes det om
 // "returnIsZero" boolen er true, samt at brukeren bare ga enter som
 // input, da breaket vi ut, og returerer result (som er setti til 1)
 if (returnIsZero && strlen(buffer) == 0) {
  break;
 }
 else {	   // Hvis ENTER ikke er lov
  if (strlen(buffer) == 0) { // Sjekk streng lengden
goodInput = false;  // Hvis den er null, så er den bad
  }
  else {	  // Hvis len &--#62; 0 så sjekkes hvert tegn
// Her loopes det igjennom hvert eneste tegn i strengen, og
// sjekker om den er mellom ascii verdiene til 0 og 9
for (i = 0; i &--#60; strlen(buffer); i++) {
 if ((int)buffer[i] &--#60; int("0") || (int)buffer[i] &--#62;int("9")){
  goodInput = false;
  break;
 }
}
  }
  if (goodInput) {   // Om bruker input var tall og god
// Loop igjennom hvert tegn og gjør om til tall (integer)
for (i = strlen(buffer) - 1, j = 0; i &--#62;= 0; i--, j++) {
 tempTall = int(buffer[j])- 48;
 result += tempTall * (int)pow(10.0, (double)i);
}
if (result &--#62;= min && result &--#60;= max) {
 break;	// Hvis innenfor riktig område
}
  }
 }
}
return result;	  // Returner innlest tall
}

 

og en litt mindre tungvinn måte:

// -------------------- SIKKER INNLESING AV TALL ------------------------------
int les_tall(char* msg) {
int temp_tall;	  // Midlertidig int for å holde på tall

while (true) {	  // Loop uendelig, intill "break"
 cout &--#60;&--#60; "\n" &--#60;&--#60; msg;   // Skriv ut melding
 cin.unsetf(ios::skipws);  // IKKE ignorer whitespace
 cin &--#62;&--#62; temp_tall;	// Les inn tall
 if (cin.good()) {	// Hvis god input
  cin.ignore(20, '\n');  // Ignorer
  break;	  // Og break ut av loopen
 }
 cin.clear();	 // Clear Errpr Flags
 cin.ignore(20, '\n');   // Ignore
}
return temp_tall;	 // Returner en gyldig int
}

Endret av Valkyrex
Lenke til kommentar
  • 1 måned senere...
  • 3 uker senere...

Jepp, som LonelyMan sier. Om du skal teste dette for alle filer, blir det fort unødvendig komplisert.

 

Et enkelt forebyggende mot feil, eller korrupterte filer, er å inkludere et "magic number" i starten av filformatet ditt, som sannsynligvis indikerer at det er riktig filtype. For Java's .class-filer som skal leses av JVM, er dette tallet CAFEBABE (i hex); i PNG-filer er det 89504E470D0A1A0A (i hex). Når du lager en ny fil, legger du alltid dette tallet i starten av filen, og tilsvarende sjekker du alltid at dette tallet stemmer når du skal lese filen.

Edit: Men for å ta meg selv litt i nakken, er nok ikke dette en løsning på problemet ditt. Om jeg var deg, ville jeg antatt at det som stod der var tall, og så gitt brukeren en feilmelding om lesingen feilet. Dette kan løses med f.eks. exceptions, men da må du i minste tilfelle bruke C++.

Endret av LostOblivion
Lenke til kommentar

Tja, Python har exception handeling det også (http://docs.python.org/tutorial/errors.html). Kan sammenlignes med C++ sine Try og Catch keywords. (kan hende andre språk har det også)

 

Magicnumber løser bare en initiell sjekk om det er "denne filen jeg lette etter" opplegg. Hjelper nada å ha en fil som har mista ganske mye integritet under f.eks sending via UDP eller noe (ja, dårlig eksemple, hehe), men at magicnumber fortsatt er intakt og på riktig plass.

 

- Anta at filen er korrekt.

- Fang opp feil underveis, og håndter de på riktig måte.

 

=)

Lenke til kommentar

Spørsmålet var i forbindelse med et skoleprosjekt. Jeg løste problemet ved å sjekke om en innlesing ble gyldig. Dvs noe slikt:

 

error = fscanf(fp,"%f",&number);
if(error != 1){printerror();}

Dette fungerte tilfredsstillende, men det finnes sikkert mange andre måter å gjøre dette bedre på. Fristen for prosjektet er ute for lenge siden så for meg er problemet løst!

 

Takk for hjelpen!

 

Christian

Lenke til kommentar
  • 3 uker senere...

Ja som LostOblivion sa, å ha et magic number i fila som kjennetegner din fil er utmerket. Kjennetegnet bør være 4 bytes slik at du kan laste den inn i en dword type variabel og sjekke alle 4 bytene samtidig.

 

Etter dette magiske kjennetegnet kan du ha 2-3 eller 4 hasher i tillegg, hvis det første magiske nummeret passerer og godkjennes, så kan du sjekke de 3-4 hashene du har som er like etter det magiske nummeret, dette for å virkelig være skråsikker på at filen er din fil. Du kan bruke 3 MD5 hasher, som burde være evig nok.

 

Etter hashene kan du lagre et tall som forteller størrelsen på innholdet i filen etter det magiske tallet og hashene og eventuelt antall poster som er i filen.

 

Etter dette kan du lagre poster.

 

Her er en robust måte å verifisere filen, i kronologisk rekkefølge:

 

1: Sjekk at filen eksisterer, hvis ikke, avslutt

2: Sjekk at filstørrelsen er minst MAGICNUMBER+3HASHES i størrelse, hvis ikke, avslutt

3: Sjekk MAGICNUMBER, hvis ikke matcher, avslutt

4: Sjekk alle 3 hashene, hvis ikke matcher, avslutt

5: Sjekk at størrelsen på filen (MINUS) headerdata er riktig, hvis ikke, avslutt

6: Prosesser filen hvis du kom hit. Alt virker ok.

Endret av LonelyMan
Lenke til kommentar

Gjør det litt vanskelig da ;)

 

Enklere å bare:

- Når filen lages så slenges en checksum (f.eks md5 eller hva nå enn) av filen på enden.

- Når filen leses så sjekkes det om checksumen fra fila, og checksumen av selve fila er like (da ser du om fila har tapt integritet under lagring / flytting / behandling).

Lenke til kommentar

Opprett en konto eller logg inn for å kommentere

Du må være et medlem for å kunne skrive en kommentar

Opprett konto

Det er enkelt å melde seg inn for å starte en ny konto!

Start en konto

Logg inn

Har du allerede en konto? Logg inn her.

Logg inn nå
  • Hvem er aktive   0 medlemmer

    • Ingen innloggede medlemmer aktive
×
×
  • Opprett ny...