Newsgroup: it.comp.lang.c
Titolo: [LONG] Problemi con stringhe:

Salve a tutti,
ho un problema che probabilmente qualcuno potra aiutarmi a risolvere con
poco sforzo:
	typedef struct {int age,year ; char name[20];} person;
	int index;
	char input;
	index = get_first_free_list_index();
	list[index] = (person *) malloc(sizeof(person));
	printf("\nFirst available Index found: %d",index);
	printf("\nEnter name: ");
	scanf("%s",(*list[index]).name); //              <----- PROBLEMA
	//gets((*list[index]).name);
	do
	{
		printf("Enter birth year: ");
		scanf("%d",&(*list[index]).year);
	}
	while(((*list[index]).year == 0) || ((*list[index]).year > YEAR));
Il problema è il seguente:
con il metodo adottato da questo codice accade che se nel nome immetto uno
spazio, le stringa effettiva si ferma allo spazio (escluso), e gli altri
membri del record vengono inquinati dall'input in questione! Con numeri o
caratteri come il punto esclamativo va tutto liscio invece...
Provando a sostituire la scanf con gets, il problema invece è che la gets
viene completamente ignorata in esecuzione e senza chiedermi il nome, il
rpogramma passa subito alla richiesta dell'età... non ho capito come mai...
posto anche l'output del programma per chiarificare (ottenuto con il codice
presente sopra... senza gets per intenderci):
----------------------------------
 1. Enter new person;
 2. Show registered people;
 3. Sort people list;
 4. Erase person;
 5. Exit.
----------------------------------
Choice: 1
First available Index found: 2
Enter name: Marco
Enter birth year: 1984
Add to database 'Marco' born in 1984, 20 years old? (y/n)
Answer = y
----------------------------------
 1. Enter new person;
 2. Show registered people;
 3. Sort people list;
 4. Erase person;
 5. Exit.
----------------------------------
Choice: 1
First available Index found: 3
Enter name: Mamma Mia!
Enter birth year: Add to database 'Mamma' born in -842150451, 842152455
years ol
d? (y/n)
Answer = Answer = y
----------------------------------
 1. Enter new person;
 2. Show registered people;
 3. Sort people list;
 4. Erase person;
 5. Exit.
----------------------------------
Choice: 2
List:
┌─────┬─────────────────┬─────┬──────┬────────┐
│  I# │ NAME            │ AGE │ YEAR │ MEMADD │
│   0 │ Marco           │  20 │ 1984 │ 431f90 │
│   1 │ Poochiettone    │  22 │ 1982 │ 431f40 │
│   2 │ Marco           │  20 │ 1984 │ 431ef0 │
│   3 │ Mamma           │ 842152455 │ -842150451 │ 431ea0 │
└─────┴─────────────────┴─────┴──────┴────────┘
----------------------------------
 1. Enter new person;
 2. Show registered people;
 3. Sort people list;
 4. Erase person;
 5. Exit.
----------------------------------
Choice:
-- 
Poochie ©
Teoricamente la teoria è uguale alla pratica, ma in pratica non lo è...

 Provando a sostituire la scanf con gets, il problema invece è che la
gets
Certo.  Mai mischiare scanf() e gets().
scanf() si ferma al primo spazio incontrato e tutto il resto rimane
nel buffer di ingresso, ad uso e consumo delle altre funzioni di
input.  Se vuoi svuotare il contenuto del buffer, dopo ogni lettura
con scanf() devi usare una istruzione del tipo:
while (getchar() != '\n')
      ;

 while (getchar() != '\n');
Intendi mai usare entrambe funzioni nello stesso programma se non mettendo 
il frammento di codice da te indicato dopo ogni scanf ?
Tipo:
scanf("%d",&numero);
while (getchar() != '\n');
gets(stringa);


 Teoricamente la teoria è uguale alla pratica, ma in pratica non lo è...
Ciao,
Sinetris

 il frammento di codice da te indicato dopo ogni scanf ?
E' sempre meglio, secondo me, usare fgets(), magari occupandosi per
conto proprio di risolvere problemi di "parsing" della stringa in
input.  scanf() č tutt'altro che agevole da usare.  In generale, č
sempre bene non mischiare mai funzioni diverse di input.

 fgets(stringa, sizeof(stringa), stdin);
Ovviamente, sizeof(), in questo caso, vale solo se "stringa" è un
array di caratteri (perché in questo caso "stringa" non decade in
puntatore al primo elemento e quindi l'applicazione di sizeof
restituisce la dimensione effettiva del buffer allocato), non vale nel
caso "stringa" sia un puntatore a caratteri.  In quest'ultimo caso
bisogna esplicitamente passare la lunghezza (come hai fatto tu nella
tua funzione).
BTW, si dice "length", non "lenght" ;-)

 sempre bene non mischiare mai funzioni diverse di input.
Ho provato la fgets() ma accade che utilizzandola, mi salva nella stringa
anche il carattere di ritorno a capo, cosa che con gets non succedeva! È
normale?


 normale?
Sě.  Puoi eliminarlo:
char s[100], char *p;
fgets(s, sizeof(s), stdin);
for (p = s; *p; p++)
    if (*p == '\n')
       *p = '\0';

Anno Domini Fri, 13 Feb 2004 08:39:36 +0100, Andrea Laforgia, nobilissimus
apud internautas, scripsit:
Ah, un'altra cosa: tutte le considerazini sul fatto che scanf sia meglio
non utilizzarlo vale solo per le stringhe o anche se devo acquisire un
valore numerico?
Grazie


 valore numerico?
Vale per qualsiasi uso.  Fai conto che non esista.

> Ah, un'altra cosa: tutte le considerazini sul fatto che scanf sia

Interessante... e quale funzione dovrei usare per leggere un float o un
intero?
Ri-grazie per i consigli! ;)


 normale?
Si. E' normale che sia così. Resta da spiegare l'utilità di una simile
caratteristica di fgets().
In ogni caso avrei una soluzione alternativa da suggerirti, oltre alla fgets
che ti costringe a puntare la prima occorrenza del carattere '\n' ed a
sostituirla con uno '\0'.
puoi utilizzare la funzione strstr per raggiungere lo scopo:
void elimina_rc (char string[])
 {
   char *tmp = strstr(string,"\n");
   *tmp = '\0';
 }
Il metodo alternativo per ricevere una stringa da input inframezzata da
spazi e tutto quello che vuoi sarebbe il seguente:
void inizializza_stringa (char string[])
 {
  int i=0;
  char c;
  fflush(stdin);
  while (( c = getchar()) != '\n')
    string [i++] = c;
  string[i]= '\0';
 }
Marcello
---
--
Outgoing mail is certified Virus Free.
Checked by AVG anti-virus system (
http://www.grisoft.com
).
Version: 6.0.587 / Virus Database: 371 - Release Date: 12/02/2004

 }
Hum, non e` sicuro che nella stringa ci sia il newline. Ed in ogni caso
il controllo sul valore di ritorno ci starebbe bene:
void elimina_rc (char string[]) {
        char *tmp = strstr(string, "\n");
        if (tmp)
                *tmp = '\0';
}
Poi, visto che si tratta di cercare un singolo carattere, e` meglio
usare strchr (che dovrebbe anche essere piu` veloce):
void elimina_rc (char string[]) {
        char *tmp = strchr(string, '\n');
        if (tmp)
                *tmp = '\0';
}
Luca
-- 
Home: 
http://kronoz.cjb.net
Collect some stars to shine for you
And start today 'cause there's only a few
A sign of times my friend

 Hum, non e` sicuro che nella stringa ci sia il newline.
Non è sicuro?
Con fgets?


Con fgets?
   No, se l'ultima riga del file non contiene un newline alla fine.
-- 
S.L.

 ha scritto nel messaggio
news:238r2052f6p1hlqjr...@4ax.com...

Ma, se io scrivo nel file con fgets, avrò un newline anche alla fine
dell'ultima riga, o no?


 }
In termini di velocità suppongo siano identiche, ma hai ragione.
Concettualmente è più corretto usare strchar.


 Con fgets?
Supponi di avere un buffer di 5 caratteri:
char buff[5];
fgets(buff, 5, file);
Se una linea e` piu` lunga di 4 caratteri nel buffer il newline non ci
sara`. Es:
linea da leggere: "abc\n"
fgets(buff, 5, file);
buff => {'a', 'b', 'c', '\n', '\0'}
linea da leggere: "abcde\n"
fgets(buff, 5, file);
buff => {'a', 'b', 'c', 'd', '\0'}
fgets(buff, 5, file);
buff => {'e', '\n', '\0'}
Luca
-- 
Home: 
http://kronoz.cjb.net
Ligabue canta: "Tutti vogliono viaggiare in primaaaa..."
Io ci ho provato e dopo un chilometro ho fuso il motore e bruciato
la frizione...

 dell'ultima riga, o no?
fgets non scrive ;) E non esiste una funzione che scriva automaticamente
il newline. E anche se esistese non e` mai buona pratica fare assunzioni
non cosi` scontate sui dati  in input. La mancata validazione dell'input
e`  una  delle  principali  cause  di  malfunzionamenti  e  problemi  di
sicurezza nel software.
Luca
-- 
Home: 
http://kronoz.cjb.net
"Su cio` di cui non si puo` parlare e` bene tacere".
 Ludwig Wittgenstein

 sicurezza nel software.
Ok. Non sono stato chiarissimo:
fgets() non scrive, ma riceve in input una stringa.
Poi io questa stringa la scrivo nel file.
Il fatto è che fgets() chiude il vettore con un carattere newline.
Almeno questo è quello che ho notato il 100% delle volte che ho usato tale
funzione.
Non sapevo che, potesse accadere che fgets() non inserisse lo '\n' nel
vettore...


 buff => {'e', '\n', '\0'}
Ok.
Di norma, prima di inizializzare qualsiasi variabile con un qualsivoglia
valore, ci si dovrebbe prendere la briga di assicurarsi che sia stata
allocata memoria sufficiente, ma mi rendo conto che ciň non sia possibile in
maniera assoluta.
La conclusione č che tu hai ragione ed io ho torto... :(


funzione.
   Supponiamo un file cosi` fatto:
======
pippo<\n>
pluto<\n>
topolino<EOF>
======
   quando fgets leggera` la riga contenente topolino, non inserira` un
\n, perche` non lo trovera` nel file, arrivera` alla fine del file
prima di trovare un \n.
   Per definizione fgets legge il numero massimo di caratteri
specificato nell'invocazione, si ferma prima se legge un \n (e allora
lo inserisce nell'output) o se raggiunge la fine del file (e allora
non aggiunge alcun \n automaticamente). Inoltre, fgets potrebbe non
inserire un \n, perche` si e` fermata prima in lettura, avendo
raggiunto il limite massimo di caratteri specificati dal chiamante.
-- 
S.L.

 ha scritto nel messaggio
news:1dos20hdnicmm3g6k...@4ax.com...

Hai ragione. Sei stato chiarissimo.
Grazie.


 intero?
Sempre fgets() e poi trasformi la stringa in numero con le apposite
funzioni di conversione della libreria standard.

>   fflush(stdin);
Oltre alle valide risposte di Kronos, aggiungo che il comportamento di
fflush() non è definito per gli stream di input, ma solo per quelli di
output.  Questo utilizzo di fflush(), quindi, potrebbe essere
pericoloso.