Questo sito utilizza cookies solo per scopi di autenticazione sul sito e nient'altro. Nessuna informazione personale viene tracciata. Leggi l'informativa sui cookies.
Username: Password: oppure
C/C++ - Un po' di (in)utilità per stringhe in C
Forum - C/C++ - Un po' di (in)utilità per stringhe in C - Pagina 3

Pagine: [ 1 2 3 4 ] Precedente | Prossimo
Avatar
lumo (Member)
Expert


Messaggi: 449
Iscritto: 18/04/2010

Segnala al moderatore
Postato alle 17:20
Martedì, 23/05/2017
Sul tampone ero ovviamente ironico.

Per quanto riguarda l'uso delle stringhe in C, la mia opinione personale è che dove serva manipolazione delle stringhe molto avanzata usare il C è un controsenso.
Nonostante questo ci sono molti software scritti in C che lavorano molto con il testo, ad esempio gli editor di testo (vim tanto per dirne uno). In quei casi ho visto che a parte qualche helper di solito si preferisce andare di malloc+funzioni standard, è quello che ci si aspetta normalmente.

Un approcio che considero interessante in C è quello di sdstring, https://github.com/antirez/sds (ricordo che antirez è Salvatore Sanfilippo, autore di Redis dove si usa anche sds).
Il codice è molto facile da comprendere e la particolarità è che gli oggetti sdstring si possono passare in modo compatibile alle funzioni che vogliono un const char* con terminatore nullo.

Secondo me è molto più didattico implementare un clone di std::string semplice (magari senza tutto il lato C++ che serve per interagire con la STL, cioè iteratori e allocatore ridefinibile).
Come struttura si usa il classico puntatore + lunghezza + memoria allocata.
Di solito std::string è più complicato perché utilizza delle ottimizzazioni, una decina di anni fa era il copy-on-write, adesso mi pare che quasi tutte le librerie standard per C++ comuni utilizzino la small string optimization, ci sono molti video su youtube su questo argomento ben fatti.

Ultima modifica effettuata da lumo il 23/05/2017 alle 17:22
PM Quote
Avatar
AldoBaldo (Member)
Guru


Messaggi: 699
Iscritto: 08/01/2015

Segnala al moderatore
Postato alle 19:35
Martedì, 23/05/2017
Ragazzi carissimi, vi ringrazio veramente tanto per la pazienza che mi riservate e per gli spunti che mi proponete. Alcuni sono in grado di comprenderli e state ben certi che ne faccio tesoro, altri volano oggettivamente un po' troppo alto sopra la mia testa.

Rimane un fatto: non sto più usando "buffer", bensì "tampone". Ovviamente, se prima abbrevviavo con "buff", ora abbrevio con "tamp" (colpa tua, lumo). Vi avverto: sono a un passo dal cominciare a usare nomenclature in piemontese... ad esempio, sostituisci_frammento() potrebbe diventare cambeja_n_toc() :rotfl:


ATTENZIONE! Sono un hobbista e l'affidabilità delle mie conoscenze informatiche è molto limitata. Non prendere come esempio il codice che scrivo, perché non ho alcuna formazione accademica e rischieresti di apprendere pratiche controproducenti.
PM Quote
Avatar
Mikelius (Member)
Expert


Messaggi: 525
Iscritto: 14/04/2017

Segnala al moderatore
Postato alle 20:03
Martedì, 23/05/2017
Testo quotato

Postato originariamente da AldoBaldo:

Ragazzi carissimi, vi ringrazio veramente tanto per la pazienza che mi riservate e per gli spunti che mi proponete. Alcuni sono in grado di comprenderli e state ben certi che ne faccio tesoro, altri volano oggettivamente un po' troppo alto sopra la mia testa.




Ho ripescato la mia vecchia funzione DoppioSpazio().
Funziona (almeno pare) non ho avuto tempo di ricontrollarla per bene, mi piacerebbe poi vedere la tua di implementazione AldoBaldo.
(p.s. ho pure incluso un piccolo main() per una prova al volo se volete)

Codice sorgente - presumibilmente C++

  1. /*
  2.  * Data una stringa, crea una stringa con l'eliminazione dei doppi spazi.
  3.  * Ritorna la dimensione della nuova stringa. Source e destination devono
  4.  * essere della stessa dimensione per il caso limite che source non avesse doppiSpazi
  5.  *** MANCANO I CONTROLLI SULL'INPUT****
  6.  */
  7.  
  8. #include <stdlib.h>
  9. #include <stdio.h>
  10. #include <string.h>
  11.  
  12. /* Controlla un carattere con il successivo, se sono entrambi spazi, avanza il primo indice e ne copia solo 1
  13.  * altrimenti copia normalmente il carattere. Se si trova un doppio spazio ripete il do while
  14.  * (potrebbero esserci 3 spazii consecutivi ad esempio). Infine ritorna la dimensione nella nuova stringa pulita
  15.  */
  16. size_t doppioSpazio(const char *source, char *destination )
  17. {
  18.     int controllo = 0; // Controlla se si trova un doppioSpazio
  19.     size_t i = 0 ;  //Primo indice
  20.     size_t k = 0 ; // Secondo Indice
  21.     size_t dimFrase = strlen(source);
  22.  
  23.     char *appoggio = (char*)malloc( (dimFrase+1)* sizeof( char ) );
  24.     strncpy( appoggio, source, dimFrase+1 );
  25.  
  26.     do{
  27.         controllo = 0;
  28.         for( k=0, i = 0; i < dimFrase; k++, i++ ){
  29.             if( appoggio[i] == ' ' && appoggio[i+1] == ' ' ){
  30.                i++;
  31.                controllo = 1;
  32.             }
  33.             appoggio[k] =appoggio[i];
  34.         }
  35.         appoggio[k] = '\0';
  36.     } while( controllo==1);
  37.     strcpy( destination, appoggio );
  38.     free(appoggio);
  39.     return strlen(destination);
  40. }
  41. int main( void )
  42. {
  43.     char    src[50] = "Ciao    Mondo    Bello    ";
  44.     char   dest[50] = "";
  45.     size_t    dimN  = 0;
  46.     printf( "\n**** STRINGHE INIZIALI ****\n\n" );
  47.     printf( "Stringa Iniziale =[%s]\n", src );
  48.     printf( "Stringa Finale   =[%s]\n", dest );
  49.  
  50.     dimN = doppioSpazio( src, dest);
  51.     printf( "\n\n\n\n**** STRINGHE FINALI ****\n\n" );
  52.     printf( "Stringa Iniziale =[%s]\n", src );
  53.     printf( "Stringa Finale   =[%s]\n", dest );
  54.  
  55.     return 0;
  56. }



Ultima modifica effettuata da Mikelius il 23/05/2017 alle 20:06
PM Quote
Avatar
lumo (Member)
Expert


Messaggi: 449
Iscritto: 18/04/2010

Segnala al moderatore
Postato alle 20:23
Martedì, 23/05/2017
Facilissimo un buffer overflow con quella funzione, sta nel mucchio "insicure e inutili" a mio avviso.

Questo è come si potrebbe fare in C++ (da standard 2011 in poi)
Codice sorgente - presumibilmente C++

  1. #include <iostream>
  2. #include <string>
  3. #include <algorithm>
  4.  
  5. void rimuoviDoppie(std::string& s)
  6. {
  7.     auto last = std::unique(s.begin(), s.end(), [](char c1, char c2) {
  8.                 return c1 == c2 && c1 == ' ';
  9.     });
  10.  
  11.     s.erase(last, s.end());
  12. }
  13.  
  14. int main()
  15. {
  16.     std::string s = "Parola  con doppi  spazi";
  17.     rimuoviDoppie(s);
  18.     std::cout << s << std::endl;
  19.     return 0;
  20. }



Tengo aperto il topic per la discussione ma vi prego almeno di rimanere in tema (stringhe), lasciamo i tamponi altrove.

Ultima modifica effettuata da lumo il 23/05/2017 alle 20:23
PM Quote
Avatar
Mikelius (Member)
Expert


Messaggi: 525
Iscritto: 14/04/2017

Segnala al moderatore
Postato alle 20:52
Martedì, 23/05/2017
Testo quotato

Postato originariamente da lumo:

Facilissimo un buffer overflow con quella funzione, sta nel mucchio "insicure e inutili" a mio avviso.

Questo è come si potrebbe fare in C++ (da standard 2011 in poi)




L'avevo scritta 10 anni fa più o meno...
Non voglio essere presuntuoso, ma per capire,
Un esempio pratico di un possibile buffer overflow?

P.s. conosco a malapena il C, il tuo codice in C++ lo capisco a malapena XD
Fermo restando l'utilità o meno (almeno un utilità didattica potrebbe averla) mi piacerebbe vedere un implementazione in C  in modo da orientare le mie prossime (e vecchie) funzioni nella giusta direzione.
(Ad esempio, se avessi chiesto come parametri di input solo source, e allocato dentro la funzione destination, ritornando il puntatore a destination e demandando il compito di deallocare la stringa al chiamante, sarebbe stata più sicura?)

Ultima modifica effettuata da Mikelius il 23/05/2017 alle 21:59
PM Quote
Avatar
AldoBaldo (Member)
Guru


Messaggi: 699
Iscritto: 08/01/2015

Segnala al moderatore
Postato alle 22:56
Martedì, 23/05/2017
Io avrei fatto così:

Codice sorgente - presumibilmente C++

  1. #include <stdio.h>
  2. #include <ctype.h>
  3.  
  4. /*==============================================================================
  5. Elimina i caratteri spaziatori consecutivamente multipli, facendo inoltre in
  6. modo che la stringa modificata non contenga mai un carattere spaziatore nella
  7. sua prima ne' nella sua ultima posizione. Il puntatore alla stringa originale
  8. puo' coincidere con quello alla stringa modificata. Qualora i due puntatori non
  9. coincidano, lo spazio di memoria puntato da modf deve avere dimensioni
  10. prudenzialmente almeno uguali a quello puntato da orig.
  11. Restituisce la dimensione della nuova stringa così modificata. In caso d'errore,
  12. restituisce il valore massimo rappresentabile per mezzo del tipo size_t.
  13. ==============================================================================*/
  14.  
  15. size_t EliminaSpaziatureMultiple( const char *orig, char *modf ) {
  16.     if( orig && modf ) {
  17.         const char *po = orig; // puntatore per scorrere la stringa originale
  18.         char *pm = modf;       // puntatore per scorrere la stringa modificata
  19.         size_t l = 0;          // per la lunghezza della stringa risultante
  20.         int spazio = 0;        // !=0 se si tratta di uno spazio; 0 altrimenti
  21.  
  22.         while( isspace(*po) ) ++po; // eliminiamo gli eventuali spazi iniziali
  23.  
  24.         while( *po ) { // finche' ci sono caratteri nella stringa originale
  25.             spazio = isspace( *po ); // il carattere corrente e' uno spazio?
  26.  
  27.             ++l; // copia comunque, quindi la stringa modificata si "allunga"
  28.             *pm++ = *po++; // spazio o no, copia il carattere corrente
  29.  
  30.             if( spazio ) // se il carattere appena copiato e' uno spazio...
  31.                 while( isspace(*po) ) // ...e fintanto che ci sono spazi...
  32.                     ++po; // ... passa al carattere successivo
  33.         }
  34.  
  35.         if( !spazio ) { // l'ultimo carattere copiato e' uno spazio?
  36.             *pm = '\0'; // se non lo e', "termina" la stringa modificata
  37.         }
  38.         else { // si', l'ultimo carattere e' uno spazio: eliminiamolo!
  39.             --l; // la stringa viene accorciata di un carattere (lo spazio)
  40.             *(pm-1) = '\0'; // il terminatore viene posto un carattere prima
  41.         }
  42.  
  43.         return l; // restituisce la lunghezza della stringa modificata
  44.     } else return -1; // essendo size_t senza segno, questo significa
  45.                       // restituire il valore massimo possibile per size_t
  46. }
  47.  
  48. int main( void ) {
  49.     char src[50] = "Ciao    Mondo    Bello    ";
  50.     char dest[50] = "";
  51.     size_t dimN = 0;
  52.  
  53.     printf( "\n**** STRINGHE INIZIALI ****\n\n" );
  54.     printf( "Stringa Iniziale =[%s]\n", src );
  55.     printf( "Stringa Finale   =[%s]\n", dest );
  56.  
  57.     dimN = EliminaSpaziatureMultiple( src, dest );
  58.  
  59.     printf( "\n\n\n\n**** STRINGHE FINALI ****\n\n" );
  60.     printf( "Stringa Iniziale =[%s]\n", src );
  61.     printf( "Stringa Finale   =[%s]\n", dest );
  62.     printf( "Lunghezza della stringa finale: %u\n", dimN );
  63.  
  64.     return 0;
  65. }


Ultima modifica effettuata da AldoBaldo il 23/05/2017 alle 23:29


ATTENZIONE! Sono un hobbista e l'affidabilità delle mie conoscenze informatiche è molto limitata. Non prendere come esempio il codice che scrivo, perché non ho alcuna formazione accademica e rischieresti di apprendere pratiche controproducenti.
PM Quote
Avatar
Mikelius (Member)
Expert


Messaggi: 525
Iscritto: 14/04/2017

Segnala al moderatore
Postato alle 0:10
Mercoledì, 24/05/2017
Testo quotato

Postato originariamente da AldoBaldo:

Io avrei fatto così:

{...}




Mi piace. Comunque, come nel mio bisogna stare attenti alla memoria allocata per *modf, se nel main() si passa un Array di 2 char, ad esempio, si genera un errore.
Io prima per l'esempio di Buffer Overflow, mi riferivo al fatto di passare dei parametri "giusti" facendo ora delle prove mi accorgo che non è bene fidarsi del main() .

PM Quote
Avatar
lumo (Member)
Expert


Messaggi: 449
Iscritto: 18/04/2010

Segnala al moderatore
Postato alle 16:41
Mercoledì, 24/05/2017
Testo quotato

Io prima per l'esempio di Buffer Overflow, mi riferivo al fatto di passare dei parametri "giusti" facendo ora delle prove mi accorgo che non è bene fidarsi del main() .


Sono stato un po' brusco con "insicuro e inutile", comunque è proprio quello di cui parlo. Riscrivere la funzione non serve a molto se poi ha la stessa interfaccia insicura.

Consiglio due alternative
Codice sorgente - presumibilmente Plain Text

  1. // ritorna la stringa senza spazi doppi, il chiamante deve occuparsi di liberare la memoria allocata dinamicamente
  2. char* eliminaDoppiSpazi(const char* s);



oppure
Codice sorgente - presumibilmente C/C++

  1. // ritorna la lunghezza della stringa modificata per convenienza
  2. int eliminaDoppiSpazi(char* s);


Siccome la stringa dopo l'elaborazione non può essere più lunga di quella originale, si può usare la stessa memoria (che sicuramente è sufficiente), modificando però la stringa originale.
È un caso particolare permesso da questo algoritmo, se la lunghezza potesse aumentare non si potrebbe fare.
È anche lo stesso metodo che uso io con std::unique in C++

Una interfaccia un po' più sicura per la tua potrebbe essere questa in altri casi
Codice sorgente - presumibilmente C/C++

  1. // ritorna la lunghezza di dest
  2. int eliminaDoppiSpazi(const char* s1, char* dest, size_t maxDestLength);


Così si rifà un po' a strcat -> strncat, ma in questo caso non ha molto senso.

PM Quote
Pagine: [ 1 2 3 4 ] Precedente | Prossimo