Questo sito utilizza cookies, anche di terze parti, per mostrare pubblicità e servizi in linea con il tuo account. Leggi l'informativa sui cookies.
Username: Password: oppure
C/C++ - Estrarre elementi da una stringa simil-csv
Forum - C/C++ - Estrarre elementi da una stringa simil-csv

Avatar
()
Newbie


Messaggi:
Iscritto:

Segnala al moderatore
Postato alle 1:00
Giovedì, 01/01/1970
"Stuzzicato" da una discussione alla quale ho partecipato in un altro forum (in lingua inglese), ho messo insieme una piccola collezione di funzioni da hobbista. Nelle intenzioni, le funzioni dovrebbero fornire un modo preimpostato per caricare elementi da una stringa formattata secondo una specie di "csv della mutua".

Il formato leggibile è del tipo "elemento 1,elemento 2,elemento 3,elemento 4" e così via, dove "," può essere sostituito da qualunque carattere si desideri, purché rappresentabile tramite il tipo char.

Le funzioni che ho previsto sono (come da file d'intestazione items.h):

Codice sorgente - presumibilmente C++

  1. /**=============================================================================
  2. Libreria "items" - version 1.0.0, 20150102
  3. =============================================================================**/
  4.  
  5. #ifndef ITEMS_H_INCLUDED
  6. #define ITEMS_H_INCLUDED
  7.  
  8. long CountItemsInString( const char *data, int sep );
  9. long CountItemsInFile( const char *fileName, int sep );
  10.  
  11. long GetAllItemsFromString( const char *data, int sep, const char ***ptrs );
  12. long GetAllItemsFromFile( const char *fileName, int sep, const char ***ptrs );
  13.  
  14. char *GetItemFromString( const char *data, int sep, long nItem );
  15. char *GetItemFromFile( const char *fileName, int sep, long nItem );
  16.  
  17. char *LoadFile( const char *fileName );
  18. char *AllocateCopy( const char *s, long l );
  19. void FreeStringMemory( char **s );
  20. void FreeStringsMemory( const char ***ptrs );
  21.  
  22. #endif // ITEMS_H_INCLUDED



Le ultime quattro, in effetti, non sono molto utili (sono più che altro funzioni "di servizio" per le altre) ma le ho rese comunque accessibili già che erano lì, fatte e finite.

Siccome non sono certo un esperto, chiedo a chi avesse voglia di dare un'occhiata all'implementazione se sono realizzate in un modo sensato oppure no. Per quel che ne so, si compilano a dovere e non hanno creato problemi nei test, ma non si sa mai. Il file items.c è in allegato, non lo copio/incollo perché è troppo lungo.

Per onestà, vi dirò che circa una settimana fa ho posto la stessa domanda nel forum in inglese che citavo poco sopra, ma più che delle opinioni sulla correttezza ed efficacia del codice e/o dell'algoritmo ho ricevuto dei rimbrotti per ragioni che non ho ben capito (pare che mi si rimproverasse di non aver voglia d'imparare... mah...).

Ovviamente nessuno è costretto a rispondermi, ci mancherebbe altro, ma apprezzerei se qualcuno lo facesse.

Ultima modifica effettuata da il 07/01/2015 alle 19:13
PM Quote
Avatar
()
Newbie


Messaggi:
Iscritto:

Segnala al moderatore
Postato alle 19:26
Mercoledì, 07/01/2015
Bello... pare che per qualche ragione non vengano accettati gli allegati, per cui (con rammarico) provvedo a quel copia/incolla di dimensioni epiche che avrei voluto evitare.


Codice sorgente - presumibilmente C++

  1. /**=============================================================================
  2. "items" library - version 1.0.0, 20150102
  3. =============================================================================**/
  4.  
  5. #include "items.h"
  6. #include <stdlib.h>
  7. #include <stdio.h>
  8. #include <string.h>
  9. #include <ctype.h>
  10.  
  11.  
  12. /*==============================================================================
  13. Apre un file come stream testuale, alloca memoria dinamica nella quantita'
  14. opportuna e legge il testo in essa. La stringa risultante e' terminata da '\0'.
  15. Il chiamante deve liberare la memoria dinamica puntata dal valore di ritorno non
  16. appena non e' piu' necessaria. Se la funzione non riesce ad aprire il file o ad
  17. allocare memoria, il valore di ritorno e' NULL.
  18. ==============================================================================*/
  19.  
  20. char *LoadFile( const char *fileName ) {
  21.     char *auxPtr, *data = NULL;
  22.     long dataDim;
  23.     int c;
  24.     FILE *f;
  25.  
  26.     if( fileName == NULL ) return data;
  27.     if( *fileName == '\0' ) return data;
  28.  
  29.     f = fopen( fileName, "r" );
  30.     if( f == NULL ) return data;
  31.  
  32.     for( dataDim=0; fgetc(f)!=EOF; ++dataDim );
  33.  
  34.     data = malloc( dataDim+1 );
  35.  
  36.     if( data != NULL ) {
  37.         rewind( f );
  38.         auxPtr = data;
  39.  
  40.         while( (c=fgetc(f))!=EOF )
  41.             *auxPtr++ = c;
  42.         *auxPtr = '\0';
  43.     }
  44.  
  45.     fclose( f );
  46.     return data;
  47. }
  48.  
  49.  
  50. /*==============================================================================
  51. Alloca memoria dinamica e copia in essa una stringa. Il chiamante deve liberare
  52. la memoria dinamica allocata appena non ne ha piu' necessita'.
  53. ==============================================================================*/
  54.  
  55. char *AllocateCopy( const char *s, long l ) {
  56.     long sl; // [s]tring [l]ength
  57.     char *copy = NULL;
  58.  
  59.     if( s == NULL ) return copy;
  60.  
  61.     sl = l<0 ? strlen(s) : l;
  62.  
  63.     if( (copy=malloc(sl+1)) != NULL )
  64.         { memcpy(copy,s,sl); copy[sl]='\0'; }
  65.  
  66.     return copy;
  67. }
  68.  
  69.  
  70. /*==============================================================================
  71. Restituisce la quantita' totale degli elementi contenuti nella stringa passata
  72. tramite il parametro data. Gli elementi vengono riconosciuti contando le
  73. ricorrenze del carattere passato tramite il parametro sep.
  74. ==============================================================================*/
  75.  
  76. long CountItemsInString( const char *data, int sep ) {
  77.     long n;
  78.  
  79.     if( data == NULL ) return -1;
  80.  
  81.     for( n=1; *data; ++data )
  82.         if( *data == sep ) ++n;
  83.  
  84.     return n;
  85. }
  86.  
  87.  
  88. /*==============================================================================
  89. Restituisce la quantita' totale degli elementi contenuti nel file il cui nome e'
  90. passato tramite il parametro fileName. Gli elementi vengono riconosciuti
  91. contando le ricorrenze del carattere passato tramite il parametro sep.
  92. ==============================================================================*/
  93.  
  94. long CountItemsInFile( const char *fileName, int sep ) {
  95.     FILE *f;
  96.     long n;
  97.     int c;
  98.  
  99.     if( fileName == NULL ) return -1;
  100.     if( *fileName == '\0' ) return -1;
  101.  
  102.     f = fopen( fileName, "r" );
  103.     if( f == NULL ) return -1;
  104.  
  105.     for( n=1, c=fgetc(f); c!=EOF; c=fgetc(f) )
  106.         if( c == sep ) ++n;
  107.  
  108.     fclose( f );
  109.     return n;
  110. }
  111.  
  112.  
  113. long TokeniseString( char *data, int sep, const char ***ptrs ) {
  114.     const char **auxPtr, **tap; /* [t]emporary [a]rray of [p]ointers */
  115.     const char *strtok_result;
  116.     long totItems;
  117.     char sp[2] = { sep, '\0' };
  118.  
  119.     totItems = CountItemsInString( data, sep );
  120.  
  121.     auxPtr = tap = calloc( totItems, sizeof(char*) );
  122.     if( tap == NULL ) return -1;
  123.  
  124.     for( strtok_result = strtok(data,sp);
  125.          strtok_result != NULL;
  126.          *auxPtr++=strtok_result, strtok_result=strtok(NULL,sp));
  127.  
  128.     *ptrs = tap;
  129.     return totItems;
  130. }
  131.  
  132.  
  133. /*==============================================================================
  134. Carica tutti gli elementi dalla stringa passata tramite il parametro data,
  135. copiandoli nella memoria dinamica e restituendo il loro indirizzo sotto forma di
  136. un array di puntatori a char. Gli elementi vengono riconosciuti contando le
  137. ricorrenze del carattere passato tramite il parametro sep.
  138. ==============================================================================*/
  139.  
  140. long GetAllItemsFromString( const char *data, int sep, const char ***ptrs ) {
  141.     long totItems = -1;
  142.     char *copy;
  143.  
  144.     copy = AllocateCopy( data, -1 );
  145.     if( copy == NULL ) return -1;
  146.  
  147.     totItems = TokeniseString( copy, sep, ptrs );
  148.     if( totItems == -1 ) free( copy );
  149.  
  150.     return totItems;
  151. }
  152.  
  153.  
  154. /*==============================================================================
  155. Carica tutti gli elementi contenuti nel file del quale viene passato il nome
  156. tramite il parametro fileName, copiandoli nella memoria dinamica e restituendo
  157. il loro indirizzo sotto forma di un array di puntatori a char. Gli elementi
  158. vengono riconosciuti contando le ricorrenze del carattere passato tramite il
  159. parametro sep.
  160. ==============================================================================*/
  161.  
  162. long GetAllItemsFromFile( const char *fileName, int sep, const char ***ptrs ) {
  163.     long totItems = 0;
  164.     char *data = NULL;
  165.  
  166.     if( fileName == NULL ) return -1;
  167.     if( *fileName == '\0' ) return -1;
  168.  
  169.     data = LoadFile( fileName );
  170.         if( data == NULL ) return -1;
  171.  
  172.     totItems = TokeniseString( data, sep, ptrs );
  173.     if( totItems == -1 ) free( data );
  174.  
  175.     return totItems;
  176. }
  177.  
  178.  
  179. /*==============================================================================
  180. Carica dalla stringa data l'elemento alla posizione indicata dal parametro nItem
  181. (zero based), copiandolo nell'area della memoria dinamica puntata dal puntatore
  182. a char che viene restituito come valore di ritorno. Gli elementi vengono
  183. riconosciuti contando le ricorrenze del carattere passato tramite il parametro
  184. sep.
  185. ==============================================================================*/
  186.  
  187. char *GetItemFromString( const char *data, int sep, long nItem ) {
  188.     long ci; /* [c]urrent [i]tem */
  189.     char *strItem = NULL;
  190.     char sp[2] = { sep, '\0' };
  191.  
  192.     if( data == NULL ) return strItem;
  193.  
  194.     for( ci=0;
  195.          (ci<nItem)&&(data=strchr(data,sep));
  196.          ++data, ++ci );
  197.  
  198.     if( ci == nItem )
  199.         strItem = AllocateCopy( data, strcspn(data,sp) );
  200.  
  201.     return strItem;
  202. }
  203.  
  204.  
  205. /*==============================================================================
  206. Carica dal file che ha per nome la stringa passata tramite il parametro data
  207. l'elemento alla posizione indicata dal parametro nItem (zero based), copiandolo
  208. nell'area della memoria dinamica puntata dal puntatore a char che viene
  209. restituito come valore di ritorno. Gli elementi vengono riconosciuti contando le
  210. ricorrenze del carattere passato tramite il parametro sep.
  211. ==============================================================================*/
  212.  
  213. char *GetItemFromFile( const char *fileName, int sep, long nItem ) {
  214.     long i, start, length, c, ci; /* [c]urrent [i]tem */
  215.         char *strItem = NULL;
  216.     FILE *f;
  217.  
  218.     if( fileName == NULL ) return strItem;
  219.     if( *fileName == '\0' ) return strItem;
  220.  
  221.     f = fopen( fileName, "r" );
  222.     if( f == NULL ) return strItem;
  223.  
  224.     for( ci=0, c=fgetc(f); ci!=nItem && c!=EOF; c=fgetc(f) )
  225.         if( c == sep ) ++ci;
  226.  
  227.     if( ci == nItem ) {
  228.         start = ftell(f)-1;
  229.         for( c=fgetc(f); c!=sep&&c!=EOF; c=fgetc(f) );
  230.         length = ftell(f)-1-start;
  231.  
  232.         if( (strItem=malloc(length+1)) != NULL ) {
  233.             fseek( f, start, SEEK_SET );
  234.             for( i=0; i<length; *(strItem+i)=fgetc(f), ++i );
  235.             *(strItem+i) = '\0';
  236.         }
  237.     }
  238.  
  239.     fclose( f );
  240.     return strItem;
  241. }
  242.  
  243.  
  244. /*==============================================================================
  245. Questa e' banale. Libera la memoria dinamica puntata dal contenuto del parametro
  246. s e imposta il puntatore in s su NULL (in altre parole, quando la funzione
  247. ritorna, il contenuto di s e' NULL).
  248. ==============================================================================*/
  249.  
  250. void FreeStringMemory( char **s ) {
  251.     if( *s!= NULL ) {
  252.         free( (void*)*s );
  253.         *s = NULL;
  254.     }
  255. }
  256.  
  257.  
  258. /*==============================================================================
  259. Libera la memoria allocata nello spazio dinamico dalla funzione
  260. GetItemsFromString() o dalla funzione GetItemsFromFile(). In uscita, il valore
  261. del contenuto di ptrs e' NULL.
  262. ==============================================================================*/
  263.  
  264. void FreeStringsMemory( const char ***ptrs ) {
  265.     if( ptrs == NULL ) return;
  266.     if( *ptrs == NULL ) return;
  267.  
  268.     if( **ptrs != NULL ) {
  269.         free( (void*)(**ptrs) );
  270.         **ptrs = NULL;
  271.     }
  272.  
  273.     free( (void*)(*ptrs) );
  274.     (*ptrs) = NULL;
  275. }


Ultima modifica effettuata da il 07/01/2015 alle 19:35
PM Quote
Avatar
pierotofy (Admin)
Guru^2


Messaggi: 6109
Iscritto: 04/12/2003

Segnala al moderatore
Postato alle 20:30
Mercoledì, 07/01/2015
Mi sembra un ottimo esercizio!

Gli sbrottamenti che hai incontrato probabilmente vengono dal fatto che un'imlementazione completa per il formato CSV non è proprio così semplice (non si tratta di semplicemente tokenizzare sulla virgola), ci sono regole un pò più complesse: https://tools.ietf.org/html/rfc4180

La cosa bella è che molti programmi esportano su CSV ma non seguono perfettamente lo standard RFC4180, il che crea una serie di nuovi problemi.

Ma siccome l'intento è di generare un simil-csv, fintanto che sei a conoscenza delle limitazioni... tutto a posto!

Come memorizzo la stringa "ciao, come va?" usando "," come separatore? :)


Seguimi su Twitter: http://www.twitter.com/pierotofy

Fai quello che ti piace, e fallo bene.
PM Quote
Avatar
()
Newbie


Messaggi:
Iscritto:

Segnala al moderatore
Postato alle 21:58
Mercoledì, 07/01/2015
Pfffff!!! Meno male che non hai trovato errori, ho sempre il timore che mi sfugga qualcosa (provo e riprovo con mille test, ma il timore rimane lì). Allora userò queste funzioni nei miei giochetti, d'ora in avanti.

Circa la stringa "ciao, come va?" con la virgola come separatore è al di là dello scopo di queste funzioni. Diciamo che sarebbe sufficiente usare come separatore un carattere poco comune nella lingua italiana? Tipo, che so, '#' o '|' o '\t'...

Per un'implementazione più completa del formato csv ho revisionato ieri l'altro una classe c++ che avevo sviluppato un paio d'anni fa. In quella classe si tiene conto anche di cose come l'uso delle virgolette (che possono essere o non essere presenti) e l'espunzione dei caratteri di spaziatura in apertura e chiusura degli elementi, nonché del fatto che csv "ragiona" secondo uno schema che a me ricorda una matrice bidimensionale (tot campi per ogni record, con i campi divisi da un delimitatore arbitrario e un record per riga). Ricordo che avevo usato come riferimento questa pagina: http://creativyst.com/Doc/Articles/CSV/CSV01.htm, dove si dice che non esiste un vero standard univoco "ufficiale" per il formato csv (vero o falso?). In effetti la versione che mi hai suggerito (e che ho scaricato) pare che abbia delle piccole differenze rispetto a quella alla quale ho fatto riferimento. Anzi, pare che descriva più versioni, non una sola.

Ammetto che mi piacerebbe condividere la mia classe, ma non mi azzardo a farlo di mia iniziativa perché il file di implementazione è scandalosamente lungo (442 righe! :om: ) e magari anche un po' "spaghettoso". Più maneggevole, invece, il file di intestazione:

Codice sorgente - presumibilmente C++

  1. /** v1.4.0 - 2015 01 05 **/
  2.  
  3. #ifndef CSV_H_INCLUDED
  4. #define CSV_H_INCLUDED
  5.  
  6. #include <stdio.h>
  7.  
  8.  
  9. class CSV {
  10.  
  11.     public:
  12.  
  13.     CSV();
  14.     CSV( const char *dati, char separatore = ',' );   // throw const char *
  15.     CSV( FILE *dati, char separatore = ',' );   // throw const char *
  16.     CSV( const CSV &ex );                       // throw const char *
  17.     ~CSV();
  18.  
  19.     CSV operator=( CSV &ex );                   // throw const char *
  20.  
  21.     void set_csv( const char *dati, char separatore = ',' );   // throw const char *
  22.     void set_csv( FILE *dati, char separatore = ',' );   // throw const char *
  23.  
  24.     int get_nColonne( void ) { return nColonne; }
  25.     int get_nRighe( void ) { return nRighe; }
  26.     double get_dato_num( int riga, int colonna );
  27.     const char *get_dato( int riga, int colonna );
  28.     int get_dimDato( int riga, int colonna );
  29.  
  30.     private:
  31.  
  32.     char *s;
  33.     char **p;
  34.     int nColonne;
  35.     int nRighe;
  36.     size_t dimDati;
  37.  
  38.     void InizializzaNullo( void );
  39.     void AnalizzaDati( char separatore );
  40.     void IndicizzaDati( char separatore );
  41.     void PulisciStringheDati( void );
  42.     void Copia( const CSV *csvOrig );
  43.     void Dealloca( void );
  44. };
  45.  
  46. #endif // CSV_H_INCLUDED



Che dici, può valere la pena "svelare" l'implementazione? Potrebbe interessare a qualcuno? Lo farei volentieri per spirito solidaristico, perché quando trovo codice altrui che mi aiuti a capire qualche particolarità sulla quale ho dei dubbi faccio i salti di gioia. Però non sono sicuro d'aver fatto chissà quale capolavoro (non ho modo di confrontarmi con nessuno nel "mondo reale", ed è una grande limitazione) e una "supervisione" sarebbe davvero un toccasana (tra l'altro, servirebbe anche a me per imparare qualcosa).

Scusa se sono un po' logorroico, ma ho sempre tante cose da dire. E poi è un segno d'entusiasmo, no?

PM Quote
Avatar
pierotofy (Admin)
Guru^2


Messaggi: 6109
Iscritto: 04/12/2003

Segnala al moderatore
Postato alle 22:10
Mercoledì, 07/01/2015
Testo quotato


dove si dice che non esiste un vero standard univoco "ufficiale" per il formato csv (vero o falso?)



Falso, RFC4180 è lo standard di riferimento, poi che alcuni programmatori deviano dallo standard per creare diverse implementazioni è un altro discorso.

Potresti fare richiesta per entrare come membro, dopodichè potresti condividere il programma nella sezione C++. http://www.pierotofy.it/pages/members/join_module/

Saresti accettato subito :)


Seguimi su Twitter: http://www.twitter.com/pierotofy

Fai quello che ti piace, e fallo bene.
PM Quote
Avatar
()
Newbie


Messaggi:
Iscritto:

Segnala al moderatore
Postato alle 22:28
Mercoledì, 07/01/2015
Testo quotato

Postato originariamente da pierotofy:Potresti fare richiesta per entrare come membro, dopodichè potresti condividere il programma nella sezione C++. http://www.pierotofy.it/pages/members/join_module/

Saresti accettato subito :)



Guarda che come "programmatore" sono un caprone (vedi la mia firma, qui sotto)!
Comunque vado a vedere quella pagina. Subito!

PM Quote
Avatar
pierotofy (Admin)
Guru^2


Messaggi: 6109
Iscritto: 04/12/2003

Segnala al moderatore
Postato alle 17:47
Giovedì, 08/01/2015
Accettato, fammi sapere se ti è arrivata la mail di conferma.


Seguimi su Twitter: http://www.twitter.com/pierotofy

Fai quello che ti piace, e fallo bene.
PM Quote
Avatar
AldoBaldo (Member)
Expert


Messaggi: 345
Iscritto: 08/01/2015

Segnala al moderatore
Postato alle 19:42
Giovedì, 08/01/2015
Ricevuta!

Ma... hai visto il programma? Non è un po' troppo... ehm... "involuto"? C'è da dire che non l'ho in alcun modo riordinato per "presentarlo" -- una volta funzionante l'ho tenuto così com'era dal 2013, e così com'era l'ho spedito. Va be', l'importante è che ora sono un MEMBER! E vai così! Ora dovrò studiare un po' meglio le opportunità offerte da pierotofy.it. Un bello stimolo. Grazie.

Ultima modifica effettuata da AldoBaldo il 08/01/2015 alle 19:43


Ma cosa vuoi che ne sappia? Io ci gioco, col codice, mica ci lavoro!
PM Quote