View Full Version : [c] stringhe di lunghezza indefinita
Innominato
11-12-2003, 13:26
C'è un modo per allocare una stringa senza sapere a priori la sua lunghezza e senza spreco di spazio?
cioè se io devo memorizzare una stringa immessa da un utente, devo inizializzarla o con dimensione n che stabilisco io o con n caratteri(+1 per lo /0) che mi passa l'utente.
Ma senza che l'utente mi dica la dimensione, senza usare un'altra stringa aux e senza dare io un n grande, c'è un altro modo?
realloc, ma e' lenta.
In alternativa una struttura del tipo
typedef struct {
char stringa[100];
struct nodo* next;
} nodo;
se sfori allochi e aggiungi un altro nodo
Forse c'e' un modo migliore, ma non mi viene in mente :p
michele.t
11-12-2003, 17:25
se il poroblema è occupare meno memoria possibile penso che la lista linkata sia l'ideale (per quello che ne so). io ne ho scritta una per dei numeri, ma modificarla per i caratteri non costa nulla. fatemi sapere se è una soluzione valida, sto studiando c++ in uni e la cosa mi interessa. altrimenti puoi allocare dinamicamente un array in memoria (ma devi passargli la grandezza). il prob della lista linkata è che la svuoti dall'alto (l'ultima cosa entrata è quella che esce per prima). per risolvere potresti usare la lista linkata circolare, dove immetti in coda e peschi in testa (per stampare stringhe va benissimo)
array.....
int n;
char *v;
v = new char[n]
lista linkata...
#include <iostream.h>
#ifndef STACK_H
#define STACK_H
#include "ListNode.h"
class Stack {
public:
Stack() {
top = NULL;
}
bool isEmpty() const {
return top == NULL;
}
void set(int x) {
ListNode *temp;
temp = new ListNode(x);
temp->ptrNext = top;
top = temp;
}
int get() {
int v = top->value;
ListNode *temp = top;
top = top->ptrNext;
delete temp;
return v;
}
private:
ListNode *top;
};
#endif
#include <iostream.h>
#ifndef LISTNODE_H
#define LISTNODE_H
class ListNode {
friend class Stack;
public:
ListNode(int n)
{
ptrNext = NULL;
value = n;
}
private:
int value;
ListNode *ptrNext;
};
#endif
#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
using namespace std;
template<class T>
class Stack
{
public:
// Constructor with dimension
Stack(int dim) { vet = new T[checkDim(dim)]; };
// Constructor without dimension
Stack() { vet = new T[5]; };
// Check and verify the chosed dimensione
int checkDim(int dim) { return(dim <= 0)?5:dim; };
// The destructor
~Stack(){};
// add a element
void push(T * value) { vet[sp++] = value; };
// return a element (and no delete)
T pop() { return vet[sp--]; };
private:
T * vet;
int sp;
};
Raga...qui si parla di C...non di C++ ;)
Concordo con lista linkata di lovaz, ma l'input a questo punto andrà gestito carattere per carattere (oppure sfruttando la fgets sullo stdin)...
Innominato
12-12-2003, 12:35
Si a me serve in c liscio :D
Non abbiamo ancora fatto tutto all'unive, infatti non sapevo si potesse dare il comando struct nodo* next; dentro la definizione di nodo... devo studiarci sopra... grazie intanto.
downloader
12-12-2003, 13:07
Utilizza una malloc
char * pippo = (char*)malloc(numero_caratteri + 1);
memset(pippo, 0, numero_caratteri + 1);
alla fine quando non ti serve + usa free(pippo);
se in corso d'opera devi modificarne la lunghezza usa la realloc.
Il + 1 è per il tappo finale (0 binario).
;)
michele.t
12-12-2003, 18:48
era c! oooppsss :D
Originariamente inviato da downloader
Utilizza una malloc
char * pippo = (char*)malloc(numero_caratteri + 1);
memset(pippo, 0, numero_caratteri + 1);
alla fine quando non ti serve + usa free(pippo);
se in corso d'opera devi modificarne la lunghezza usa la realloc.
Il + 1 è per il tappo finale (0 binario).
;)
E se numero_caratteri non lo conosci prima di prendere la stringa in ingresso ?!?!? ;)
downloader
13-12-2003, 11:54
Originariamente inviato da cionci
E se numero_caratteri non lo conosci prima di prendere la stringa in ingresso ?!?!? ;)
Infatti io lo intendo come variabile.
Chiaramente dall'input può arrivare una qualsiasi lunghezza e se non puoi limitarla devi dettare delle regole di lunghezza massima. Ma questo è l'unico modo di gestione dinamico che c'è in C. In C++ in particolare in MFC puoi trovarelasse CString (se nn ricordo male anche in Borland C++ Builder) che gestisce lunghezze dinamiche.
Originariamente inviato da downloader
Infatti io lo intendo come variabile.
Sì, ma nel tuo codice lo devi conoscere a priori per allocare la stringa ;)
Provate questo (non l'ho debuggato, l'ho scritto così al volo):
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define VARSTR_LEN 10
struct svarstr {
char str[VARSTR_LEN];
struct svarstr *next;
};
typedef struct svarstr* varstr;
void free_varstr(varstr str)
{
varstr tmp;
do {
tmp = str->next;
free(str);
str = tmp;
} while(str);
}
void write(varstr str, FILE *f = stdout)
{
do {
fprintf(f, "%s", str->str);
str = str->next;
} while(str);
}
varstr read(FILE *f = stdin)
{
varstr str, tmp;
int i;
str = (varstr)malloc(sizeof(svarstr));
str->next = NULL;
tmp = str;
while(1)
{
if(fgets(tmp->str, VARSTR_LEN, f) == NULL)
{
fprintf(stderr, "fgets error\n");
free_varstr(str);
return NULL;
}
if(strlen(tmp->str) < (VARSTR_LEN-1) || tmp->str[VARSTR_LEN-2] == '\n')
{
tmp->str[strlen(tmp->str)-1] = '\0';
return str;
}
else
{
tmp->next = (varstr)malloc(sizeof(svarstr));
tmp = tmp->next;
tmp->next = NULL;
}
}
}
int main()
{
varstr str;
str = read();
write(str);
free_varstr(str);
getchar();
}
downloader
15-12-2003, 10:07
Originariamente inviato da cionci
if(fgets(tmp->str, VARSTR_LEN, f) == NULL)
{
fprintf(stderr, "fgets error\n");
free_varstr(str);
return NULL;
}
Domanda: se la fgets riceve una stringa superiore ai 10 caratteri?
:O
In realta' ripensandoci la cosa migliore in questo caso e' usare realloc, avendo l'accortezza di non chiamarla a ogni carattere, ma di far crescere la stringa di un tot di byte alla volta.
Per es.:
stringa iniziale di 100 byte
quando sto per esaurire lo spazio realloc con dimensione 200 byte, poi 300 byte, e cosi' via.
downloader
16-12-2003, 09:57
Secondo me c'è poco da fare... se non si conosce la lunghezza iniziale di una stringa occorre controllare l'input dell'utente.
Se l'input è dos, si utilizzano gli interrupt (l'interrupt) relativi allinput da tastiera. Altrimenti se è windows si gestiscono i messaggi relativi alla casella di testo (che poi non ce ne sarebbe bisogno lestringhe sono davero davvero dinamiche)
Ogni malloc o realloc vuole la size come parametro.
Ma se quella fgets legge dallo stdin come gli si può passare una stringa di lunghezza completamente dinamica?
downloader
16-12-2003, 10:38
Originariamente inviato da lovaz
fgetc?
Io ho scritto fgets. Se intendi poi l'uso di fgetc... che fai inseire all'utente una stringa lettera per lettera???:confused:
Si', per sapere la lunghezza della stringa devi "contare" i caratteri.
Oppure usare qualche libreria, tipo la readline, ma non l'ho mai usata.
downloader
16-12-2003, 10:56
Ah le librerie le risolvono queste cose. Su quello alzo le mani. Ma se nn si vuol utilizzare nessuna libreria occorre solo gestire fisicamente e a basso livello l'input dell'utente. Cosa che probabilmente fanno gia le librerie.:rolleyes:
Ora io sono un po' arrugginito sul c, ma usando la fgetc o analoga non puoi leggere l'input un char alla volta e agire di conseguenza? Magari riallocando?
downloader
16-12-2003, 11:03
Originariamente inviato da lovaz
Ora io sono un po' arrugginito sul c, ma usando la fgetc o analoga non puoi leggere l'input un char alla volta e agire di conseguenza? Magari riallocando?
Ovvio... ma non ti sembra un po' brutto come metodo di input per un utente?
Tu la faresti un interfaccia nella quale devi inserire il nome di un libro, ad esempio, e lo devi inserire lettera per lettera (lettera+invio, lettera+invio)?
Non lettera+invio, semplicemente lettera :rolleyes:
Sono sicuro che esiste una funzione per prendere un carattere
downloader
16-12-2003, 11:35
Originariamente inviato da lovaz
Non lettera+invio, semplicemente lettera :rolleyes:
Sono sicuro che esiste una funzione per prendere un carattere
con invio non intendevo '\n' ma la pressione del tasto invio...
Originariamente inviato da lovaz
Non lettera+invio, semplicemente lettera :rolleyes:
Sono sicuro che esiste una funzione per prendere un carattere
Mi quoto da solo...
downloader
16-12-2003, 11:54
Originariamente inviato da lovaz
Mi quoto da solo...
Ok... non posso dire che ci stiamo capendo alla grande... :D
Una funzione c'è... ma nn è na vera e propria funzione, fa parte dell'uso di un po' di codice assembly che permette la lettura delle lettere appena digitate...
Spero di aver capito bene...
Intendevi questo?
In effetti ho controllato che la fgetc non fa quello che dicevo, ma mi sembra di ricordare che esistesse una funzione che lo fa...
Bisognerebbe guardare nelle ncurses :confused:
downloader
16-12-2003, 12:38
Originariamente inviato da lovaz
In effetti ho controllato che la fgetc non fa quello che dicevo, ma mi sembra di ricordare che esistesse una funzione che lo fa...
Bisognerebbe guardare nelle ncurses :confused:
curses.h intendi?
Quelle gestiscono il video, niente di +. Almeno per quello che ricordo ora...
Gestisce anche l'input, fai un man getch
downloader
16-12-2003, 12:47
Originariamente inviato da lovaz
Gestisce anche l'input, fai un man getch
Sì giusto... adesso non sto su unix e sto andando a memoria... ma la getch se non ricordo male è la stessa cosa della corrispondente in stdio.h
Non fa quella cosa che dicevi tu...:rolleyes:
Originariamente inviato da downloader
Domanda: se la fgets riceve una stringa superiore ai 10 caratteri?
:O
Prova il mio programma e guarda cosa fa...
Se sono più di 10 non ritorna NULL !!! Almeno a me... proprio su questo si basa il mio programma...
downloader
18-12-2003, 14:09
Originariamente inviato da cionci
Prova il mio programma e guarda cosa fa...
Se sono più di 10 non ritorna NULL !!! Almeno a me... proprio su questo si basa il mio programma...
E' vero, non avevo considerato che rimaneva nello stream anche gli altri caratteri...
downloader
18-12-2003, 14:10
chiedo pardon...
:ave:
downloader
19-12-2003, 14:03
Originariamente inviato da cionci
Allora che vi pare ?
Buono. Non ricordavo assolutamente che la fgets lavorava così. Ma non ti conviene utilizzare malloc e realloc? Anche per chiarezza...:)
L'unico modo sarebbe abbinare l'uso di realloc alla fgets, ma la realloc è molto scomoda...visto che praticamente mai può lavorare nelle condizioni ottimali, ma deve spesso cercare uno spazio contiguo adatto a contenere la nuova stringa ridimensionata per poi copiarvi il contenuto dalla locazione originale... Magari usando realloc si ha il vantaggio di continuare ad usare un puntatore a char per la lettura del contenuto della stringa... Comunque l'idea della lista di stringhe mi piaceva...
downloader
19-12-2003, 14:18
E' vero (anche se relativamente, l'heap è comunque piazzata nei gap della memoria non utilizzati), ma poi finisce che ti devi scrivere una libreria solo per gestire la lista...
Comunque è ok;)
Chiaro che dipende da ciò per cui ti serve... Se si tratta di fare semplice I/O allora va bene così... Se si tratta di manipolare le stringhe basta modificare il codice per usare realloc...
char *read(FILE *f = stdin)
{
char *str = (char *)malloc(sizeof(char)*VARSTR_LEN);
while(1)
{
if(!fgets(str, VARSTR_LEN, f))
{
fprintf(stderr, "fgets error\n");
free(str);
return NULL;
}
if(strlen(str) < (VARSTR_LEN-1) || str[VARSTR_LEN-2] == '\n')
{
str[strlen(str)-1] = '\0';
return str;
}
else
{
if(!(str = (char *)realloc(str, (strlen(str) + 100)*sizeof(char)))
return NULL;
}
}
}
Originariamente inviato da downloader
anche se relativamente, l'heap è comunque piazzata nei gap della memoria non utilizzati
Vero, ma l'heap durante l'utilizzo si frammenta (basta pensare a cosa succede se si allocano A e B in sequenza e dopo si vuole ridimensionare A)...
downloader
19-12-2003, 15:26
hehehe ok chiudiamola qui. Sono d'accordo.
Ma converrai con me che in ogni caso, la lunghezza di una stringa non sarà mai troppo grande da risultare un problema... Certe problematiche prestazionali in termini di spazio utilizzato e velocità di accesso sono ininfluenti nella pratica.;)
Originariamente inviato da downloader
Certe problematiche prestazionali in termini di spazio utilizzato e velocità di accesso sono ininfluenti nella pratica.;)
Sicuramente nella maggior parte dei casi sono ininfluenti...
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.