Προς το περιεχόμενο

Εργασία σε C


Nereus

Προτεινόμενες αναρτήσεις

Δημοσ.

Στην δικιά μου φυσικά δεν έβαλα κανέναν έλεγχο για whitespace και τοιάυτα. Μπορείς εύκολα να το βάλεις εσύ.

 

Επίσης, αν θυμάμαι καλά, στο K&R πρέπει να είχε μια καλή συνάρτηση για το διάβασμα integers.

Δημοσ.
Το διάβασα.:mrgreen:

[..]

2. Σε μια απο τις άσκησεις που έχω ο χρήστης θα πρέπει να εισάγει 2 τιμές οι οποίες να χωρίζονται με space. Δηλαδη έχω γράψει scanf("%d %d"&a,&B). Δεν ξέρω πως σε αυτό χρησημοποιήται η gets().

[..]

 

Ο παρακάτω κώδικας με την βοήθεια της εντολής strtok σπάζει την ενιαία είσοδο της gets με βάση τα κενά που περιέχει έτσι ώστε κάθε χωριζόμενο από κενό αλφαριθμητικό σύνολο να επιστρέφεται ως ανεξάρτητο char* pszToken το οποίο με την σειρά του τροφοδοτείται στην _TrueNumber.

 

Εάν η ρουτίνα _TrueNumber επιστρέψει επιτυχία, ότι δηλαδή πρόκειται για έναν πραγματικό αριθμό, τότε το char* pszToken μετατρέπεται σε αριθμό (atoi) αποθηκευόμενο κατά σειρά εισόδου στην ανάλογη θέση του πίνακα int (πχ. int nNumbers[2]) που έχουμε δηλώσει κατά την ανάπτυξη της εφαρμογής.

 

Για ευελιξία υπολογίζουμε αυτόματα το πλήθος των αριθμών που μπορεί να χωρέσει ο πίνακας μας στην μεταβλητή nNumbersElements βάση του τύπου sizeof(nNumbers)/sizeof(int).

 

Συνεπώς εάν το πλήθος των αριθμών εισόδου μας, που εντόπισε η strtok (nNumbersIdx) είναι μικρότερο ή μεγαλύτερο από τις διαστάσεις του πίνακα μας (nNumbersElements) επιστρέφουμε και το αντίστοιχο μήνυμα λάθους στον χειριστή, το ίδιο συμβαίνει και στην ειδική περίπτωση που η _TrueNumber αποτύχει οπότε θέτουμε την nNumbersIdx=-1 ώστε να αποκλείσουμε την εμφάνιση άλλων μηνυμάτων λάθους πέραν εκείνου της _TrueNumber.

 

>
//---------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <conio.h>
//---------------------------------------------------------------------------
char  _szUserInput[128], *pszToken  = NULL;
int   nNumbers    [2]  ,
     nNumbersElements  =  sizeof(nNumbers)/sizeof(int),
     nNumbersIdx       =  -1;
//---------------------------------------------------------------------------
int   _TrueNumber (char *pszBuffer);
//---------------------------------------------------------------------------
int main(int argc, char* argv[])
{
  for(;
   {
     printf(" Enter %d number%s",nNumbersElements,
                                 (nNumbersElements>1)?"s:":":");
     gets(_szUserInput);

     // Break _szUserInput based on space
     nNumbersIdx = 0;
     
     if((pszToken=strtok(_szUserInput," "))!=NULL)
      do{
           if(!_TrueNumber(pszToken))
            {
              nNumbersIdx = -1;
              printf(" Incorrect input - %s\n",pszToken);
              break;
            }
           else
            {
              if(nNumbersIdx<nNumbersElements)
               nNumbers[nNumbersIdx++] = atoi(pszToken);
              else
               nNumbersIdx++;
            }
        }while((pszToken=strtok(NULL," "))!=NULL);

      if(nNumbersIdx==nNumbersElements)
       break;
      else
       if(nNumbersIdx!=-1)
        printf(" %s numbers (%d) - we need %d!\n",(nNumbersIdx<nNumbersElements)?
                                                   "Too few":"Too many",
                                                   nNumbersIdx,
                                                   nNumbersElements);
   }

  for(nNumbersIdx=0;nNumbersIdx<nNumbersElements;nNumbersIdx++)
   printf(" [%d] = %d\n",nNumbersIdx,nNumbers[nNumbersIdx]);

  printf("\nPlease hit any key to quit..");

  getch();
  return 0;
}
//---------------------------------------------------------------------------
int   _TrueNumber(char *pszBuffer)
{
  int   nUserInputIdx;

  for(nUserInputIdx=0;nUserInputIdx<strlen(pszBuffer);nUserInputIdx++)
   if(!isdigit(pszBuffer[nUserInputIdx]))
    return 0;

  return 1;
}

 

Ο κώδικας έχει αναπτυχθεί σε C/C++ Builder 6 και ελπίζω να μην παρουσιάζει τεχνικά προβλήματα.

 

Καλή τύχη!

Δημοσ.

Η scanf είναι πολύ δυνατή, μπορεί να αντιμετωπίσει μόνη της το πρόβλημα.

 

Π.χ., έστω ότι θέλουμε να διαβάσουμε 2 ακέραιους και να αγνοήσουμε ότι άλλο ακολουθεί μέχρι το enter που πάτησε ο χρήστης.

Έστω επίσης ότι θέλουμε να υπάρχουν οσαδήποτε κενά ανάμεσα από τους αριθμούς.

Τέλος, έστω ότι δε θέλουμε να αφήσουμε το χρήστη στην ησυχία του μέχρι να μας δώσει ντε και καλά τους 2 αριθμούς.

 

>
#include <stdio.h>

int main(void)
{
       int a, b, ints_read;
       do {
               printf("Dwse 2 akeraious: ");
               ints_read = scanf(" %d %d", &a, &;
               scanf("%*10000[^\n]");
               printf("Diabasa %d akeraious\n", ints_read);
       } while (ints_read != 2);
       return 0;
}

 

Εξηγήσεις:

1) Στην πρώτη scanf, το κενό πριν από κάθε %d σημαίνει ότι θέλουμε να προσπεράσει οσαδήποτε κενά, tabs ή enter υπάρχουν πριν και ανάμεσα από τους αριθμούς.

 

Στη δεύτερη scanf,

2) Το % σημαίνει να διαβάσει κάτι.

3) Το * σημαίνει να το διαβάσει αλλά να μην το αναθέσει σε καμία μεταβλητή. Έτσι δε χρειάζεται να του περάσουμε μεταβλητή για να βάλει το αποτέλεσμα.

4) Το 10000 σημαίνει να διαβάσει σε έναν buffer ο οποίος είναι μέχρι δέκα χιλιάδες χαρακτήρες. Φυσικά αφού του βάλαμε * στο (3), τον buffer ούτε τον δηλώνουμε ούτε τον περνάμε παράμετρο.

5) Οι αγκύλες [] σημαίνει ότι επιτρέπουμε να διαβαστούν μόνο γράμματα που να περιέχονται μέσα στις αγκύλες. Π.χ. αν θέλαμε να διαβάσουμε μόνο "α" και "β" και να σταματάγε το διάβασμα αν ο χρήστης έδινε "γ", έπρεπε να βάλουμε [αβ].

6) Το ^ σημαίνει όχι, δηλαδή να διαβάσει οποιοδήποτε άλλο γράμμα εκτός από αυτά που ακολουθούν.

7) Το \n είναι το enter.

 

Σε απλά ελληνικά, ;) η δεύτερη scanf λέει

"Διάβασε μέχρι 10000 γράμματα αλλά άμα βρεις enter σταμάτα". Ουσιαστικά δηλαδή αδειάζει το input buffer.

Υπόψη ότι δεν μπορούμε να χρησιμοποιήσουμε fflush(stdin), η συμπεριφορά του είναι undefined με βάση το standard.

Δημοσ.

Εξηγήσεις:

1) Στην πρώτη scanf, το κενό πριν από κάθε %d σημαίνει ότι θέλουμε να προσπεράσει οσαδήποτε κενά, tabs ή enter υπάρχουν πριν και ανάμεσα από τους αριθμούς.

 

Δηλαδή χωρίς τα κενά θα άλλαζε κάτι; Π.χ. αν ήταν:

>
scanf("%d%d",&a,&.

Στη δεύτερη scanf,

2) Το % σημαίνει να διαβάσει κάτι.

3) Το * σημαίνει να το διαβάσει αλλά να μην το αναθέσει σε καμία μεταβλητή. Έτσι δε χρειάζεται να του περάσουμε μεταβλητή για να βάλει το αποτέλεσμα.

4) Το 10000 σημαίνει να διαβάσει σε έναν buffer ο οποίος είναι μέχρι δέκα χιλιάδες χαρακτήρες. Φυσικά αφού του βάλαμε * στο (3), τον buffer ούτε τον δηλώνουμε ούτε τον περνάμε παράμετρο.

5) Οι αγκύλες [] σημαίνει ότι επιτρέπουμε να διαβαστούν μόνο γράμματα που να περιέχονται μέσα στις αγκύλες. Π.χ. αν θέλαμε να διαβάσουμε μόνο "α" και "β" και να σταματάγε το διάβασμα αν ο χρήστης έδινε "γ", έπρεπε να βάλουμε [αβ].

6) Το ^ σημαίνει όχι, δηλαδή να διαβάσει οποιοδήποτε άλλο γράμμα εκτός από αυτά που ακολουθούν.

7) Το \n είναι το enter.

 

Σε απλά ελληνικά, ;) η δεύτερη scanf λέει

"Διάβασε μέχρι 10000 γράμματα αλλά άμα βρεις enter σταμάτα". Ουσιαστικά δηλαδή αδειάζει το input buffer.

Υπόψη ότι δεν μπορούμε να χρησιμοποιήσουμε fflush(stdin), η συμπεριφορά του είναι undefined με βάση το standard.

 

Νομίζω ότι κάτι σαν το εξής:

 

>
while((ch = getc(fp)) != EOF && ch != '\n');

 

είναι αρκετά πιο εύκολο.

Δημοσ.

@Sta:

1) Ναι, ανάλογα με τον compiler μπορεί να σταμάταγε σε enter κτλ. Και για άλλους τύπους δεδομένων, π.χ. strings ή χαρακτήρες είναι "υποχρεωτικό".

2) Γούστα! Αρκεί να γίνεται σωστά η δουλειά... Εγώ βασικά το έγραψα για να δείξω το τι μπορεί να κάνει η scanf... Π.χ. σε άλλες περιπτώσεις μπορεί κάποιος να θέλει να διαβάσει ένα string μέχρι 100 χαρακτήρες και να αποφύγει τυχόν buffer overrun...

Δημοσ.

Πάντως στον C++ Builder 6, στον Turbo C++ Express αλλά και στην παλιά κλασσική Turbo C (DOS) η scanf δεν επιστρέφει τιμές περισσότερες από εκείνες που μπορεί να αποθηκεύσει στις παρεχόμενες μεταβλητές της οπότε η συνθήκη while (ints_read != 2); δεν ισχύει υπό περιπτώσεις, καθώς εάν εισάγουμε πχ. 3 αριθμούς, δηλαδή παραπάνω δεδομένα από τα αναμενόμενα, η scanf επιστρέφει 2.

 

Γενικά, η scanf είναι καλή όταν είμαστε σίγουροι για την μορφή των δεδομένων εισόδου.

 

Υ.Γ.

Εάν η συμπεριφορά της scanf άλλαξε σε κάποιο νεότερο C specification δεν ξέρω..

Δημοσ.

Απ' όσο ξέρω δεν άλλαξε η συμπεριφορά της, δηλαδή αν βάλεις τη scanf να διαβάσει 2 ακέραιους και δώσεις 3, θα επιστρέψει 2 αλλά θα αφήσει τον input pointer πριν τον τρίτο αριθμό. Ο τρίτος αριθμός θα διαβαστεί με επόμενη scanf, είτε σαν δεδομένο είτε απλά για έλεγχο του στυλ "εεεε!!! μόνο 2 αριθμούς ήθελα, όχι τρεις!!!".

  • 4 εβδομάδες αργότερα...
Δημοσ.
@Sta:

1) Ναι, ανάλογα με τον compiler μπορεί να σταμάταγε σε enter κτλ. Και για άλλους τύπους δεδομένων, π.χ. strings ή χαρακτήρες είναι "υποχρεωτικό".

 

Νομίζω ότι στην προκειμένη περίπτωση τα: scanf(" %d %d",...) και scanf("%d%d",...) είναι ταυτόσημα.

Δημοσ.
>
#include <alllibraries.h> // lol

int main(void)
{
int input1;

scanf("%d", &input1);
while(input1 < 0 || input1 > 9)
{
printf("Please re-insert the number");
scanf("%d", &input1);
}

// the rest of the program goes here

return 0;
}

Δημοσ.

@Sta:

Από το specification της C, ISO/IEC 9899:TC2:

5. A directive composed of white-space character(s) is executed by reading input up to the first non-white-space character (which remains unread), or until no more characters can be read.

...

8. Input white-space characters (as specified by the isspace function) are skipped, unless the specification includes a [, c, or n specifier.244)

Με βάση το [5], βάζοντας whitespace στο directive προσπερνάμε τυχόν κενά.

Με βάση το [8], για ακεραίους όντως τα "%s%s" και " %s %s" είναι ισοδύναμα (όπως είπες).

 

@myle:

Από το ίδιο specification:

If this object does not have an appropriate type, or if the result of the conversion cannot be represented in the object, the behavior is undefined.

Δηλαδή

1) Αν στο πρόγραμμα αυτό δώσεις γράμματα για είσοδο, τότε η scanf δεν πρόκειται να τα προσπεράσει ποτέ, οπότε θα γίνει άπειρο loop χωρίς να ξαναζητηθεί είσοδος από το χρήστη.

2) Στην περίπτωση αυτή η τιμή του input1 είναι απροσδιόριστη, οπότε δεν είναι σωστό να ελέγχεται (> 0 ή < 9).

Αρχειοθετημένο

Αυτό το θέμα έχει αρχειοθετηθεί και είναι κλειστό για περαιτέρω απαντήσεις.

Επισκέπτης
Αυτό το θέμα είναι πλέον κλειστό για περαιτέρω απαντήσεις.
  • Δημιουργία νέου...