Avatar billede tangveje Nybegynder
27. september 2004 - 00:00 Der er 19 kommentarer og
1 løsning

Funktion : Validere input fra scanf()

Jeg sidder med et lille problem, som jeg ikke rigtigt kan finde et ordentligt svar på. I mit program skal jeg kunne modtage forskellig input fra brugeren, normalt i form af et enkelt tal eller et koordinat (x,y).

Jeg foretrækker at bruge printf og scanf frem for cout og cin. Problemet er at jeg skal sikre mig mod ugyldigt input fra brugeren. Så hvis programmet ber om en integer og brugeren indtaster en char, skal den lade som ingen ting og lade brugeren indtaste et nyt tal, indtil inputtet er korrekt.

Hvad jeg gerne vil have er en funktion som kan gøre det hele på en gang... tage imod et input, checke at det er gyldigt, hvis det er returneres inputtet, hvis ikke startes forfra. Tænkte det ville være hurtigt overstået, så jeg lavede følgende test kode

void inp( char *in )
{
    int s ;
   
    while ( true )
    { 
        if ( scanf( in, &s ) )
        {
            printf( "Great!\n" ) ;
            break ;
        }
        else
        {
            printf( "Invalid!\n" ) ;
            break ;
        }
    }
}

Ovenstående kode compiler og virker fint, men her er problemet

1. Hvis jeg fjerner den sidste 'break', for at få den til at starte forfra hvis inputtet er ugyldigt går den helt i ged. Hvis jeg indtaster en ugyldig værdi bliver den ved med at skrive 'Invalid!' om og om igen, istedet for at spørge om en ny værdi.

2. Jeg vil gerne gøre funktionen mere dynamisk, så jeg kan sende den en anden parameter, indeholdende en pointer til en variabel af en hvilken som helst type. Altså jeg kunne skrive inp( "%2d", &someint ) eller inp( "%1c", &somechar ) og funktionen ville så bruge den pointer til at gemme inputtet i. Aner ikke hvordan dette kan gøres, men hvis scanf() kan, så kan jeg vel også :)

Håber nogen kan hjælpe mig på vej med dette.
Avatar billede arne_v Ekspert
27. september 2004 - 00:13 #1
Den her skulle have løst begge problemer:

#include <stdio.h>

void input(char *pmt,char *fmt,void *data)
{
  while(1)
  {
      printf("%s",pmt);
      if(scanf(fmt,data))
        return;
      else
        fflush(stdin);
  }
}

int main()
{
  int i;
  char s[100];
  input("Indtast tal: ", "%d", &i);
  printf("%d\n",i);
  input("Indtast tekst: ", "%s", s);
  printf("%s\n",s);
  return 0;
}
Avatar billede arne_v Ekspert
27. september 2004 - 00:14 #2
fflush(stdin) løser #1.

void *data løser #2.
Avatar billede tangveje Nybegynder
27. september 2004 - 00:19 #3
Hmm, #2 er løst, men den går stadig i ged hvis jeg indtaster et ugyldigt tegn, f.eks. en char istedet for en int.
Avatar billede arne_v Ekspert
27. september 2004 - 00:21 #4
Ikke hos mig.

C:\>input
Indtast tal: a
Indtast tal: b
Indtast tal: 123
123
Indtast tekst: x
x

Hvad compiler bruger du ?
Avatar billede tangveje Nybegynder
27. september 2004 - 00:23 #5
Bruger G++ under linux (ikke sikker på hvilken version, den der følger med Fedora Core 2)
Avatar billede arne_v Ekspert
27. september 2004 - 00:24 #6
g++ -v
Avatar billede arne_v Ekspert
27. september 2004 - 00:25 #7
Men det er lidt mystisk da jeg har testet med GCC 3.1 bare på windows.
Avatar billede arne_v Ekspert
27. september 2004 - 00:26 #8
Det virker også med Microsoft og Borland compilere.

Har du kodet det helt ligesom mit eksempel ?
Avatar billede tangveje Nybegynder
27. september 2004 - 00:27 #9
[casper@0x535b848e TTT2]$ g++ -v
Læser specifikationer fra /usr/lib/gcc-lib/i386-redhat-linux/3.3.3/specs
Konfigureret med: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --disable-libunwind-exceptions --with-system-zlib --enable-__cxa_atexit --host=i386-redhat-linux
Trådmodel: posix
GCC version 3.3.3 20040412 (Red Hat Linux 3.3.3-7)
Avatar billede tangveje Nybegynder
27. september 2004 - 00:28 #10
Og her er hele koden, direkte kopieret

#include <cstdio>
#include <cstdlib>
#include <vector>

using namespace std ;

class board
{
    public:
        // Constructor
        board( ) { }
     
        board( int row, int col ) : vBoard( row )
        {
            for ( int i = 0 ; i < row ; i++ )
            {
                vBoard[i].resize( col, 0 ) ;
            }
        }
     
        // Destructor
             
        // Accessors
        const vector<int>& operator[](int row) const ;
           
        // Mutators 
    private:       
        vector< vector<int> > vBoard ;
} ;

void input(char *pmt,char *fmt,void *data)
{
  while(1)
  {
      printf("%s",pmt);
      if(scanf(fmt,data))
        return;
      else
        fflush(stdin);
  }
}

int main()
{
  int i;
  char s[100];
  input("Indtast tal: ", "%d", &i);
  printf("%d\n",i);
  input("Indtast tekst: ", "%s", s);
  printf("%s\n",s);
  return 0;
}
Avatar billede bertelbrander Novice
27. september 2004 - 00:34 #11
fflush(stdin); er undefined, det "virker" måske på Windows men ikke på Linux.
Man kan ikke flush'e input streams.

Løsning -> brug fgets + sscanf.
Avatar billede bertelbrander Novice
27. september 2004 - 00:37 #12
Fra C standarden:

7.19.5.2 The fflush function
Synopsis
#include <stdio.h>
int fflush(FILE *stream);

Description
Ifstream points to an output stream or an update stream in which the most recent operation was not input, the fflush function causes any unwritten data for that stream to be delivered to the host environment to be written to the file; otherwise, the behavior is undefined.
Avatar billede tangveje Nybegynder
27. september 2004 - 00:37 #13
Bertel > Kan du give et eksempel?
Avatar billede bertelbrander Novice
27. september 2004 - 00:44 #14
#include <stdio.h>
#include <stdlib.h>

int GetInt(const char *aPrompt)
{
  while(1)
  {
      char Buf[32], *endp;
      int Res;
      printf("%s", aPrompt);
      fflush(stdout);
      fgets(Buf, sizeof(Buf), stdin);
      Res = strtol(Buf, &endp, 10);
      if(*endp == '\n')
        return Res;
  }
}

int main(void)
{
  int x = GetInt("Skriv et heltal:");
  printf("Du Skrev: %d\n", x);
  return 0;
}
Avatar billede tangveje Nybegynder
27. september 2004 - 00:47 #15
Nice, det virker :) Mange tak begge to. Bertel, lægger du er svar så du kan få del i pointsne?
Avatar billede bertelbrander Novice
27. september 2004 - 00:49 #16
Eller hvis man vil kunne læse andre typer:

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

void GetInt(const char *aPrompt, const char *aFormat, void *aData)
{
  while(1)
  {
      char Buf[32];
      printf("%s", aPrompt);
      fflush(stdout);
      fgets(Buf, sizeof(Buf), stdin);
      if(sscanf(Buf, aFormat, aData))
        return;
  }
}

int main(void)
{
  int x;
  GetInt("Skriv et heltal:", "%d", &x);
  printf("Du Skrev: %d\n", x);
  return 0;
}

Jeg foretrækker dog en løsning med cin & cout og en template funktion.
Avatar billede bertelbrander Novice
27. september 2004 - 00:53 #17
F.ex:

#include <sstream>
#include <iostream>
#include <string>

template<typename T>
void Get(T &t, const char *aPrompt)
{
  while(1)
  {
      std::cout << aPrompt;
      std::string s;
      std::getline(std::cin, s);
      std::stringstream ss(s);
      if(ss >> t)
        return;
  }
}

int main()
{
  int x;
  Get(x, "Enter a int: ");
  std::cout << "You entered: " << x << std::endl;
  double d;
  Get(d, "Enter a double: ");
  std::cout << "You entered: " << d << std::endl;
}
Avatar billede bertelbrander Novice
27. september 2004 - 00:54 #18
Jeg vil helst ikke have point.
Avatar billede tangveje Nybegynder
27. september 2004 - 00:55 #19
Helt i orden, men tak skal du i hvert fald have :)
Avatar billede arne_v Ekspert
27. september 2004 - 06:19 #20
Sorry.

Jeg lod mig vejlede/vildlede af den her:
  http://www.cppreference.com/stdio_details.html#fflush
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester