PDA

Visualizza la versione completa : [C]Lettura stringhe


dennis87
22-05-2014, 17:54
Ciao a tutti, in un esercizio mi si chiede di scrivere un programma che legge continuamente dallo standard input righe di testo terminate da \n, e far terminare il programma quando inserisco una riga vuota.

Cercando informazioni ho trovato che per leggere righe di testo posso usare la funzione
fgets(destinazione, dimensione, stdin) che mi sembra di aver capito legge stringhe digitate da tastiera della dimensione massima di "dimensione" e la salva in "destinazione" terminando ogni stringa con \n.

Quello che non mi è chiaro è come far riconoscere la stringa vuota e quindi far terminare il programma.
Quello che ho provato a scrivere è questo:


#include <stdio.h>

#define BUFFER_LENGTH 255

int main(int argc, char *argv[]) {
char buffer[BUFFER_LENGTH];
while(buffer!=NULL){
fgets(buffer, BUFFER_LENGTH, stdin);
}
return 0;
}


ma sia con NULL che con "" non esce dal while.
Mi potreste dare una mano?
Grazie mille

vbextreme
22-05-2014, 19:39
while ( *(fgets(buffer,BUFFER_LENGTH, stdin)) != '\n')
{
...
}


o in alternativa:



do
{
fgets(buffer, BUFFER_LENGTH, stdin);
}while ( buffer[0] != '\n');

dennis87
22-05-2014, 19:52
soluzione abbastanza semplice alla fine.
Ma se io non sapessi a priori la lunghezza della stringa che vado ad inserire? Esiste un modo di fare la fgets senza dire a priori la lunghezza della stringa?

SEGFAULT
22-05-2014, 23:40
Il problema è che la fgets() scrive la stringa che viene presa in input nel buffer. Per forza di cose devi "dirgli" una dimensione massima entro la quale non si sfora la lunghezza del buffer.
L' alternativa sarebbe quella di leggere la stringa carattere per carattere con la getchar(), allocando un carattere alla volta (oppure raddoppiando la dimensione del buffer ogni volta che serve più spazio), però se chiami troppe volte la realloc() può essere inefficiente.

Scara95
23-05-2014, 15:16
Il problema è che la fgets() scrive la stringa che viene presa in input nel buffer. Per forza di cose devi "dirgli" una dimensione massima entro la quale non si sfora la lunghezza del buffer.
L' alternativa sarebbe quella di leggere la stringa carattere per carattere con la getchar(), allocando un carattere alla volta (oppure raddoppiando la dimensione del buffer ogni volta che serve più spazio), però se chiami troppe volte la realloc() può essere inefficiente.
In realtà puoi benissimo usare fgets alla base:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define N 128

static int cpy(char **line, size_t *n, int l, char *BUFFER, int tmp_l) {
if(((*n)-l) < (tmp_l+1)) {
*n = l+tmp_l+1;
char *tmp = (char *)realloc(*line, sizeof(char)*(*n));
if(tmp == NULL) {
return -2;
}
*line = tmp;
}
strcpy((*line)+l, BUFFER);
return 0;
}

int get_line(char **line, size_t *n, FILE *f) {
char BUFFER[N];
int l = 0, tmp_l;
while(1) {
if(fgets(BUFFER, N, f) == NULL) {
return -1;
}
tmp_l = strlen(BUFFER);
if(BUFFER[tmp_l-1] == '\n') {
BUFFER[--tmp_l] = '\0';
break;
}
if(feof(f)) {
break;
}
if(cpy(line, n, l, BUFFER, tmp_l)) {
return -2;
}
l += tmp_l;
}
if(cpy(line, n, l, BUFFER, tmp_l)) {
return -2;
}
return l+tmp_l;
}

int main(void) {
char *l = NULL;
size_t n = 0;
while(get_line(&l, &n, stdin) >= 0) {
printf("%s", l);
}
return 0;
}

A parte che se stai usando un compilatore tipo gcc puoi usare getline (funzione di libreria, che non è pienamente compatibile con la funzione presentata sopra).

Edit 15:47 corretto errore di svista: BUFFER[--tmp_l] == '\0'; -> BUFFER[--tmp_l] = '\0';

Scara95
23-05-2014, 15:22
while ( *(fgets(buffer,BUFFER_LENGTH, stdin)) != '\n')
{
...
}


o in alternativa:



do
{
fgets(buffer, BUFFER_LENGTH, stdin);
}while ( buffer[0] != '\n');


Questo codice è totalmente insicuro in ogni caso...

SEGFAULT
23-05-2014, 15:31
@Scara95: Così deve comunque specificare la lunghezza massima della stringa, che è N nel tuo caso.

Scara95
23-05-2014, 15:40
@Scara95: Così deve comunque specificare la lunghezza massima della stringa, che è N nel tuo caso.
Guarda bene -.-"'
Cambia il 128 in un numero basso, ad esempio 5, compila e scrivi una frase di 20 caratteri.

vbextreme
26-05-2014, 08:45
Quando si legge una stringa è sempre poco efficente una lettura "dimanica" della stessa.
Sempre meglio quindi allocare un buffer di dimensioni "esagerate" in modo da non doverlo poi inutilmente ritoccare sucessivamente.


Questo codice è totalmente insicuro in ogni caso...
???

Scara95
26-05-2014, 14:07
Quando si legge una stringa è sempre poco efficente una lettura "dimanica" della stessa.
Sempre meglio quindi allocare un buffer di dimensioni "esagerate" in modo da non doverlo poi inutilmente ritoccare sucessivamente.

Puoi benissimo sovra-allocare anche con getline o con la funzione che ti ho presentato io: basta che allochi precedentemente la memoria e passi come parametri il puntatore al blocco di memoria e il numero di bytes allocati.


???
Puoi rischiare di perdere dati, nel caso di EOF non preceduto da ritorno a capo non da una soluzione coerente.
Insomma, puoi benissimo usarlo, ma a condizione che alcuni casi non si presentono CERTAMENTE mai in input.

Loading