PDA

View Full Version : [C] stringa da stdin


redcloud
09-12-2005, 15:27
Salve, ho bisogno di una funzione che prenda in input (dallo stdin) una stringa senza spazi di lunghezza arbitraria e che la memorizzi automaticamente in un array senza quindi usare malloc apriori (proprio perchè la lunghezza della stringa data in input è variabile). Mi servirebbe standard. Come fareste?

Ziosilvio
09-12-2005, 15:35
Non e' un problema banale.

Io userei una lista anziche' un array.
I campi degli elementi della lista sarebbero un buffer di un numero fisso di char, e un puntatore all'elemento successivo.
Man mano che leggi un carattere alla volta con getchar, lo inserisci nel buffer dell'elemento corrente: se non bastano, allochi un nuovo elemento della lista e cominci da li'.
Quando hai finito di leggere l'input, calcoli quanto e' lunga la stringa, allochi un array e ci "svuoti" i buffer uno dopo l'altro.

redcloud
09-12-2005, 15:55
Grazie, l'idea è quella ma vorrei sapere se c'è una funzione standard preposta. A quanto pare no.

Ziosilvio
09-12-2005, 16:27
Grazie, l'idea è quella ma vorrei sapere se c'è una funzione standard preposta. A quanto pare no.
Anche per quello che ricordo io, nella libreria standard una funzione simile non c'e'.

sirus
09-12-2005, 16:29
Grazie, l'idea è quella ma vorrei sapere se c'è una funzione standard preposta. A quanto pare no.
no...non esistono funzioni standard per la gestione di liste/pile/code...il campo di utilizzo è troppo eterogeneo :)
comunque mi sembra il metodo migliore...e comunque con una lista è necessario usare l'allocazione dinamica :mc:

NA01
09-12-2005, 17:02
la getline se trova un puntatore nullo alloca la memoria che serve. ciao!

sirus
09-12-2005, 17:03
la getline se trova un puntatore nullo alloca la memoria che serve. ciao!
questa mi mancava :D grazie...

NA01
09-12-2005, 17:04
ah, forse però non è portabile su win....
chi ha windows controlli :)

ciao

NA01
09-12-2005, 17:07
confermo. è un'estensione gnu_sources e su dev c++ non compila...

scusate per il falso allarme

ciao!

redcloud
09-12-2005, 17:32
Provate ad eseguire questo codice:

char *a = (char *) malloc(sizeof(char));

scanf("%s", a);

Alloco un puntatore di 1 char e in stdin scrivo una qualsiasi stringa superiore a 1 char.

Nessun errore. E' normale?

EDIT: sto lavorando sotto Debian, probabilmente sotto Windows darebbe errore.

sirus
09-12-2005, 17:47
Provate ad eseguire questo codice:

char *a = (char *) malloc(sizeof(char));

scanf("%s", a);

Alloco un puntatore di 1 char e in stdin scrivo una qualsiasi stringa superiore a 1 char.

Nessun errore. E' normale?
si...però sfondi l'array che hai allocato il che comporta problemi durante l'esecuzione se fai uso di altre variabili dinamiche...

redcloud
09-12-2005, 17:52
Quindi nella tavola delle variabili risulterebbe l'array di 1 char. Una successiva malloc potrebbe tranquillamente sovrascrivere "lo straripamento". Capito. Però un minimo di errore lo potrebbe dare, che palle! Si deve andare a intuizione!

sirus
09-12-2005, 17:55
Quindi nella tavola delle variabili risulterebbe l'array di 1 char. Una successiva malloc potrebbe tranquillamente sovrascrivere "lo straripamento". Capito. Però un minimo di errore lo potrebbe dare, che palle! Si deve andare a intuizione!
e perché dovrebbe?
il C non è così fiscale in questi casi :(

redcloud
09-12-2005, 17:59
E' parecchio che non usavo il C ma mi ricordo che quando lavoravo con i for e gli indici degli array, se sforavo, mi andava tranquillamente in Segmentation fault. Cosa che così non avviene più. Mah.

sirus
09-12-2005, 18:02
E' parecchio che non usavo il C ma mi ricordo che quando lavoravo con i for e gli indici degli array, se sforavo, mi andava tranquillamente in Segmentation fault. Cosa che così non avviene più. Mah.
ovvio...quella memoria è gestita staticamente nel DataSegment oppure nell'apposita sezione RDA di ogni sottoprogramma. Quella che tu allochia con malloc risiede nella HEAP e li il controllo non è lo stesso... :) e poi quando si lavora con i puntatori (seriamente) bisogna sapere dove si va a parare con il prorpio codice :p

VegetaSSJ5
09-12-2005, 19:55
Non e' un problema banale.

Io userei una lista anziche' un array.
I campi degli elementi della lista sarebbero un buffer di un numero fisso di char, e un puntatore all'elemento successivo.
Man mano che leggi un carattere alla volta con getchar, lo inserisci nel buffer dell'elemento corrente: se non bastano, allochi un nuovo elemento della lista e cominci da li'.
Quando hai finito di leggere l'input, calcoli quanto e' lunga la stringa, allochi un array e ci "svuoti" i buffer uno dopo l'altro.
Ziosilvio scusa se dal basso della mia inesperienza mi permetto di chiederti (mi permetto di darti del tu, se non gradisci dimmelo pure che edito... :sofico: ) chiarimenti, ma il mio dubbio è questo. Secondo la tua idea un nodo della lista conterrebbe un array dichar di lunghezza predefinita, se poi non basta quell'array viene allocato un altro nodo e si continua a memorizzare in quello nuovo. Ma a questo punto visto che comunque le operazioni di allocazione in memoria deve farle lo stesso non conviene direttamente dichiararsi un puntatore ad un vettore di char di lunghezza predefinita e poi eventualmente riallocarlo con dimensione 2*LENGTH? :)

sirus
10-12-2005, 10:22
Ziosilvio scusa se dal basso della mia inesperienza mi permetto di chiederti (mi permetto di darti del tu, se non gradisci dimmelo pure che edito... :sofico: ) chiarimenti, ma il mio dubbio è questo. Secondo la tua idea un nodo della lista conterrebbe un array dichar di lunghezza predefinita, se poi non basta quell'array viene allocato un altro nodo e si continua a memorizzare in quello nuovo. Ma a questo punto visto che comunque le operazioni di allocazione in memoria deve farle lo stesso non conviene direttamente dichiararsi un puntatore ad un vettore di char di lunghezza predefinita e poi eventualmente riallocarlo con dimensione 2*LENGTH? :)
secondo me Ziosilvio intendeva qualche cosa tipo questa

typedef struct {
char c;
nodo* next;
} nodo;

come definizione di dato :p quando ti serve un nuovo carattere lo allochi senza problemi :)

redcloud
10-12-2005, 10:37
Magari inserendo un array anzichè un carattere. Allocare una struct per un solo carattere non mi sembra così efficiente. Creare manualmente quindi una sorta di bufferizzazione.

sirus
10-12-2005, 10:42
Magari inserendo un array anzichè un carattere. Allocare una struct per un solo carattere non mi sembra così efficiente. Creare manualmente quindi una sorta di bufferizzazione.
si ma vuoi mettere la semplicità della funzione/codice che acquisisce in strutture che contengono caratteri invece che contengono array :p ?
bisogna sempre guardare rapporto efficienza/semplicità :D

VegetaSSJ5
10-12-2005, 10:46
vi ripeto che non continuo a vedere la convenienza della soluzione con la lista rispetto all'usare direttamente un vettore riallocabile di char. per farvi un esempio è come se doveste entrare in macchina e lo fate entrando dal bagagliaio per poi andarvi a sedere al vostro posto... :D :mc:

cionci
10-12-2005, 17:43
Io leggerei carattere per carattere e se la stringa dovesse essere più lunga del buffer allocato farei una realloc... Non è detto che si debba fare una realloc per ogni char letto, ma basta anche reallocare ogni tot char...

Ziosilvio
10-12-2005, 20:06
vi ripeto che non continuo a vedere la convenienza della soluzione con la lista rispetto all'usare direttamente un vettore riallocabile di char
Cioè: tu dici di allocare, cominciare a leggere l'input, e riallocare man mano che si sfora.
Convengo con te (e con cionci) che, in realtà, è la soluzione più veloce.
Il problema è che, quando si chiama la realloc, ci deve essere già uno spazio di memoria contigua della grandezza richiesta.
Ora: su un PC degli ultimi tre anni su cui gira un programma svolto come esercizio per un corso di programmazione, il problema effettivamente non si dovrebbe presentare.
Ma se si ha una macchina con capacità più limitate, e una memoria molto frammentata per un motivo o per l'altro, allora potrebbe essere possibile allocare tante regioni di memoria piccole, ma non una grande.

In finale: a conti fatti, e alla luce degli interventi tuo e di cionci, direi che in realtà la risposta alla domanda originaria --- se ci fosse un metodo standard per ricevere in input una stringa di lunghezza arbitraria --- in realtà è positiva, e si basa sull'uso di getchar e realloc.
Che poi il metodo sia perfettibile... beh, direi che questo dipende fondamentalmente dalla mia voglia di spaccare il capello in quattro.

VegetaSSJ5
10-12-2005, 20:22
Cioè: tu dici di allocare, cominciare a leggere l'input, e riallocare man mano che si sfora.
Convengo con te (e con cionci) che, in realtà, è la soluzione più veloce.
Il problema è che, quando si chiama la realloc, ci deve essere già uno spazio di memoria contigua della grandezza richiesta.
Ora: su un PC degli ultimi tre anni su cui gira un programma svolto come esercizio per un corso di programmazione, il problema effettivamente non si dovrebbe presentare.
Ma se si ha una macchina con capacità più limitate, e una memoria molto frammentata per un motivo o per l'altro, allora potrebbe essere possibile allocare tante regioni di memoria piccole, ma non una grande.

In finale: a conti fatti, e alla luce degli interventi tuo e di cionci, direi che in realtà la risposta alla domanda originaria --- se ci fosse un metodo standard per ricevere in input una stringa di lunghezza arbitraria --- in realtà è positiva, e si basa sull'uso di getchar e realloc.
Che poi il metodo sia perfettibile... beh, direi che questo dipende fondamentalmente dalla mia voglia di spaccare il capello in quattro.
non avevo pensato a questa cosa. sono abituato a guardare le cose in piccolo e non in grande. in effetti la soluzione del vettore riallocabile non è sempre fattibile ed è dipendente dall'hardware specifico della macchina, per cui a questo punto sarei più propenso per la soluzione che hai dato originariamente e cioè di vari nodi di una lista ognuno contenente un buffer. :ave:

cionci
10-12-2005, 20:48
non avevo pensato a questa cosa. sono abituato a guardare le cose in piccolo e non in grande. in effetti la soluzione del vettore riallocabile non è sempre fattibile ed è dipendente dall'hardware specifico della macchina, per cui a questo punto sarei più propenso per la soluzione che hai dato originariamente e cioè di vari nodi di una lista ognuno contenente un buffer. :ave:
I problemi di frammentazione di memoria sono abbastanza poco frequenti... Soprattutto con un sistema a memoria virtuale...e possono apparire soltanto con sistemi con poca memoria...e con quantità da riallocare moooolto grandi (di ordine paragonabile alla emmoria di sistema)...

La lista è un ottimo metodo, ma se si considera che per ogni char ci vogliono 8 byte (con allineamento ai 4 byte)...bhe, alla fine non so quanto convenga in un sistema con poca memoria... Certo, con la realloc c'è il problema della frammentazione, ma con la lista la memoria si esaurisce ;)

A questo punto credo che la soluzione migliore sia una lista di stringhe linkate...
Il problema è che sia in questo modo che nella lista bisogna comuqnue lavorare con funzioni apposite sule stringhe così costruite...mentre con la realloc ci si può lavorare con la libreria standard...

redcloud
11-12-2005, 00:19
Altra cosa strana:
#include <stdio.h>

int main() {
int i=0;
char a[5000];

scanf("%s", a);

for(i=0; i<5000; i++) {
printf("%s\n", a);
}
}
Se da stdin inserisco più di 4096 caratteri, la scanf va nel pallone (nel senso che i caratteri da 4097 in su non vengono inseriti nell'array). Colpa della bufferizzazione?

cionci
11-12-2005, 08:52
Sicuramente è un problema di dimensione massima del buffer per lo stdin...

VegetaSSJ5
11-12-2005, 10:48
Sicuramente è un problema di dimensione massima del buffer per lo stdin...
e non ti pare un problema questo? perchè coloro che hanno implementato le funzioni di libreria hanno permesso che si verificassero queste cose?

per risolvere questo problema bisognerebbe prendere da stdin 4096 caratteri alla volta vero?

cionci
11-12-2005, 11:00
Sì...

redcloud
11-12-2005, 12:13
Ho provato a leggere da file è la limitazione a 4096 non c'è, ma come faccio a sapere se c'è una limitazione per la bufferizzazione da file? Sono arrivato a 20k caratteri ma ancora niente. Chi mi assicura che posso usare file di 20MB? Non riesco neanche a trovare uno straccio di documentazione su google, neanche man mi aiuta, e soprattutto neanche i libri di C che ho.

cionci
11-12-2005, 12:19
Ho provato a leggere da file è la limitazione a 4096 non c'è, ma come faccio a sapere se c'è una limitazione per la bufferizzazione da file? Sono arrivato a 20k caratteri ma ancora niente. Chi mi assicura che posso usare file di 20MB? Non riesco neanche a trovare uno straccio di documentazione su google, neanche man mi aiuta, e soprattutto neanche i libri di C che ho.
Hai letto 20 mbyte con una sola lettura con fscanf ?

redcloud
11-12-2005, 13:23
No, con fgetc e non 20MB ma soltanto 4.

cionci
11-12-2005, 13:25
No, con fgetc e non 20MB ma soltanto 4.
Vedrai che se leggi con fgetc anche lo stdin ti funziona oltre 4096...

redcloud
11-12-2005, 13:45
E invece no, la bufferizzazione è legata agli stream e non alla funzione. Strano però!