PDA

Visualizza la versione completa : [C] Da stringa ad array di stringhe di lunghezza predefinita


fbcyborg
03-12-2010, 18:36
Salve a tutti,

sono oramai giorni che tento di scrivere un programma in C che data una stringa di lunghezza arbitraria, mi restituisca un'array di array di stringhe. Sostanzialmente, vorrei dividere una stringa, o più generalmente un tot di bytes di dati, in blocchi da 8 stringhe. La parte di codice che decide quanti blocchi servono l'ho già implementata.
Stavo facendo anche altre prove, fra cui quella di fare una read su file, e memorizzarne tutto il contenuto in un array di byte. Quindi a partire da questo array di byte, volevo prendere 8 byte alla volta da esso e trasferirli in un'array di array da 8 byte ciascuno. Solo che non ci riesco. Ho problemi anche nella copia dell'array. Qualcuno potrebbe instradarmi, in modo da poter cominciare a buttare giù qualche linea di codice che abbia senso?

simo_85
03-12-2010, 21:04
Lo stesso problema era stato trattato tempo fa sul forum di ubuntu, nel cuale un tente ha postato la seguente soluzione, comincia da questo codice:


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

char *nextWord(char** string)
{
char *word, *start, *s = *string;
int len;

/*Cerco il primo carattere valido*/
while( !(*s>='a'&&*s<='z') && !(*s>='A'&&*s<='Z') )
{
if(*s=='\0')
return NULL;
s++;
}

/*Salvo l'inizio della parola*/
start = s;

/*Cerco la fine della parola*/
while( (*s>='a'&&*s<='z')||(*s>='A'&&*s<='Z') )
s++;

/*Alloco la memoria per la parola e la copio in quella memoria.*/
len = s-start;
word=malloc((len+1)*sizeof(char));
strncpy(word,start,len);
word[len]='\0';

/*Aggiorno la stringa*/
*string=s;

return word;
}

int pointer_toString_alloc(char* string, char*** words)
{
char *word;
*words = NULL;
int count =0;

while( (word=nextWord(&string))!=NULL )
{
count++;
if(*words==NULL)
*words=malloc(sizeof(char*));
else
*words=realloc(*words,count*sizeof(char*));

(*words)[count-1] = word;
}

return count;
}

int main(void)
{
char string[200];
char** words;
int count,i;

printf("\n** Please insert a sentence: ");
fflush(stdin);
fgets(string, sizeof(string), stdin);
/*scanf("%[^\n]", &string);*/
count = pointer_toString_alloc(string, &words);
for(i=0;i<count;i++)
printf("@ % p byte[%d] = %s\n", &words[i], i, words[i]);

free(words);
return 0;
}

fbcyborg
03-12-2010, 22:27
Ciao,

per prima cosa voglio ringraziarti per la risposta. È anche probabile che fra tutte le ricerche che ho fatto su Internet fin'ora sia già capitato di fronte a quel codice. Solo che a differenza di ora non l'avevo provato.
Il problema fondamentale è che questo codice fa la separazione in token, dove il delimitatore è lo spazio. Io invece dovrei trattare stringhe di lunghezza indefinita (ipoteticamente anche lunghe migliaia di caratteri, e senza spazi) e spezzarle in blocchi di 8 byte ciascuno. Poi potrei, in seguito, trattare direttamente byte.

Al momento ho provato a buttare giù del codice, che però non funziona:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>

#define BLOCK_DIM 8

int get_filesize(char *file_name);

typedef struct {
void *block;
} block_bytes;

int main(int argc, char *argv[]){
int file_size = get_filesize("textfile.txt");
int num_of_blocks;
int i,j,k;
void *file;

printf("File size: %d\n", file_size);
int abs = file_size % BLOCK_DIM;
printf("abs: %d\n", abs);
if(abs == 0){
num_of_blocks = file_size / BLOCK_DIM;
}else{
num_of_blocks = file_size / BLOCK_DIM + 1;
}
printf("blocks: %d\n", num_of_blocks);

block_bytes blocks[num_of_blocks];

int fd = open("textfile.txt",O_RDONLY);
if(fd==-1){
perror("could not open file");
exit(-1);
}

file = malloc(file_size);

if(read(fd,file,file_size)==-1){
perror("could not open file");
exit(-1);
}

close(fd);

printf("%s\n",(char *)file);
/* QUI VORREI INSERIRE DEL CODICE CHE FA LA
* SCANSIONE DI file CARATTERE PER CARATTERE
* E PRENDE I BYTE DI file 8 ALLA VOLTA E
* LI INSERISCE IN OGNI ELEMENTO DI blocks
* CHE A SUA VOLTA È UN ARRAY DI num_blocks ELEMENTI
*/
for(j=0;j<file_size;j++){
for(k=0;k<BLOCK_DIM;k++){

}
}
}

int get_filesize(char *file_name){
struct stat file_stat;

if(stat(file_name, &file_stat) != 0) {
perror("could not stat");
return (-1);
}
return (int)file_stat.st_size;
}



Inoltre, qui (http://stackoverflow.com/questions/4168244/c-split-store-string-of-x-length-into-an-array-of-structs/4169168#4169168) ho trovato anche il seguente codice:

#include <string.h>

#define SMAX 10
typedef struct {char str[SMAX+1];} ST;

int main()
{
ST st[SMAX]={0};
char *tmp = "For configuration options (arch/xxx/config.in, and all the Config.in files),somewhat different indentation is used.";
int i=0,j;
for( ; (st[i++]=*(ST*)tmp).str[SMAX]=0 , strlen(tmp)>=SMAX; tmp+=SMAX );

for( j=0;j<i;++j )
puts(st[j].str);

return 0;
}
Il problema è che per SMAX<11 va in errore, altrimenti sarebbe anche buona come funzione. Certo, dovrei riadattarla (anche se per me è un po' complicato il primo ciclo for), ma almeno funziona. Io dovrei impostare SMAX = 8.

Poi ho finito le idee... :(

simo_85
04-12-2010, 00:00
Ok scusa non avevo capito ben :D ..
Comunque, a partire dalla lunghezza della tua stringa miliare :D puoi anche calcolare quante stringhe da 8 bytes puoi creare.
Es: se la stringa casualmente è lunga 172 bytes/caratteri, saprai che a priori ti serviranno 22 arrays da 8 per contentere la stringa totale (se i sotto arrays devono essere per forza tutti da 8, sennò per l'ultimo puoi essere esatto con l'allocazione di memoria, senza lasciare bytes li a girarsi i pollici) . Giusto? Per ovviare questo problema ti basta una piccola funzione..
Infine, il tuo problema è risolvibile, se non sbaglio, con un puntatore a puntatori ed un buon utilizzo di malloc & (forse) anche realloc

fbcyborg
04-12-2010, 00:35
Originariamente inviato da simo_85
Ok scusa non avevo capito ben :D ..
Comunque, a partire dalla lunghezza della tua stringa miliare :D puoi anche calcolare quante stringhe da 8 bytes puoi creare.
OK, e fin qui, ci siamo. Infatti ho scritto questo pezzo di codice appositamente, proprio per sapere a priori quanti blocchi da 8 byte mi servono:

if(abs == 0){
num_of_blocks = file_size / BLOCK_DIM;
}else{
num_of_blocks = file_size / BLOCK_DIM + 1;
}


Originariamente inviato da simo_85
Es: se la stringa casualmente è lunga 172 bytes/caratteri, saprai che a priori ti serviranno 22 arrays da 8 per contentere la stringa totale (se i sotto arrays devono essere per forza tutti da 8, sennò per l'ultimo puoi essere esatto con l'allocazione di memoria, senza lasciare bytes li a girarsi i pollici) . Giusto? Per ovviare questo problema ti basta una piccola funzione..
Sì, in realtà, mi creo N blocchi più 1, (es, stringa di 25 caratteri, 24 è un multiplo di 8, quindi 4 blocchi pieni più uno per il 25° carattere) con il codice qui sopra, o meglio, so quanti blocchi mi servono. Inoltre con l'ultimo blocco, non pieno, (caso n. blocchi non è multiplo di 8), devo fare il padding (pe esempio aggiungendo spazi), e per questo ho un'altra funzione ancora già pronta, che lo fa.

Originariamente inviato da simo_85
Infine, il tuo problema è risolvibile, se non sbaglio, con un puntatore a puntatori ed un buon utilizzo di malloc & (forse) anche realloc
Ecco, è proprio qui che avrei bisogno di una grossa mano purtroppo. Ho fatto diversi tentativi, con diversi approcci al problema ma non ne vengo fuori. Mi dici che basta una piccola funzione, ma con i puntatori di puntatori comincio a fare un po' di confusione.

Il motivo della stringa miliare è semplicissimo. Devo poter trattare file (per ora diciamo di testo) di lunghezza ignota a priori, e quindi, potrebbero anche non contenere spazi, o comunque caratteri casuali. Ma questo non importa.

Grazie molte per l'aiuto. :ciauz:

simo_85
04-12-2010, 01:06
Originariamente inviato da fbcyborg
Ecco, è proprio qui che avrei bisogno di una grossa mano purtroppo. Ho fatto diversi tentativi, con diversi approcci al problema ma non ne vengo fuori. Mi dici che basta una piccola funzione, ma con i puntatori di puntatori comincio a fare un po' di confusione.

Il motivo della stringa miliare è semplicissimo. Devo poter trattare file (per ora diciamo di testo) di lunghezza ignota a priori, e quindi, potrebbero anche non contenere spazi, o comunque caratteri casuali. Ma questo non importa.
Grazie molte per l'aiuto. :ciauz:

Un puntatore a puntatore è come un array di arrays.. A te serve solo calcolare quanti array di colonna hai bisogno se la loro lunghezza è di 8 bytes.. Fatto quello diciamo che puoi riempire con un ciclo.

fbcyborg
04-12-2010, 08:32
OK, in realtà anche questa era una prova che avevo fatto, e mi era anche sorto il dubbio se inserire anche lo '\0' o no. Quindi dovrei dichiarare un'array del tipo:

void blocks[num_of_blocks][BLOCK_DIM]
Ma si può fare? :confused:
Forse nel caso del char sì...

char blocks[num_of_blocks][BLOCK_DIM]

Nel caso di utilizzo anche di '\0' (che non so se serva o no, ma penso di no), dovrei fare:

char blocks[num_of_blocks][BLOCK_DIM+1]

Comunque per me, anche in ottica di implementazioni future del software che vorrei scrivere, sarebbe più comodo usare una matrice di void, però mi pare che ci siano dei problemi, ovvero:

ho pensato di fare anche così:

typedef struct {
void block[BLOCK_DIM];
} block_bytes;

Per poi dichiarare un'array così, più avanti nel codice:
block_bytes blocks[BLOCK_DIM], però quella dichiarazione all'interno della struct non me la consente.

fbcyborg
04-12-2010, 10:01
Dunque, ho provato con la matrice:


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>

#define BLOCK_DIM 8

int get_filesize(char *file_name);
/*
typedef struct {
char block[BLOCK_DIM];
} block_bytes;
*/
int main(int argc, char *argv[]){
int file_size = get_filesize("textfile.txt");
int num_of_blocks;
int i,j,k;
char *file;

printf("File size: %d\n", file_size);
int abs = file_size % BLOCK_DIM;
printf("abs: %d\n", abs);
if(abs == 0){
num_of_blocks = file_size / BLOCK_DIM;
}else{
num_of_blocks = file_size / BLOCK_DIM + 1;
}
printf("blocks: %d\n", num_of_blocks);

char blocks[num_of_blocks][BLOCK_DIM];

int fd = open("textfile.txt",O_RDONLY);
if(fd==-1){
perror("could not open file");
exit(-1);
}

file = malloc(file_size);

if(read(fd,file,file_size)==-1){
perror("could not open file");
exit(-1);
}

close(fd);

printf("%s\n",(char *)file);
k=0;
for(i=0;i<file_size;i++){ // scansione su array file
if(k==BLOCK_DIM){ // se sono arrivato alla fine della riga
k=0; // azzero k
j++; // passo alla riga successiva
}
blocks[j][k] = file[i]; // copio il char puntato da i in blocks[j][k]
k++;
}

for(i=0;i<num_of_blocks;i++)
printf("%s\n",blocks[i]);
}

int get_filesize(char *file_name){
struct stat file_stat;

if(stat(file_name, &file_stat) != 0) {
perror("could not stat");
return (-1);
}
return (int)file_stat.st_size;
}

Purtroppo però mi va in segmentation fault, la prima volta che cerca di fare la copia, ovvero qui:

blocks[j][k] = file[i];
Ho pensato di fare una malloc, ma è un casino.
Ovvero anche facendo così

char **blocks;
blocks = (char **)malloc(num_of_blocks*BLOCK_DIM);
for(i=0; i<num_of_blocks; i++)
blocks[i] = (char *)malloc( BLOCK_DIM * sizeof(char) );
invece che dichiarare la matrice come ho fatto prima, mi va sempre in segmentation fault.

Dove sto sbagliando?

Ippo343
04-12-2010, 11:27
Scusa sono di corsa e non ho avuto tempo di leggere bene tutta la discussione quindi questo post ha alte probabilità di essere inutile.

Se non ho capito male, tu hai un'enorme stringa e vuoi dividerlo in un array di stringhe tutte lunghe BLOCK_SIZE, giusto?

Allora puoi usare un ciclo for che incrementa ogni volta il puntatore alla stringa di BLOCK_SIZE, e usare la strncpy per copiare BLOCK_SIZE caratteri nel tuo array.

Più tardi se ho tempo leggo tutto e vedo quanto è stata inutile questa risposta :)

fbcyborg
04-12-2010, 11:36
Ciao!
Grazie! L'idea sarebbe buona, ma c'ho già provato e non funziona (almeno a me).
Il problema è che facendo così non funziona:


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

int main(int argc, char *argv[]){
char *frase = "Ciao come stai? Hello world!";
char blocchi[4][8];
int i,j,k;
for(i=0;i<28;i++){ // scansione su array file
strncpy(blocchi[k],frase[i],8);
k++;
}
for(i=0;i<4;i++)
printf("%s\n",blocchi[i]);
}

Di sicuro è sbagliato il codice, ma per un motivo o per un altro c'è qualche cosa che non riesco a fare. A questo punto mi domando: ma non è che questa cosa che cerco di fare è impossibile? O_O È assurdo che sia così complicata una cosa apparentemente così stupida.

Altro tentativo:


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

int main(int argc, char *argv[]){
char *frase = "Ciao come stai? Hello world!";
char blocchi[4][8];
int i,j,k;
for(i=0;i<28;i++){ // scansione su array file
if(k==8){ // se sono arrivato alla fine della riga
k=0; // azzero k
j++; // passo alla riga successiva
}
blocchi[j][k] = frase[i]; // copio il char puntato da i in blocks[j][k]
k++;
}
for(i=0;i<4;i++)
printf("%s\n",blocchi[i]);
}

Questo stampa quanto segue:
Ciao come stai? Hello world!
e stai?_Hello world!
Hello world!
rld!____
Poi vabbè, l'ultimo blocco andrebbe paddato, ma questo lo posso fare.
(Ho rappresentato gli spazi con "_".
Ho evidenziato quelle parti, perché se ci fosse un terminatore di stringa dopo l'ottavo carattere, forse otterrei quello che voglio.

Loading