Λοιπόν, πάμε τώρα να βελτιώσουμε την λύση της 1ης μας άσκησης, χρησιμοποιώντας την στάνταρ συνάρτηση:
- Κώδικας: Επιλογή όλων
int sscanf (const char *s, const char *template, …); // #include <stdio.h>
Η συνάρτηση αυτή λειτουργεί ακριβώς όπως η γνωστή μας (και συχνά προβληματική)
scanf() με τη διαφορά πως παίρνει ένα έξτρα όρισμα στην αρχή της, που είναι ένα string. Αντί να διαβάζει δηλαδή απευθείας από την κύρια είσοδο, διαβάζει μέσα από ένα string που της το περνάμε σαν 1ο της όρισμα.
Άρα για να την χρησιμοποιήσουμε θα πρέπει πρώτα να διαβάσουμε την γραμμή εισόδου μέσα σε ένα μεγάλο string (το inbuf στην περίπτωσή μας) το οποίο κατόπιν θα το περάσουμε σαν 1ο όρισμα της sscanf(). Τα υπόλοιπα θα είναι ακριβώς ότι θα γράφαμε στην scanf() αν διαβάζαμε απευθείας από την κύρια είσοδο.
Υπάρχουν πολλά πλεονεκτήματα σε αυτόν τον τρόπο!
Πρώτον ο χρήστης μπορεί να μας πληκτρολογήσει πολλές τιμές στην ίδια γραμμή, τις οποίες εμείς μπορούμε να τις διαβάσουμε σε κατάλληλες μεταβλητές, όπως θα κάναμε και με την απλή scanf.
Έπειτα, δεν χρειάζεται να χρησιμοποιήσουμε τις συναρτήσεις atoi, atol, atof, κλπ για να μετατρέψουμε την είσοδο σε αριθμούς, γιατί αυτές οι μετατροπές γίνονται αυτόματα από την sscanf() ανάλογα τα % που θα χρησιμοποιήσουμε μέσα στην sscanf (%d, %c, %f, %ld, κλπ, κλπ).
Ακόμα, η sscanf (όπως και η scanf) μας επιστρέφει το πλήθος των εκχωρήσεων που έγιναν σε μεταβλητές. Οπότε αν για παράδειγμα ο χρήστης μας πληκτρολογήσει λιγότερες ή περισσότερες από τις τιμές που περιμένουμε να διαβάσουμε, έχουμε τρόπο να το ελέγξουμε και να πράξουμε ανάλογα.
Και τέλος, γλιτώνουμε το πρόβλημα με τυχόν απομεινάρια στην κύρια είσοδο.
Δείτε ένα μικρό απόσπασμα κώδικα...
- Κώδικας: Επιλογή όλων
...
char inbuf[ MAXINBUF ] = "";
int n1, n2;
printf("dwste 2 akeraioys arithmoys: ");
s_get( inbuf, MAXINBUF );
sscanf( inbuf, "%d %d", &n1, &n2);
printf("dwsate toys arithmoys: %d kai %d\n", n1, n2);
...
Ο παραπάνω κώδικας αφενός δεν υποφέρει από το πρόβλημα των απομειναριών στην κύρια είσοδο κι αφετέρου όχι μόνο μας επιτρέπει να δώσουμε και τους 2 αριθμούς στην ίδια γραμμή, αλλά τους εκχωρεί κιόλας στις μεταβλητές μας χωρίς να χρειάζεται να τις μετατρέψουμε εμείς χειροκίνητα με την atoi().
Επίσης, από μόνη της η scanf αν ο χρήστης μας πληκτρολογήσει έναν αντί για δυο αριθμούς που περιμένουμε και πατήσει ENTER, η συνάρτηση θα διαβάσει τον πρώτο στην μεταβλητή n1 και στην μεταβλητή n2 θα βάλει την τιμή 0 (δοκιμάστε το να το δείτε και μόνοι σας). Ακόμα, αν ο χρήστης μας πληκτρολογήσει περισσότερα από 2 νούμερα, τότε η συνάρτηση θα κρατήσει μονάχα τα 2 πρώτα και θα τα εκχωρήσει στις μεταβλητές n1 και n2, αγνοώντας τα υπόλοιπα (δοκιμάστε το κι αυτό μόνοι σας).
Αν τώρα θέλουμε να ελέγξουμε επί τούτου την περίπτωση που θα μας δώσει μονάχα ένα νούμερο, ώστε να του πούμε να ξαναπροσπαθήσει από την αρχή, μπορούμε να ελέγξουμε την τιμή που μας επιστρέφει η sscanf.
Στο απόσπασμα κώδικα που ακολουθεί, κάνω ότι και στο προηγούμενο με την προσθήκη ενός do-while loop στην περίπτωση που ο χρήστης μας δώσει λιγότερα από 2 νούμερα. Το loop θα συνεχίσει να τον ρωτάει μέχρι να μας δώσει τουλάχιστον 2 νούμερα.
- Κώδικας: Επιλογή όλων
...
char inbuf[ MAXINBUF ] = "";
int n1, n2;
int plithos = 0;
do {
printf("dwste 2 akeraioys arithmoys: ");
s_get( inbuf, MAXINBUF );
plithos = sscanf( inbuf, "%d %d", &n1, &n2);
} while (plithos < 2);
printf("dwsate toys arithmoys: %d kai %d\n", n1, n2);
...
Οπότε, θέλετε να προσπαθήσετε τώρα να τροποποιήσετε τον κώδικα της 1ης άσκησης ώστε να διαβάζει σε μια γραμμή το εκάστοτε ζευγάρι αριθμών (int, long και float) με την sscanf και να τυπώνει τις πράξεις μεταξύ των ζευγαριών κάθε φορά;
Μπορείτε επίσης να τροποποιήσετε και τον κώδικα της 2ης άσκησης, που διαβάζει δυο αριθμούς και μια πράξη και τυπώνει το αποτέλεσμα. Αυτή τη φορά διαβάστε και τα 3 μαζί σε μια γραμμή (προφανώς με χρήση των s_get και sscanf).
Αν έχετε απορίες, μέχρι τώρα γράψτε τες να τις λύσουμε. Αν δυσκολευτείτε με την βελτίωση των λύσεων στις 2 ασκήσεις πείτε μου να δώσω εγώ τον κώδικα. Ή πείτε μου αν θέλετε να δώσω εγώ τον κώδικα της μιας από τις δυο ασκήσεις και να γράψετε εσείς την άλλη.
ΥΓ. Αν δείτε την τεκμηρίωση του gcc στην scanf θα δείτε πως λέει πως υπάρχει απλά για λόγους συμβατότητας και προτείνει να χρησιμοποιείτε την sscanf (εννοείται πως πρέπει πρώτα να διαβάζουμε την κύρια είσοδο μονοκόμματα σε ένα μεγάλο string (το inbuf στη περίπτωσή μας) που θα το περνάμε κατόπιν σαν 1ο όρισμα της sscanf).