Newsgroup: it.comp.lang.c
Titolo: Bisogno urgente di aiuto

Salve a tutti,
avrei un problema con il C: vorrei fare in modo che un utente inserisca
come input una stringa che non abbia alcuna dimensione prefissata
(paradossalmente potrebbe essere infinita). Ma come posso fare ad
allocare spazio prima se non so quanto lunga sarà la stringa?
Devo scrivere una riga di 5 elementi di cui 4 sono interi e solo
l'ultimo una stringa "infinita". Così:
12 300 -1 5 nome
Mi era così comodo usare la scanf ma così facendo non ho il controllo
sulla dimensione della stringa inserita. Ho pensato alla getchar ma
diventa tutto più scomodo da gestire (grazie alla scanf gli interi li
raccolgo all'interno di una variabile int senza prendere in
considerazione i singoli caratteri che compongono un numero formato ad
esempio dalle centinaia). 
Grazie

Luke ha scritto:

Dimenticati che i primi caratteri descrivono numeri interi, a estrarli 
ci penserai dopo. Per ora considera tutto come una sequenza di caratteri 
(anche perche' lo e').
Per leggere una stringa infinita mi e' venuta questa idea, non so se 
funziona.
---prova.c---
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BLOCK_SIZE 20
struct block{
	char  text[BLOCK_SIZE];
	int   chars;
	block *next_bl;
};
int leggi(FILE *istream, block **string)
{
	block *curr_block=*string;
	int   n_blocks=0;
	while((ch=getc(istream))!='\n'){
		/*se e' il primo blocco*/
		if(curr_block==NULL){
			curr_block=malloc(sizeof(struct block));
			/*controlla se malloc ha funzionato e poi*/
			/*azzera curr_block->text e curr_block->chars*/
		}
		/*controlla il blocco non e' pieno*/
		if(curr_block->chars<BLOCK_SIZE){
			curr_block->text[curr_block->chars-1]=ch;
			curr_block->chars++;
		}else{
		/*se e' pieno alloca un nuovo blocco e scrivi li*/
			curr_block->next_bl=malloc(sizeof(struct block))
			/*controlla malloc*/
			curr_block->next_bl->text[0]=ch;
			curr_block->next_bl->chars=1;
			curr_block=curr_block->next_bl;
			n_blocks++;
		}
	}
 
	/*aggiunge il terminatore alla fine
	dell'ultimo pezzo di stringa*/
	if(curr_block->chars<BLOCK_SIZE){
		curr_block->text[curr_block->chars-1]=0;
	}
	/*restituisce la lunghezza di tutta la stringa*/
	return ((n_blocks-1)*BLOCK_SIZE+curr_block->chars);
}
int main()
{
	struct block *stringa_lunga;
	int len;
	stringa_lunga=malloc(sizeof(struct block));
	if(stringa_lunga==NULL){
		/*...*/
	}
	len=leggi(stdin,&stringa_lunga);
	printf("Inseriti %d caratteri.\n",len);
	return 0;
}
-----------
L'ho buttata giu' di corsa, non so se funziona. Prova a controllare, 
soprattutto quel puntatore a puntatore e il pezzo che dovrebbe allocare 
il primo blocco. Il concetto comunque e' quello.
-- 
Devaraja (Xdevaraja87^gmail^c0mX)
Linux Registerd User #338167
http://counter.li.org

 
Infatti e' piena di errori. Se hai un attimo la correggo e la posto.




Il concetto di infinito mal si adatta ai computer ed al loro lavorare sul
discreto; quindi pensa a quanto potrebbe essere lunga al massimo (se parli
di "nomi" di persona non credo che qualcuno ne possa avere uno lungo piu
di 100 caratteri.. e se sei in dubbio metti 1000 che so..) e richiedi una
stringa non piu lunga di quello che hai pensato con fgets. Ad esempio..
char nome[1000];
fgets( stdin, sizeof(nome), nome );
Se proprio vuoi ottimizzare l'uso di memoria a discapito del tempo di
esecuzione o se l'entropia dell'universo fa sì che puoi avere nomi che
vanno da 5 caratteri a 3915729538 caratteri, allora puoi divertirti con i
cicli, getchar e realloc.
k=0
while( (c = getchar()) != EOF && c != '\n' )
{
	nome = realloc(nome, k+2 );
	nome[k++] = c;
}
nome[k] = '\0';
Byez
Mmpunfh

DevarajA ha scritto:

Qui c'e' la versione abbastanza funzionante: 
http://phpfi.com/80583
Solo ora mi rendo conto che tu hai scritto "nome", quindi e' probabile 
che la "stringa infinita" non sia piu' lunga di 15-20 caratteri. Non 
voglio pensare a cosa leggero' della mia soluzione quando avro' 
scaricato gli altri post :-)
Se la "stringa infinita" e' corta puoi usare un semplice buffer e poi 
copi quello che hai letto in un'area allocata dinamicamente.


Matic ha scritto:

AFAIK realloc() alloca un nuovo spazio della dimensione specificata e 
dopo, solo se la cosa e' riuscita, copia il contenuto del vecchio buffer 
e lo dealloca. Se il buffer di partenza e' seguito da abbastanza spazio 
libero si limita a estenderlo, ma se cosi' non fosse dovrebbe cercare un 
nuovo spazio, e maggiore e' la dimensione richiesta, minore e' la 
possibilita' di trovarlo. Non so se questo sia vero, ma se lo e' la tua 
soluzione (che per piccole stringhe e' decisamente da preferire alla 
mia) ha una probabilita' di fallimento che aumenta a ogni chiamata. Se 
mi sbaglio fammelo sapere.
Comunque, visto che si parla di nomi, credo che un array automatico di 
40-50 char sia sufficiente.


 mi sbaglio fammelo sapere.
Non ho ben capito che intendi dire.. le *alloc falliscono in caso la
memoria sia esaurita e quindi che le possibilità di fallimento aumentano
ad ogni chiamata è una cosa ovvia e applicabile a tutte le soluzioni
proposte, no ? ;-)
Forse intendi dire che siccome con realloc c'è la possibilità che in
memoria ci siano contemporaneamente due copie del buffer aumenta il
rischio.. vero, tuttavia stiamo parlando di consumi di mega e mega non
tanto applicabili a stringhe e soprattutto non applicabili a questo
problema che è ovvio vada risolto con buffer statici
Bye
M.

 proposte, no ? ;-)
Si, infatti il mio discorso si applicava a stringhe molto lunghe, anche 
se poi con la memoria virtuale devono essere davvero *molto* lunghe.
L'unico vantaggio della mia soluzione e' che lo spazio puo' non essere 
contiguo e quindi si puo' utilizzare anche un heap frammentato, sempre 
che l'implementazione non preveda meccanismi per ricompattare il tutto.


 che l'implementazione non preveda meccanismi per ricompattare il tutto.
Ma si può fare ? E i puntatori che hai nel tuo programma chi li
ridefinisce ?

Matic ha scritto:

Beh.. non ci avevo pensato. Ma tu devi proprio essere cosi' pignolo? ;-)


 sulla dimensione della stringa inserita.
Ciao,
 se utilizzi un sistema GNU la scanf va benissimo, basta usare %as.
Se non usi un sistema GNU ... e' colpa tua :-))
A parte gli scherzi, quello che puoi fare e' allocare una quantita' 
ragionevole di memoria e usare la scanf specificando la quantita' massima 
di caratteri da leggere.
Esempio:
char buf[1024];
scanf ("...%1023s...", ..., buf, ...);
Poi controlli la lunghezza della stringa, e se e' 1023 vuol dire che c'e' 
altro da leggere!
 Ciao
 Federico

Ragazzi ho risolto i problema (grazie ad un suggerimento ricevuto):
uso una getchar()  memorizzando ogni singolo carattere in un array di
tipo puntatore a carattere. Uso la realloc per riallocare ogni volta
l'array. Infine uso la sscanf per formattare tutto il testo inserito
dall'utente e che si trova nell'array.
Comunque grazie a tutti per i consigli