Newsgroup: it.comp.lang.c
Titolo: scanf puo' leggere stringhe?

Salve,
per mia sfortuna mi ritrovo a dover scrivere un po di codice C dovendo
rinunciare a tutti i vantaggi del C++.
Semplicemente vorrei sapere se e come scanf possa leggere stringhe in input,
con e senza spazi.
Ho provato
char* c;
scanf("%s", &c);
Ma va tutto in crash non appeno digito qualcosa.
Grazie

 scanf("%s", &c);
Ok, ho aggiustato scrivendo char c[1024] ma mi par di aver visto in passato
codici con puntatori senza definire a priori la dimensione.
In ogni caso, per leggere intere frasi con spazi?
Grazie

 In ogni caso, per leggere intere frasi con spazi?
Consiglio: usa sempre e solo fgets().
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

 In ogni caso, per leggere intere frasi con spazi?
No problem.
scanf("%1023[^\n]",c);
Nell' ipotesi che c punti ad una regione di memoria di 1024 char, il 
convertitore di formato %1023[^\n]  legge 1023 caratteri dal buffer di 
input (o quanti ne vuoi leggere tu,  sempre che ci sia memoria allocata 
per contenerli). Occorre lasciare spazio per completare a stringa con il 
  caratter nul ('\0').
[^\n]  chiede di leggere tutti i caratteri del buffer di input fino 
(escluso) il carattere newline.
Giorgio

 Consiglio: usa sempre e solo fgets().
Guarda questo piccolo codice: praticamente legge una stringa con spazi ed
estrae la prima parola ma non funziona... anzi a volte si, molte volte no
(stampando caratteri assurdi).
#include <stdio.h>
int main() {
 char cmd[1024], comm[1024];
 int i = 0;
 fgets(cmd, 1024, stdin);
 while (!isspace(cmd[i])) {
  comm[i] = cmd[i];
  i++;
 };
 printf ("\n%s", comm);
 return 0;
}
In pratica come faccio a scandire tutta la frase estraendo le varie parole
separate da spazi?
Pensavo di mettere tutto dentro ad un ciclo while (cmd[i]) in modo da farlo
fermare quando trova il carattere nullo e modificando alcuni indici etc...
cmq prima devo risolvere questo problema.
Grazie

 Grazie
Ciao
Mtc`
P.S:
Se sai quello che fai puoi usare la sconsigliatissima strtok.

 ptr[0] = '\0';
Ma qui metti all'inizio il carattere nullo?
Ciao

 separate da spazi?
#include <stdio.h>
#include <stdlib.h>
#define TEXT1 "Quel ramo del lago di Como,che volge a mezzogiorno"
#define TEXT2 "Nel mezzo del cammin di nostra vita,"\
              "mi ritrovai per una selva oscura"
typedef struct {
        char *data;
        char *delimiters;
        unsigned long data_length;
        unsigned long pointer;
} string_tokenizer_t, *pstring_tokenizer_t;        
char *str_dup(const char *str)
{
      char *p = malloc(strlen(str)+1);
      if (p)
        strcpy(p, str);   
      return p;
}
pstring_tokenizer_t string_tokenizer_new(const char *data, const char
*delimiters)
{
        pstring_tokenizer_t instance = malloc(sizeof *instance);
        if (instance)
        {
            instance->data = str_dup(data);
            instance->data_length = strlen(instance->data);
            instance->delimiters = str_dup(delimiters);
            instance->pointer = 0;
        }
        return instance;
}
void string_tokenizer_delete(pstring_tokenizer_t instance)
{
     if (instance)
     {
        free(instance->data);          
        free(instance);          
     }
}
char* string_tokenizer_next_token(pstring_tokenizer_t instance, unsigned
long *token_length)
{
      char *result = NULL;
      while (instance->pointer < instance->data_length)
      {
            if (strchr(instance->delimiters,
instance->data[instance->pointer]))
            {
               ++instance->pointer;
               break;                               
            }
            if (result == NULL)
            {
               result = &instance->data[instance->pointer];
               *token_length = 1;
            }   
            else
            {
               ++(*token_length);
            }
            ++instance->pointer;
      }
      return result;
}
int main()
{
    char *word;
    unsigned long length;
    pstring_tokenizer_t tokenizer = string_tokenizer_new(TEXT1, ", ");
    printf("\"%s\" = ", tokenizer->data);
    while ((word = string_tokenizer_next_token(tokenizer, &length)))
    {
         char *token = calloc(length+1, 1);
         strncpy(token, word, length);
         printf("%s | ", token);
         free(token);
    }      
    printf("\n");
    string_tokenizer_delete(tokenizer);
    return 0;


               "mi ritrovai per una selva oscura"
[cut]
Eh, addirittura, hai scritto un word processor :P
Io ho risolto come segue:
int main() {
 char cmd[1024], com[1024];
 fgets(cmd, 1024, stdin);
 int i = 0, j = 0;
 while (cmd[i]) {
  while (!isspace(cmd[i]))
   com[j++] = cmd[i++];
  com[j] = '\0';
  printf ("%s\n", Completo(com));
  j = 0;
  i++;
 };
 return 0;
}
Mi pare abbastanza buono (fa quello che mi serve :)
Come mai la tua soluzione e' cosi lunga? Non ho letto il codice, ma cosa fa
oltre a scandire il testo in parole?
Ciao

 oltre a scandire il testo in parole?
La mia soluzione prevede un approccio object-oriented ed è una bella e
pronta per essere usata anche in contesti multi-thread (è thread-safe,
contrariamente alla strtok() della libreria standard).  La prossima volta,
comunque, il codice leggilo...


 comunque, il codice leggilo...
Si, gli ho dato un'occhiata dopo... semplicemente ho chiesto perche' non
vedevo la necessita' di scrivere tutto quel codice per risolvere il mio
problema. Se poi l'hai scritto per chiunque volesse usarlo anche in contesti
multi-thread (parli della presenza di piu' processi contemporaneamente?)
allora e' un'altro discorso.
Ciao

 problema. 
Se non hai neanche capito che cosa fa quel codice come fai a dirlo ? Dai
un giudizio quantitativo ? "Più corto" non necessariamente vuol dire
"migliore". 
E comunque non è un _tuo_ problema, quello di catturare i "token" di una
stringa è un problema ricorrente in programmazione (ecco il perché di
soluzioni più generiche).  La libreria standard prevede la funzione
strtok() che però, sfortunatamente, non è thread-safe.  Io credo che
convenga, molto spesso, utilizzare un approccio object-oriented, anche con
il C.  Ecco il perché di "tutto quel codice" ;-)


 il C.  Ecco il perché di "tutto quel codice" ;-)
Va be, ma questo forse va valutato in seguito. Cioe' a me interessava
trovare l'algoritmo (e l'algoritmo l'avevo gia trovato data la sua
semplicita', i problemi che ho incontrato erano nell'uso delle funzioni
specifiche del C, delle regole sugli array di char etc..).
Ciao

 andrebbero a confondersi?
Sě, tra diversi _thread_.


 Il suo uso è molto semplice:
In pratica ho una funzione a cui passo come argomento ogni parola estratta.
Quando passavo l'ultima parola estratta la funziona non dava l'output giusto
e non so perche'.
Ad es. se davo in input:
Quel ramo del lago
Scandivo la frase, le chiamate allla funzione (chiamiamola f)
f("Quel") // OK
f("ramo") // OK
f("del") // OK
f("lago") // Output sbagliato.
Se ti interessa posso postare la funzione che e' molto semplice ti acceno
semplicemente al fatto che in pratica conteneva un'array di parole e l'input
passato era una della parole contenute ma scritta in forma breve, cioe' solo
le prime k lettere di una parola (k <= parola.size() :)
Ciao

 In pratica ho una funzione a cui passo come argomento ogni parola estratta.
Scusa, io stavo parlando della strtok(), ovvero di quella funzione che
estrae le parole.  Mi hai detto che hai avuto problemi con strtok(), o no ?


 estrae le parole.  Mi hai detto che hai avuto problemi con strtok(), o no
?
Si, ma in pratica il problema era quando poi passavo l'input alla mia
funzione. Cioe' le parole estratte dal mio ultimo algoritmo (senza strtok)
se passate alla funzione, essa da l'output giusto. Se invece le estraevo con
strtok, l'ultima estratta, passata alla mia funzione non dava l'output che
volevo. In breve, strtok estaeva le parole, ma l'ultma estratta se passata
alla funzione da me scritta (la f diciamo) non da l'output voluto.
Ora ho cancellato quel codice e ho tentato di riscriverlo ma non riesce
nemmeno a scandire le parole :/
Cmq eccolo, vediamo cosa sbaglio:
int main() {
 char cmd[1024];
 fgets(cmd, 1024, stdin);
 char* c = malloc (sizeof(char)*1024); // E' necessario allocarla?
 c = strtok(cmd, " ");
 printf ("%s ", c);
 while (c = strtok(NULL, " "));
  printf (" %s ", c);
 return 0;
}
Ciao

 Ciao
Dunque malloc non serve e se invece metto gli apici invece delle virgolette,
le parole le scandisce (e invece nel tuo esempio ci sono le virgolette,
boh!). Pero' se passo la parola alla funzione non da l'output giusto. Se
vuoi dare un'occhiata ti do il sorgente completo:
#include <stdio.h>
char* Completo (char* comando) {
 // Per aggiungere un comando basta inserirlo nell'array e modificare il
valore di size
 #define size 14
 char* comandi[size] = {"carica", "calcio", "pugno", "pugnala", "aggira",
          "penso", "soffio", "mangia", "bevi", "prendi",
                               "pratica", "apri", "indossa", "stat"};
 int i = 0, j = 0;
 while (1) {
  j = 0;
  while (comando[j] && comando[j] == comandi[i][j]) {
   j++;
  };
   if (!comando[j])
    return comandi[i];
   else
    i++;
  if (i == size)
   return "Nessun comando";
 };
};
int main() {
 char cmd[1024];
 fgets(cmd, 1024, stdin);
 char* c = strtok(cmd, ' ');
 printf ("%s ", Completo(c));
 while (c = strtok(NULL, ' '));
  printf (" %s ", Completo(c));
 return 0;
}
Dato un input come:
sof agg pra pen man ind st
deve dare in output
soffio aggira pratica penso mangia indossa stat
Grazie ciao :)

 }
Quanto al delimitatore e` una stringa, non un carattere:
SYNOPSIS
       #include <string.h>
       char *strtok(char *s, const char *delim);
 
Luca
-- 
Home: 
http://kronoz.cjb.net
"In linea di principio sarei indifferente al natale, se solo il natale
 ricambiasse la cortesia e mi lasciasse in pace." -- Marco d'Itri

 }
L'uso degli apici singoli in strtok() è sbagliato.  Le stringhe, in C, si
racchiudono sempre tra apici doppi (").  Togli il punto e virgola dopo il
while, altrimenti il ciclo gira a vuoto.  La funzione fgets(), inoltre,
memorizza anche la sequenza di escape '\n', quindi devi avere cura di
toglierla, se vuoi elaborare la parte finale della stringa.


 toglierla, se vuoi elaborare la parte finale della stringa.
Infatti immaginavo che fosse dovuto a qualcosa di simile il mio problema. Ma
non so come toglierlo. Forse assegnando '\0' al posto di '\n'?
Ciao

 Ah, ma il secondo paramentro di strtok non e' un char?
NAME
       strtok - extract token from string
SYNOPSIS
       #include <string.h>
       char *strtok(char *s, const char *delim);
DESCRIPTION
       A `token' is a nonempty string of characters not occurring
       in the string delim, followed by  \0  or  by  a  character
       occurring in delim.
       The  strtok()  function  can be used to parse the string s
       into tokens. The first call to strtok() should have  s  as
       its first argument. Subsequent calls should have the first
       argument set to NULL. Each call returns a pointer  to  the
       next token, or NULL when no more tokens are found.
       If  a token ends with a delimiter, this delimiting charac-
       ter is overwritten with a \0 and a  pointer  to  the  next
       character  is  saved  for  the  next  call to strtok.  The
       delimiter string delim may be different for each call.
Per quanto rigurda l'eliminazione di '\n' dalla stringa, ti basta
scorrerla e sostituire il '\n' a '\0':
for (i=0; s[i]; i++)
{
   if (s[i] == '\n')
     s[i] = '\0';


 toglierla, se vuoi elaborare la parte finale della stringa.
Cosi va bene (e dire che ci avevo gia prova ma misa che avevo sbagliato gli
indici... mamma mia che esperienza traumatica gli array di char dopo le
string)?
while (cmd[i++]);
 cmd[i - 2] = ' ';
Cioe' scorro tutta la stringa fino al carattere nullo.
Assegno lo spazio al carattere di newline.
A me funziona ma per caso rischio crash?
Ciao

 }
Volevi dire s[i] = ' '.
Se no mi salta l'ultima parola. In ogni caso con il while e modificando
l'i-2 carattere risparmio righe (vedi altro post)
Ciao

 Volevi dire s[i] = ' '.
No, volevo dire s[i] = '\0'.  In una stringa letta con fgets() potresti
avere '\n' solo come ultimo carattere, quindi lo puoi sostituire con '\0',
ovvero terminare la stringa.


 Cioe' scorro tutta la stringa fino al carattere nullo.
Ti complichi la vita inutilmente:
int len = strlen(cmd);
if (cmd[len-1] == '\n') cmd[len-1] = '\0';
oppure
for (i=0; cmd[i]; i++)  
   if (cmd[i] == '\n')  
     cmd[i] = '\0';


 ovvero terminare la stringa.
Si, ma strtok ha bisogno degli spazi per scandire la parola, quindi una
frase come:
Pippo ama la pesca[INVIO]
Quando arriva a "pesca" non trova piu' altri spazi. Oppure considera il
carattere nullo come ultimo elemento di separazione?
Ciao

 if (cmd[len-1] == '\n') cmd[len-1] = '\0';
Ah vero esiste strlen. Non mi ricordo una mazza di C... a dire il vero non
mi e' mai piaciuto data l'esistenza di C++ :D
Va be, uso la versione con strlen... pero' una domanda:
visto che inserisco solo una frase e alla fine premo invio la condizione if
non e' superflua?
Se si, potrei anche solo mettere:
cmd[strlen(cmd)-1] = '\0';
ma non funziona, come mai?
Grazie

 carattere nullo come ultimo elemento di separazione?
Ti quoto :P


 non e' superflua?
No, non è superflua perché non necessariamente l'ultimo carattere
contenuto nella stringa letta è '\n'.


 contenuto nella stringa letta è '\n'.
E quale potrebbe essere allora? Se scrivo una frase e premo invio, l'ultimo
carattere o e' '\n' oppure?
Ciao



oppure leggi come funziona fgets e lo scopri da solo. Facile eh ?

 carattere o e' 'n' oppure?
..oppure leggi un manuale.
Stammi bene.


 oppure leggi come funziona fgets e lo scopri da solo. Facile eh ?
Nel manuale leggo che ritorna NULL se si verifica un'errore o se raggiunge
la fine del file.
Leggendo da stdin non ci sono file in gioco e gli errori che si posso
verificare quali possono essere?
Inoltre ho visto anche che esiste gets che prende solo un puntatore a char
come argomento, lo restituisce e sostituisce automaticamente '\n' con '\0'.
Nel mio caso, molto meglio di fgets direi o no?
Grazie

 
No. 'gets' non va mai mai usata. Non spiego il perché. E' una FAQ.
 
 
http://www.eskimo.com/~scs/C-faq/q12.23.html
Leggiti pure questo, che mi pare ben fatto.
 
http://www.azillionmonkeys.com/qed/userInput.html
Max

 Grazie
char *c;
c = (char *) malloc (sizeof(char)*40);
scanf("%s",c);
;-)