PDA

View Full Version : [C++] Piccolo problemino...


Balop
07-10-2009, 12:34
ciao a tutti, facendo un programma in c++ ho riscontrato un piccolo problemino,
intanto vi copio parte del codice:
void aggiungiUser (){
cout<< "Inserire codice \n";
gets(utenti[u].codice);
cout<< "Inserire nome \n";
gets(utenti[u].nome);
cout<< "Inserire cognome \n";
gets(utenti[u].cognome);
cout<< "Inserire email \n";
gets(utenti[u].email);
u++;
}
char scelta;
bool g = true;
do {
cout<<"Scegliere un'opzione fra le seguenti: \n";
cout<<"a) Aggiungi utente \n";
cout<<"b) Aggiungi mailing list \n";
cout<<"c) Iscrivi utente a mailing list \n";
cout<<"d) Cancella utente da mailing list \n";
cout<<"e) Stampa elenco iscritti a mailing list \n";
cout<<"f) Elimina mailing list \n";
cout<<"g) Uscire dal programma \n";
cout<<"----------------------------------------- \n\n";
cin>>scelta;
if (scelta == 'a') a:aggiungiUser ();
if (scelta == 'b') b:aggiungiMailingList ();
if (scelta == 'c') c:gestioneIscrizione ();
if (scelta == 'g') g=false;
} while (g);

prendendo in esempio la classe per aggiungere un utente, quando inserisco la lettera a da tastiera, non mi compare la scritta "Inserire codice" come dovrebbe, infatti se premo invio mi compaiono insieme "Inserire codice" e "Inserire nome" e posso inserire il nome, invece se voglio inserire il codice lo devo scrivere dopo la a della scelta, senza premere invio.... :mbe:
qualcuno sa illuminarmi su come potrei risolvere?

ah se al posto del menu faccio partire direttamente il metodo per aggiungere l'utente mi funziona perfettamente ma così non mi serve..

wingman87
07-10-2009, 12:58
Ultimamente c'e' un sacco di gente che posta per questo problema ;)
Praticamente quando digiti "a" e poi premi invio metti nel buffer di input due caratteri: 'a' e il carattere di ritorno a capo. Il primo cin consuma il primo carattere, cioe' 'a'. Il carattere di ritorno a capo resta nel buffer e quindi viene consumato piu' avanti, nella prima gets. Puoi risolvere velocemente aggiungendo un gets a vuoto dopo cin>>scelta; o meglio ancora, ma non sono sicuro che funzioni, facendo un fflush di stdin sempre dopo cin>>scelta;
fflush(stdin);

Balop
07-10-2009, 13:07
Ultimamente c'e' un sacco di gente che posta per questo problema ;)
Praticamente quando digiti "a" e poi premi invio metti nel buffer di input due caratteri: 'a' e il carattere di ritorno a capo. Il primo cin consuma il primo carattere, cioe' 'a'. Il carattere di ritorno a capo resta nel buffer e quindi viene consumato piu' avanti, nella prima gets. Puoi risolvere velocemente aggiungendo un gets a vuoto dopo cin>>scelta; o meglio ancora, ma non sono sicuro che funzioni, facendo un fflush di stdin sempre dopo cin>>scelta;
fflush(stdin);

grazie 1000 ho risolto usando fflush, non funzionava con il gets vuoto...
meno male che ho chiesto perchè non conoscendo questo comando non sarei riuscito a risolvere altrimenti....

comunque questo comando serve a "scorrere" lo standard input?
dal nome pare faccia qualcosa del genere....

ndakota
07-10-2009, 13:54
grazie 1000 ho risolto usando fflush, non funzionava con il gets vuoto...
meno male che ho chiesto perchè non conoscendo questo comando non sarei riuscito a risolvere altrimenti....

comunque questo comando serve a "scorrere" lo standard input?
dal nome pare faccia qualcosa del genere....

A svuotarlo, forse.

wingman87
07-10-2009, 14:05
Ha comportamenti differenti a seconda dell'utilizzo su uno stream di input o su uno di output, nel caso dello stream di input svuota il buffer, ma non sempre, dipende dalle implementazioni, per questo sarebbe meglio usare altri metodi.
In effetti usare gets a vuoto non e' la soluzione migliore sebbene funzioni per input sensati perche' potrebbe essere stata digitata in input una stringa piu' lunga dell'array passato a gets.
La soluzione piu' portabile sarebbe consumare l'input carattere per carattere:
void myFlush(){
c=getchar();
while(c!='\n') c=getchar();
}
Il problema e' che bisogna considerare che va chiamata solo quando si e' sicuri che sia rimasto un ritorno a capo nel buffer.
Scusa se l'ho scritta in C ma C++ non lo conosco granche'.

Balop
08-10-2009, 11:29
scusate ora mi è venuto un altro dubbio,
praticamente ho queste due classi:
class User {
public:
char codice [80], nome [80], cognome [80], email [80];
};

class MailingList : public User {
public:
char nomelista [80], creazione [80], iscritti [200];
};

però non so come fare con gli iscritti della classe MailingList, perchè nel testo dell'esercizio mi dice che ci devono essere massimo 100 iscritti per ogni mailing list, secondo me dovrei fare

class MailingList : public User {
public:
char nomelista [80], creazione [80];
User iscritti [100];
};

però poi non so come richiamare gli iscritti...
per ora nell'altra classe ho:
class MailServer : public MailingList {
public:
User utenti [5000];
MailingList liste [500];
.
.
cout<< "Inserire gli iscritti \n";
gets (liste[m].iscritti);

però se cambio il codice come ho pensato, per inserire gli iscritti nelle liste dovrei fare una cosa tipo

char tmp [5];
for (int i=0;; i++){
gets (liste[m].iscritti[i].nome);
if (strcmp(liste[m].iscritti[i].nome,tmp)==0) break; //cioè se inserisco un nome continua con il successivo, se invece non inserisco nessun nome esce dal ciclo for (potrebbe funzionare..)
}
qualcuno ha qualche idea??


edit: dimenticavo, sto cambiando pure perchè in un altro esercizio mi dice di trovare un utente, verificare se è iscritto ad una mailing list e chiedere se si deve cancellare... ma se faccio come avevo fatto inizialmente con il char iscritti [200], non posso confrontare il nome utente con la lista degli iscritti....

cionci
09-10-2009, 01:03
Perché usi la gets e non la getline ? Tra l'altro la gets è estremamente propensa al buffer overflow.
http://www.cplusplus.com/reference/iostream/istream/getline/
http://www.cplusplus.com/reference/string/getline/

cionci
09-10-2009, 01:29
grazie 1000 ho risolto usando fflush, non funzionava con il gets vuoto...
meno male che ho chiesto perchè non conoscendo questo comando non sarei riuscito a risolvere altrimenti....
Scordatelo quel comando, perché il suo comportamento non è definito nello standard e può essere diverso da sistema operativo a sistema operativo ;)
Per avere una alternativa semplice a fflush(stdin) in C++ basta:

string s;
getline(cin, s);

Ovviamente devi essere sicuro che ci sia almeno un carattere nel buffer di ingresso. Ed almeno un carattere nel buffer rimane, ad esempio, con tutti gli stream usando l'operatore >> (rimane \n).
Ad esempio, invece non rimane usando la istream::getline se si raggiunge il numero massimo di caratteri del buffer.

Balop
09-10-2009, 10:10
Scordatelo quel comando, perché il suo comportamento non è definito nello standard e può essere diverso da sistema operativo a sistema operativo ;)
Per avere una alternativa semplice a fflush(stdin) in C++ basta:

string s;
getline(cin, s);

Ovviamente devi essere sicuro che ci sia almeno un carattere nel buffer di ingresso. Ed almeno un carattere nel buffer rimane, ad esempio, con tutti gli stream usando l'operatore >> (rimane \n).
Ad esempio, invece non rimane usando la istream::getline se si raggiunge il numero massimo di caratteri del buffer.
ok, usato la getline al posto di fflush.. :)

comunque mi sapete dire come inserire un ritorno a capo in una stringa?
praticamente per il secondo problema che ho, quello degli iscritti, ho creato una stringa iscritti [100] dentro un ciclo for, così quando creo una mailing list posso inserire tutti gli iscritti, però dovrei anche mettere che se c'è un ritorno a capo senza nessuna scritta deve chiudere il ciclo for...
ho provato con if(liste[m].iscritti[i] == NULL), == "", == " ", == "\n", == "\r\n" ma niente....

cionci
10-10-2009, 08:20
Dipende da come è formata questa stringa.
Stampa i caratteri di questa stringa forzandoli a intero.

Balop
10-10-2009, 23:02
Dipende da come è formata questa stringa.
Stampa i caratteri di questa stringa forzandoli a intero.

non capisco cosa dovrei fare.. potresti essere più specifico?

cionci
11-10-2009, 09:05
Bisogna capire carattere per carattere come è formata questa stringa.
Perché può essere formata da \r\n, dal solo \n o può essere una stringa vuota.
Inoltre mi specifichi il tipo dlela variabile liste[m].iscritti[i] ?

Balop
13-10-2009, 10:24
per farti capire meglio ti posto l'intero programma (dovrei ancora sostituire tutte le gets con getline):
int u=0,m=0;
class User {
public:
char codice [80], nome [80], cognome [80], email [80];
};

class MailingList : public User {
public:
char nomelista [80], creazione [80];
string iscritti [100];
};

class MailServer : public MailingList {
public:
User utenti [5000];
MailingList liste [500];

void aggiungiMailingList (){ //esercizio 1
char tmp [5];
cout<< "Inserire nome della lista \n";
gets(liste[m].nomelista);
cout<< "Inserire data di creazione \n";
gets(liste[m].creazione);
cout<< "Inserire gli iscritti \n";
for (int i=0;;i++){
cin>>liste[m].iscritti[i];
if (liste[m].iscritti[i]=="\n") break;--> la riga che mi dà problemi
}
m++;
cout<< "\n";
}

void aggiungiUser (){ //esercizio 2
cout<< "Inserire codice \n";
gets(utenti[u].codice);
cout<< "Inserire nome \n";
gets(utenti[u].nome);
cout<< "Inserire cognome \n";
gets(utenti[u].cognome);
cout<< "Inserire email \n";
gets(utenti[u].email);
u++;
cout<< "\n";
}

void gestioneIscrizione (){ //esercizio 3
char cod [80],risposta;
bool flag=false;
int j=0;
cout<< "Inserire codice \n";
gets (cod);
for (; j<u; j++){
if (strcmp(cod,utenti[j].codice)==0){
cout<<"Nome "<<utenti[j].nome<<"\n";
cout<<"Cognome "<<utenti[j].cognome<<"\n";
cout<<"Email "<<utenti[j].email<<"\n";
flag = true;
break;
}
}
if (flag == false) cout<< "Utente non trovato.\n\n";
else{
for (int k=0; k<m; k++){
cout<<"Ci si vuole iscrivere alla mailing list " << liste[k].nomelista << " ? s/n\n";
cin>>risposta;
if (risposta == 's') {
for(int i=0;;i++){
if (liste[k].iscritti[i]=="") liste[k].iscritti[i]= utenti[j].nome;
// strcat(liste[k].iscritti, " ");
// strcat(liste[k].iscritti, utenti[j].nome);
}
}
}
}
}

void elencoutenti () { //esercizio 4
char scelta;
for (int i=0; i<m; i++){
cout<<"Stampare l'elenco utenti della mailing list "<<liste[i].nomelista<<" ? s/n\n";
cin>>scelta;
fflush(stdin);
if (scelta == 's'){
cout<<"Utenti iscritti: "<< liste[i].iscritti<<"\n\n";
}
}
}

//void cancellaUtente (){ //esercizio 5
//char cod [40];
//cout<< "Inserire codice \n";
//gets (cod);
//for (int i=0; i<m; i++){
//if (strcmp(cod,utenti[j].codice)==0){
// cout<<"Nome "<<utenti[j].nome<<"\n";
// cout<<"Cognome "<<utenti[j].cognome<<"\n";
// cout<<"Email "<<utenti[j].email<<"\n";
// break;
// }
// }
//
//
//}

void eliminazioneMailingList () { //esercizio 6
char nom [80];
char null [80];
cout<< "Inserire nome mailing list \n";
gets (nom);
for (int i=0; i<m; i++){
if (strcmp(nom,liste[i].nomelista)==0){
m--;
cout<< "Utenti iscritti: "<< liste[i].iscritti<<"\n";
if (liste[i+1].nomelista != NULL){
for (i; i<m; i++){
strcpy(liste[i].nomelista, liste[i+1].nomelista);
strcpy(liste[i].creazione, liste[i+1].creazione);
for(int k=0;;k++){
if (liste[i+1].iscritti[k]== "") break;
liste[i].iscritti[k]= liste[i+1].iscritti[k];
}
}
}
else m--;
}
}

}

void menu (){ // menù
char scelta;
bool g = true;
do {
cout<<"Scegliere un'opzione fra le seguenti: \n";
cout<<"a) Aggiungi utente \n";
cout<<"b) Aggiungi mailing list \n";
cout<<"c) Iscrivi utente a mailing list \n";
cout<<"d) Cancella utente da mailing list \n";
cout<<"e) Stampa elenco iscritti a mailing list \n";
cout<<"f) Elimina mailing list \n";
cout<<"g) Uscire dal programma \n";
cout<<"----------------------------------------- \n\n";
string s;
cin>>scelta;
getline(cin, s);

switch (scelta){
case 'a':
a:aggiungiUser ();
break;
case 'b':
b:aggiungiMailingList ();
break;
case 'c':
c:gestioneIscrizione ();
break;
case 'd':
break;
case 'e':
elencoutenti ();
break;
case 'f':
eliminazioneMailingList ();
break;
case 'g':
g=false;
break;
}
} while (g);
}
};

cionci
13-10-2009, 15:48
if(liste[m].iscritti[i].compare("\n") == 0)

Il problema però è che bisogna capire da quali caratteri è composta l'ultima stringa.
Per saperlo stampa con un for tutti gli elementi dell'ultima stringa:


for(int i = 0; i < ultimastringa.length(); ++i)
cout << (int)ultimastringa[i] << " ";
cout << endl;

Balop
13-10-2009, 16:27
if(liste[m].iscritti[i].compare("\n") == 0)

Il problema però è che bisogna capire da quali caratteri è composta l'ultima stringa.
Per saperlo stampa con un for tutti gli elementi dell'ultima stringa:


for(int i = 0; i < ultimastringa.length(); ++i)
cout << (int)ultimastringa[i] << " ";
cout << endl;

ma scusa in che senso che devo sapere che caratteri ci sono nell'ultima stringa...
sono io ad immettere i dati in liste[m].iscritti[i] con
for (int i=0;;i++){
cin>>liste[m].iscritti[i];
if (liste[m].iscritti[i]=="\n") break;
}


quindi sono io a scegliere cosa mettere nell'ultima stringa..
infatti per farlo funzionare per ora ho messo
if (liste[m].iscritti[i]==".") break;

così appena metto il punto si chiude il ciclo for e la lista degli iscritti, però per farlo più pulito volevo mettere che quando uno non inserisce nessun nome e preme invio si chiuda la lista degli iscritti..

cionci
13-10-2009, 16:32
Non so perché ma ero convinto che leggessi da file :D

Prova con:

if(liste[m].iscritti[i].length() == 0) break;

Balop
13-10-2009, 16:41
Non so perché ma ero convinto che leggessi da file :D

Prova con:

if(liste[m].iscritti[i].length() == 0) break;

no non funziona neanche questo..... :(

cionci
13-10-2009, 16:45
no non funziona neanche questo..... :(
Allora stampa quell'ultima stringa e vedi quali caratteri escono :D
Se non stampa niente è possibile che con cin non accetti stringhe vuote.
In tal caso utilizza getline(cin, liste[m].iscritti[i]) per leggere la stringa.

Balop
13-10-2009, 16:55
Allora stampa quell'ultima stringa e vedi quali caratteri escono :D
Se non stampa niente è possibile che con cin non accetti stringhe vuote.
In tal caso utilizza getline(cin, liste[m].iscritti[i]) per leggere la stringa.

finalmenteeee!!!!
funziona con
getline(cin, liste[m].iscritti[i]);
if (liste[m].iscritti[i]=="") break;

:D