PDA

View Full Version : [C++] Stringhe standard


zanardi84
24-03-2011, 16:11
Torno ad affrontare il c++ con qualche nuovo esperimento.
Ho inizialmente gestito le stringhe con gli string, ma il c++ standard dovrebbe prevedere l'uso dei vettori di caratteri char.

Ho imbastito questo semplice codice per cercare di capire quello che combina.

char input[4];
cin.getline(input, 4);
cout << input;

notando che:

Se digito direttamente invio mi stampa di fatto una riga vuota.
Se digito sino a 3 caratteri me li stampa.
Se digito 4 caratteri o più, me ne stampa 3.

So che una stringa prevede il carattere terminatore \0.
Significa che i caratteri digitati e visibili in output sono 3 perchè il quarto è il terminatore e non è visibile in output?

Quando digito più di 4 caratteri, in cin rimangono i caratteri seguenti che poi possono finire da qualche parte e alterare qualcosa? Come lo pulisco?
E gli spazi? Come li tolgo?

Mi chiedevo poi come poter gestire il controllo della stringa che dovrebbe contenere solo cifre per una conversione a intero. Pensavo di usare un algoritmo che avevo già utilizzato, cioè scansionare la stringa e testare con isdigit ogni singolo elemento, prevedendo poi il caso di numero che inizia con 0, come 02, di cui segnalerei l'errore. Esistono sistemi migliori?

tuccio`
24-03-2011, 16:41
in realtà le stringhe intese come vettori di caratteri terminati da '\0' sono più proprie del C che del C++, la classe string è standard ed è decisamente più comoda per fare le operazioni che hai elencato tu

inoltre la classe stringstream può fare il lavoro che dici sui numeri, qui trovi un esempio: http://www.cplusplus.com/reference/iostream/istream/operator%3E%3E/

tuttavia, con ogni probabilità, i numeri che iniziano con la cifra 0 vengono interpretati come ottali (in linea con quello che succede in C e C++ quando imposti il valore di una variabile intera)

zanardi84
24-03-2011, 16:48
in realtà le stringhe intese come vettori di caratteri terminati da '\0' sono più proprie del C che del C++, la classe string è standard ed è decisamente più comoda per fare le operazioni che hai elencato tu

inoltre la classe stringstream può fare il lavoro che dici sui numeri, qui trovi un esempio: http://www.cplusplus.com/reference/iostream/istream/operator%3E%3E/

tuttavia, con ogni probabilità, i numeri che iniziano con la cifra 0 vengono interpretati come ottali (in linea con quello che succede in C e C++ quando imposti il valore di una variabile intera)

Mi è stato chiesto di usare le stringhe come si usa nel C tradizionale, quindi è come se dovessi reinventare la ruota :(

tuccio`
24-03-2011, 16:55
allora per quanto riguarda la storia dei numeri, c'è la funzione strtol: http://www.cplusplus.com/reference/clibrary/cstdlib/strtol/

invece per quanto riguarda gli spazi, se vuoi toglierli ti tocca scrivere una funzione che lo faccia

zanardi84
29-03-2011, 15:33
Non sono ancora riuscito a risolvere la questione per l'acquisizione.

Se eseguo questo codice:

char stringa [5];
cin.getline(stringa, 5);
cout << stringa << endl;
cin.getline(stringa, 5);
cout << stringa << endl;

Se digito per esempio (senza "") "qwer" viene stampato e poi mi viene proposto un secondo inserimento.

Se invece digito qualcosa di più lungo, mi stampa i 4 caratteri e poi, a capo, riga vuota prima di concludersi regolarmente, e questo non va per niente bene.
Come posso risolvere?

tuccio`
29-03-2011, 16:49
vuol dire che ti serve un buffer più grande dei miseri 5 caratteri ^_^

zanardi84
29-03-2011, 17:05
vuol dire che ti serve un buffer più grande dei miseri 5 caratteri ^_^

Eh no, non risolve il problema, perchè potrei digitare 1000 caratteri a fronte di un buffer di 999 e sarei al punto di partenza.

Ho provato ad aggiungere:

cin.getline(stringa, 5);
cout << stringa << endl;
cin.clear();
fflush(stdin);
cin.getline(stringa, 5);
cout << stringa;

per resettare il cin e inserire, e sembra funzionare, ma sinceramente non so se è un reale corretto funzionamento e soprattutto se raggiunge lo scopo che mi sono posto. Che dite?

cin prevede un buffer che contiene i caratteri digitari giusto? Quindi cin.getline(destinazione, N) mi legge N caratteri da quel buffer. Se il buffer contiene più di N caratteri, se non lo resettassi rimarrebbero i caratteri rimanenti a partire da quello corrispondente al tasto invio che premo per stampare la prima stringa del codice (e sarebbe spiegata anche la ragione della riga vuota al secondo passaggio).
Dico bene?
Il buffer è in una zona di memoria riservata che non pasticcia su quella per il programma? Cioè, la presenza di caratteri in più, anche se ne facessi leggere il numero giusto, comporta qualche malfunzionamento o in questo caso devo solo occuparmi di non superare il limite della stringa che conterrà quanto voglio fare leggere alla getline?

darksax
29-03-2011, 20:45
Da MSDN...
"IF the function extracts _Count - 1 elements, it calls setstate(failbit). In any case, it returns *this."

In pratica la prima chiamata ti legge i dati ma avendo piu' caratteri, ti stampa la stringa corretta ma imposta il bit d'errore.
Di conseguenza la seconda chiamata viene ignorata.

Per verificare l'eventuale errore puoi inserire dopo la prima cout le righe:

bool b=cin.fail();
cout << "Fail=" << b << endl;

Se inserissi solo la .clear, otterresti la stampa dei successivi dati inseriti nella prima cin.


Spero di essere stato chiaro
Ciao

zanardi84
30-03-2011, 09:01
Da MSDN...
"IF the function extracts _Count - 1 elements, it calls setstate(failbit). In any case, it returns *this."

In pratica la prima chiamata ti legge i dati ma avendo piu' caratteri, ti stampa la stringa corretta ma imposta il bit d'errore.
Di conseguenza la seconda chiamata viene ignorata.

Per verificare l'eventuale errore puoi inserire dopo la prima cout le righe:

bool b=cin.fail();
cout << "Fail=" << b << endl;

Se inserissi solo la .clear, otterresti la stampa dei successivi dati inseriti nella prima cin.


Spero di essere stato chiaro
Ciao

Ho capito solo che setta il bit d'errore se il buffer non è stato tutto consumato. La cin.clear() dovrebbe risettare il bit, giusto?
Ma poi?

darksax
30-03-2011, 11:11
Se fai solo la .clear, ottieni l'esecuzione della seconda getline(), che elaborera' il restante contenuto dello stream di input (cin).

Quindi sei tu che devi decidere...
Puoi gestirti un loop per elaborare/svuotare l'input dell'utente (utilizzando la clear)

-----------(p.e.)---------
cin.getline(stringa, 5);
cout << stringa << endl;

while cin.fail() // se errore ho un overflow. leggo e svuoto!
{
cin.clear();
cin.getline(stringa, 5);
cout << stringa << endl;
}
--------------------------


oppure semplicemente buttare l'eccedenza (.clear e fflush).
Tieni conto che il numero di caratteri gestiti dalla getline e' il valore del secondo parametro -1.

A questo punto per la conversione in numerico puoi utilizzare strtol/atoi/atol etc.

Ciao

zanardi84
30-03-2011, 13:39
Se fai solo la .clear, ottieni l'esecuzione della seconda getline(), che elaborera' il restante contenuto dello stream di input (cin).

Quindi sei tu che devi decidere...
Puoi gestirti un loop per elaborare/svuotare l'input dell'utente (utilizzando la clear)

-----------(p.e.)---------
cin.getline(stringa, 5);
cout << stringa << endl;

while cin.fail() // se errore ho un overflow. leggo e svuoto!
{
cin.clear();
cin.getline(stringa, 5);
cout << stringa << endl;
}
--------------------------


oppure semplicemente buttare l'eccedenza (.clear e fflush).
Tieni conto che il numero di caratteri gestiti dalla getline e' il valore del secondo parametro -1.

A questo punto per la conversione in numerico puoi utilizzare strtol/atoi/atol etc.

Ciao

Grande!! Adesso ho capito.

Per curiosità se tentassi un approccio così?

char buffer[1000];
char stringa[5];
cin.getline(stringa, buffer, 4);
stringa[4] = '\0';


L'ultima domanda è: quale dei due approcci che hai postato è migliore?

Già che ci sono: visto che devo trattare anche float e double, che approccio mi consigli per la conversione?

darksax
30-03-2011, 15:09
Grande!! Adesso ho capito.

Per curiosità se tentassi un approccio così?

char buffer[1000];
char stringa[5];
cin.getline(stringa, buffer, 4);
stringa[4] = '\0';


L'ultima domanda è: quale dei due approcci che hai postato è migliore?

Già che ci sono: visto che devo trattare anche float e double, che approccio mi consigli per la conversione?

Se non ricordo male, l'eventuale terzo parametro della getline() e' il carattere delimitatore (che di default e' \n).
Cioe' quando dallo standard di input preleva un carattere corrispondente al delimiter (p.e. il tab), interrompe la lettura ed esce.
(Normalmente questo sistema lo usi per la lettura di file e non per lo streaming prodotto dalla tastiera).
Inoltre il \0 finale lo dovrebbe inserire la getline() a "gratisse" ma ti consiglio di verificare (ah la memoria!!!).
Un buon giro di debug aiuta a capire tanti comportamenti "nascosti".

Per gli approcci dipende da te.
Personalmente se prevedo un input di 10 caratteri e l'utente me ne inserisce 20, io normalmente elimino il superfluo!!!

Se al contrario prevedo un input di 500 caratteri, allora mi adatto ad una gestione un po' piu' "amichevole".

nel tuo caso penso che un'input secco buttando l'eccesso sia preferibile visto che in fondo devi inserire semplicemente dei numeri.

per le conversioni:
atoi(), atol(), strtol() convertono da Ascii/STRinga ad Intero/Long
atof(), strtod() convertono da Ascii/STRinga ad double

PS occhio che se i risultati non ti tornano, dai un'occhiata ad eventuali cast...

Ciao

zanardi84
30-03-2011, 15:52
Se non ricordo male, l'eventuale terzo parametro della getline() e' il carattere delimitatore (che di default e' \n).
Cioe' quando dallo standard di input preleva un carattere corrispondente al delimiter (p.e. il tab), interrompe la lettura ed esce.
(Normalmente questo sistema lo usi per la lettura di file e non per lo streaming prodotto dalla tastiera).
Inoltre il \0 finale lo dovrebbe inserire la getline() a "gratisse" ma ti consiglio di verificare (ah la memoria!!!).
Un buon giro di debug aiuta a capire tanti comportamenti "nascosti".

Per gli approcci dipende da te.
Personalmente se prevedo un input di 10 caratteri e l'utente me ne inserisce 20, io normalmente elimino il superfluo!!!

Se al contrario prevedo un input di 500 caratteri, allora mi adatto ad una gestione un po' piu' "amichevole".

nel tuo caso penso che un'input secco buttando l'eccesso sia preferibile visto che in fondo devi inserire semplicemente dei numeri.

per le conversioni:
atoi(), atol(), strtol() convertono da Ascii/STRinga ad Intero/Long
atof(), strtod() convertono da Ascii/STRinga ad double

PS occhio che se i risultati non ti tornano, dai un'occhiata ad eventuali cast...

Ciao

Hai perfettamente ragione, ho scritto una roba senza senso.

Non era quel cin.getline, ma era cin.getline(buffer, 1000) e poi strncpy(stringa, buffer, 4), seguita da stringa[4] = '\0'.

darksax
30-03-2011, 16:43
In questo modo rischi di confondere l'utente.
Pensi di aver inserito "12345678" e poi ti accorgi che ti elabora solo "1234".

Allora piu' carino sarebbe
prendi l'input di 4 caratteri (cin.getline(stringa, 5);)
e se vai in overflow mandi un msg di errore (input troppo lungo), pulisci lo stream e richiedi l'input.

Ovviamente il tutto sempre se vale la pena e ti va di "divertirti"...:)

tomminno
30-03-2011, 16:52
Ma visto che si parla di C++ e "Stringhe standard" perchè non usare le stringhe standard (string) del C++?
Che tra l'altro risolverebbero ogni problema, in questo caso.

zanardi84
30-03-2011, 16:54
In questo modo rischi di confondere l'utente.
Pensi di aver inserito "12345678" e poi ti accorgi che ti elabora solo "1234".

Allora piu' carino sarebbe
prendi l'input di 4 caratteri (cin.getline(stringa, 5));
e se vai in overflow mandi un msg di errore (input troppo lungo), pulisci lo stream e richiedi l'input.

Ovviamente il tutto sempre se vale la pena e ti va di "divertirti"...:)

Ho due problemi con l'ultima soluzione: se digitando sforassi oltre la dimensione del buffer? Cosa succederebbe?

Come posso fare il confronto sull'overflow, e su cosa?

zanardi84
30-03-2011, 16:56
Ma visto che si parla di C++ e "Stringhe standard" perchè non usare le stringhe standard (string) del C++?
Che tra l'altro risolverebbero ogni problema, in questo caso.

Gli string sono oggetti e nascondono le insidie! Usare gli elementi di base costruendo le proprie funzioni è più istruttivo per chi sta imparando. Gli string sono una stupidata da implementare (nonchè una figata!)

darksax
30-03-2011, 18:53
Ho due problemi con (....cut....)

La cin.getline() si preoccupa di metterti il terminatore di stringa (aka \0) in caso di input superiore.

Per il test dell'overflow puoi usare

if cin.fail()
errore

Comunque, riflettendo sui commenti degli altri, devo dire che in realta' non stai utilizzado il C puro (K&R)
E' vero per quel che riguarda la definizione della variabile char, ma nel C la "cin.getline()" te la sogni!!!!
Ti dovresti ingegnare con la getchar e dintorni. E li si che avresti tutti i problemi inerenti il "buffer overflow" ed affini.

Ma questa (come dice uno tizio della televisione) e' un'altra storia

Ciao

zanardi84
31-03-2011, 17:18
Risolto! Ora dovrò trovare qualcosa per scandire la stringa per ammettere il punto e/o la virgola come separatore. Qualche idea?