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++ - Conversione numero->stringa->numero
Forum - C/C++ - Conversione numero->stringa->numero

Avatar
AldoBaldo (Member)
Guru


Messaggi: 699
Iscritto: 08/01/2015

Segnala al moderatore
Postato alle 0:23
Lunedì, 01/05/2017
Ciao a te che leggi.

Oggi non sapevo che fare e ho messo insieme una paginata di roba in previsione di un futuro ed ipotetico uso personale. Probabilmente si tratta della classica "reinvenzione della ruota", però sapete che son fatto così e che mi diverto a scrivere 'ste cose. Non escludo che si tratti pure di un modo improprio di ottenere il risultato.

Detto questo... potrebbe essere utile aggiungere questi file alla sezione "esempi" dei programmi presenti su Pierotofy? Se sì, aggiungo; se no, tengo per me. Graditi opinioni e, se non significano "rivoluzionare" tutto (non ne vale la pena, credo), suggerimenti su come migliorare il codice. Meno gradito, ma comunque accettato, un eventuale "ma piantala lì!".

Tre file. Il primo: numeri_e_stringhe.h

Codice sorgente - presumibilmente C++

  1. /**=============================================================================
  2.                                NUMERI E STRINGHE
  3.                                v1.0 - aprile 2017
  4.  
  5. Gestisce valori compresi tra 9223372036854775807 e -9223372036854775807 (interi
  6. a 64 bit) con base compresa tra 2 e 36.
  7. =============================================================================**/
  8.  
  9. #ifndef NUMERI_E_STRINGHE_H_INCLUDED
  10. #define NUMERI_E_STRINGHE_H_INCLUDED
  11.  
  12. /** ===> INCLUSIONI <========================================================**/
  13.  
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <stdint.h>  /* per usare i tipi uint64_t, int64_t, int16_t e int8_t */
  17. #include <stdbool.h> /* per usare il tipo bool */
  18. #include <ctype.h>   /* per usare tolower() e toupper() */
  19.  
  20. /** ===> MACRO <============================================================ **/
  21.  
  22. #define NS_USACHIEDINUMERO    /* da definire per usare NS_chiedi_numero() */
  23. #define NS_CIFRANONVALIDA  -1 /* restituito come errore da NS_cifra() */
  24. #define NS_MINBASE          2 /* la base non puo' essere inferiore */
  25. #define NS_MAXBASE         36 /* la base non puo' essere superiore */
  26. #define NS_MINVAL          -9223372036854775807LL
  27. #define NS_MAXVAL           9223372036854775807LL
  28. #define NS_DIMBUFFNUM      68 /* le dimensioni del buffer per la
  29.                                  rappresentazione dei numeri a 64 bit
  30.                                  sotto forma di stringa */
  31.  
  32. /** ===> PROTOTIPI DELLE FUNZIONI <========================================= **/
  33.  
  34. #ifdef NS_USACHIEDINUMERO
  35. int64_t NS_chiedi_numero( int64_t minimo, int64_t massimo, int8_t base );
  36. #endif // NS_USACHIEDINUMERO
  37.  
  38. int64_t NS_sn( const char *s, int16_t *qCifre, int8_t base );
  39. char *NS_ns( int64_t n, char *buff, int8_t base );
  40. int8_t NS_cifra( char c, int8_t base );
  41. void NS_usa_cifre_maiuscole( bool usa_cifre_maiuscole );
  42.  
  43. #endif /* NUMERI_E_STRINGHE_H_INCLUDED */



Il secondo: numeri_e_stringhe.c

Codice sorgente - presumibilmente C#

  1. /**=============================================================================
  2.                                NUMERI E STRINGHE
  3.                                v1.0 - aprile 2017
  4.  
  5. Gestisce valori compresi tra 9223372036854775807 e -9223372036854775807 (interi
  6. a 64 bit) con base compresa tra 2 e 36.
  7. =============================================================================**/
  8.  
  9. #include "numeri_e_stringhe.h"
  10.  
  11. /** ===> COSTANTI <========================================================= **/
  12.  
  13. static const char *NS_Cifre = "0123456789abcdefghijklmnopqrstuvwxyz";
  14. static const char NS_SegnoNegativo = '-';
  15. static const char *NS_StrErrBaseNonValida = "(la base eccede i limiti)";
  16.  
  17. /** ===> VARIABILI GLOBALI PER QUESTO FILE <================================ **/
  18.  
  19. bool NS_usa_maiuscole = false;
  20.  
  21. /** ===> DEFINIZIONE DELLE FUNZIONI <======================================= **/
  22.  
  23. /*==============================================================================
  24. Chiede l'immissione in console di un numero intero compreso tra min e max, dove
  25. min non puo' essere inferiore a NS_MINVAL e max non puo' essere superiore a
  26. NS_MAXVAL. Restituisce il numero immesso come valore di ritorno di tipo int64_t.
  27. La funzione insiste nella sua richiesta fintanto che non viene immesso un dato
  28. accettabile, quindi ritorna.
  29. Per usare questa funzione occorre che sia definita la macro NS_USACHIEDINUMERO.
  30. ==============================================================================*/
  31.  
  32. #ifdef NS_USACHIEDINUMERO
  33. int64_t NS_chiedi_numero( int64_t min, int64_t max, int8_t base ) {
  34.     char buff[NS_DIMBUFFNUM]; /* 64 cifre + il segno negativo */
  35.     bool ripeti, pp = true; /* pp = primo passaggio */
  36.     int16_t l, qCifre;
  37.     int64_t n;
  38.  
  39.     do {
  40.         if( !pp ) /* se non e' il primo passaggio... */
  41.             printf( "Input non valido, riprova: " );
  42.         else pp = false; /* se e' il primo passaggio */
  43.  
  44.         fgets( buff, NS_DIMBUFFNUM, stdin ); /* riceviamo l'input */
  45.         for( l=0; buff[l]; ++l ); /* la lunghezza dell'input */
  46.  
  47.         if( buff[l-1] == '\n' ) {
  48.             n = NS_sn( buff, &qCifre, base ); /* converte in int64_t */
  49.             ripeti = n<min || n>max || qCifre==0;
  50.         }
  51.         else {
  52.             /* se l'ultimo carattere non e' '\n' significa che
  53.                l'input e' stato troncato, il che non va bene;
  54.                eliminiamo i caratteri ancora "pendenti" */
  55.             while( getchar() != '\n' );
  56.             ripeti = true; /* la condizione per ripetere */
  57.         }
  58.     } while( ripeti );
  59.  
  60.     return n; /* restituisce il numero immesso */
  61. }
  62. #endif // NS_USACHIEDINUMERO
  63.  
  64. /*==============================================================================
  65. Converte una stringa nel numero intero corrispondente, secondo la base di
  66. numerazione specificata.
  67. Se la stringa inizia con caratteri non idonei a rappresentare le cifre del
  68. sistema di numerazione specificato, quei caratteri non vengono considerati.
  69. Se la stringa contiene una rappresentazione numerica valida, la funzione
  70. restituisce il valore ricavato dalla stringa (in formato int64_t) e immette la
  71. quantita' delle cifre riconosciute all'indirizzo indicato dal parametro qCifre.
  72. Se la stringa NON contiene una rappresentazione numerica valida, la funzione
  73. restituisce 0 e immette 0 anche all'indirizzo indicato dal parametro qCifre.
  74. Se il parametro qCifre e' un puntatore NULL, la funzione non puo' fornire
  75. informazioni circa la quantita' di cifre riconosciute, rendendo di fatto
  76. impossibile al chiamante sapere se la conversione e' stata portata a termine
  77. correttamente oppure no.
  78. ==============================================================================*/
  79.  
  80. int64_t NS_sn( const char *s, int16_t *qCifre, int8_t base ) {
  81.     int16_t totCifre = 0; /* si presuppone il fallimento */
  82.     int64_t r = 0; /* il risultato dell'operazione */
  83.  
  84.     /* se i parametri sono accettabili, analizziamo la stringa */
  85.     if( s != NULL && (base>=NS_MINBASE&&base<=NS_MAXBASE) ) {
  86.         const char *p = s; /* puntatore ausiliario */
  87.         bool negativo;
  88.  
  89.         /* scartiamo tutti i caratteri non numerici iniziali */
  90.         while( !(NS_cifra(*p,base)>=0||*p==NS_SegnoNegativo) && *p!='\0' ) ++p;
  91.  
  92.         if( *p != '\0' ) { /* se sono rimasti caratteri */
  93.             int16_t i, j; /* contatori */
  94.  
  95.             if( (negativo=*p==NS_SegnoNegativo) ) ++p; /* saltiamo il segno */
  96.             while( *p == '0' ) ++p; /* saltiamo gli zeri iniziali */
  97.  
  98.             while( NS_cifra(p[totCifre],base) >= 0 ) /* contiamo le cifre */
  99.                 ++totCifre;
  100.  
  101.             if( totCifre > 0 ) { /* se ci sono cifre valide, calcoliamo */
  102.                 for( i=totCifre; i>0; --i ) { /* partendo dal fondo */
  103.                     int64_t tmp=1;
  104.                     for( j=0; j<totCifre-i; ++j ) tmp *= base; /* potenza */
  105.                     r += NS_cifra(p[i-1],base) * tmp;
  106.                 }
  107.  
  108.                 if( negativo ) r = -r; /* se era negativo, ripristiniamo */
  109.             }
  110.         }
  111.     }
  112.  
  113.     if( qCifre != NULL ) *qCifre = totCifre; /* solo se non e' NULL */
  114.  
  115.     return r;
  116. }
  117.  
  118. /*==============================================================================
  119. Converte un numero intero in formato int64_t nella sua rappresentazione in forma
  120. di stringa, secondo la base di numerazione specificata.
  121. Se il parametro buff e' un puntatore valido, la funzione impiega lo spazio di
  122. memoria puntato per collocarvi la stringa risultante dalla conversione. Spetta
  123. al chiamante assicurarsi che buff punti a uno spazio di memoria sufficiente per
  124. contenere la stringa (il caso piu' "esigente" riguarda il valore a 64 bit
  125. massimo in formato binario: 64 caratteri piu' il terminatore, eventualmente con
  126. l'aggiunta d'un segno negativo -- 66 caratteri in tutto).
  127. Se il parametro buff e' NULL, la funzione inserisce la stringa risultante in un
  128. buffer statico costituito da NS_DIMBUFFNUM char. Ovviamente, ogni nuova chiamata
  129. alla funzione sovrascrive il buffer cancellando l'esito delle eventuali
  130. conversioni precedenti.
  131. La funzione fallisce (restituendo una stringa che descrive l'errore) solo se
  132. il parametro base eccede i limiti consentiti da NS_MINBASE e NS_MAXBASE.
  133. ==============================================================================*/
  134.  
  135. char *NS_ns( int64_t n, char *buff, int8_t base ) {
  136.     static char sBuff[NS_DIMBUFFNUM]; /* max 64 cifre + il segno negativo */
  137.     uint64_t un; /* necessario per il valore negativo minimo */
  138.     int16_t i; /* deve poter contenere almeno la lunghezza della stringa */
  139.  
  140.     /* se il parametro buff e' nullo, usiamo il buffer statico */
  141.     if( buff == NULL ) buff = sBuff;
  142.  
  143.     if( base>=NS_MINBASE && base<=NS_MAXBASE ) {
  144.         int16_t l, lMezzi;
  145.         bool negativo;
  146.         int8_t cifra;
  147.  
  148.         negativo = n < 0; /* memorizziamo il segno */
  149.         un = negativo ? -n : n; /* usiamo sempre un valore positivo */
  150.  
  151.         /* N.B. la stringa viene generata "all'indietro" */
  152.         for( l=0; un!=0; ++l ) {
  153.             cifra = un%base;
  154.             buff[l] = NS_Cifre[cifra];
  155.             un /= base;
  156.         }
  157.  
  158.         if( negativo ) buff[l++] = NS_SegnoNegativo; /* apponiamo il segno? */
  159.         buff[l] = '\0'; /* terminiamo la stringa */
  160.  
  161.         /* "rovesciamo" la stringa (generata all'indietro) */
  162.         for( lMezzi=l/2, i=0; i<lMezzi; ++i ) {
  163.             char aux = buff[i];
  164.             buff[i] = buff[l-i-1];
  165.             buff[l-i-1] = aux;
  166.         }
  167.  
  168.         /* se necessario, usiamo le cifre in caratteri maiuscoli */
  169.         if( NS_usa_maiuscole )
  170.             for( i=0; i<l; ++i )
  171.                 buff[i] = toupper( buff[i] );
  172.     }
  173.     else { /* la base eccede i limiti imposti da NS_MINBASE e NS_MAXBASE */
  174.         for( i=0; NS_StrErrBaseNonValida[i]; ++i )
  175.             buff[i] = NS_StrErrBaseNonValida[i]; /* copiamo l'errore */
  176.         buff[i] = '\0'; /* terminiamo la stringa */
  177.     }
  178.  
  179.     return buff; /* restituiamo un puntatore al buffer effettivamente usato */
  180. }
  181.  
  182. /*==============================================================================
  183. Verifica se c corrisponde a una delle cifre valide nel sistema di numerazione
  184. che impiega la base specificata dal parametro base. Se c e' una cifra valida
  185. restituisce il valore della cifra; se c NON e' una cifra valida restituisce
  186. NS_CIFRANONVALIDA.
  187. ==============================================================================*/
  188.  
  189. int8_t NS_cifra( char c, int8_t base ) {
  190.     c = tolower(c);
  191.     int8_t i;
  192.  
  193.     if( base>=NS_MINBASE && base<=NS_MAXBASE )
  194.         for( i=0; i<base; ++i )
  195.             if( c == NS_Cifre[i] )
  196.                 return i;
  197.     return NS_CIFRANONVALIDA;
  198. }
  199.  
  200. /*==============================================================================
  201. Imposta una variabile statica che fa si' che le altre funzioni impieghino (nelle
  202. chiamate successive) caratteri maiuscoli o minuscoli come cifre per quei sistemi
  203. di numerazione che richiedono l'uso di oltre 10 cifre.
  204. ==============================================================================*/
  205.  
  206. void NS_usa_cifre_maiuscole( bool usa_cifre_maiuscole ) {
  207.     NS_usa_maiuscole = usa_cifre_maiuscole;
  208. }



Il terzo, con un programmino d'esempio d'uso: main.c

Codice sorgente - presumibilmente C#

  1. /**=============================================================================
  2. Programma d'esempio per numeri_e_stringhe.
  3. =============================================================================**/
  4.  
  5. #include <string.h>
  6. #include "numeri_e_stringhe.h"
  7.  
  8. int main() {
  9.     char buff[NS_DIMBUFFNUM];
  10.     int8_t b1, b2;
  11.     int64_t n;
  12.     bool ripeti;
  13.  
  14.     printf( "CONVERTE LA BASE DI NUMERAZIONE DI UN NUMERO DATO\n" );
  15.     printf( "=================================================\n\n" );
  16.  
  17.     do {
  18.         printf( "Immetti la base di numerazione iniziale (%d-%d): ",
  19.                 NS_MINBASE, NS_MAXBASE );
  20.         b1 = NS_chiedi_numero( NS_MINBASE, NS_MAXBASE, 10 );
  21.  
  22.         printf( "Immetti la base di numerazione di destinazione (%d-%d): ",
  23.                 NS_MINBASE, NS_MAXBASE );
  24.         b2 = NS_chiedi_numero( NS_MINBASE, NS_MAXBASE, 10 );
  25.  
  26.         printf( "Immetti il numero da convertire: " );
  27.         n = NS_chiedi_numero( NS_MINVAL, NS_MAXVAL, b1 );
  28.  
  29.         printf( "\n    base %2d: %s\n", b1, NS_ns(n,buff,b1) );
  30.         printf( "    base %2d: %s\n\n", b2, NS_ns(n,buff,b2) );
  31.  
  32.         printf( "Vuoi effettuare un'altra conversione? "
  33.                 "(\"s\" per continuare) " );
  34.  
  35.         fgets( buff, NS_DIMBUFFNUM, stdin );
  36.         ripeti = tolower(*buff) != 'n';
  37.  
  38.         /* se necessario, "svuota" stdin */
  39.         while( buff[strlen(buff)-1] != '\n' )
  40.             fgets( buff, NS_DIMBUFFNUM, stdin );
  41.  
  42.         printf( "\n\n" );
  43.     } while( ripeti );
  44.  
  45.     return 0;
  46. }


Ultima modifica effettuata da AldoBaldo il 01/05/2017 alle 8:42


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
nessuno (Normal User)
Guru^2


Messaggi: 6402
Iscritto: 03/01/2010

Segnala al moderatore
Postato alle 11:32
Lunedì, 01/05/2017
E' ovviamente un "reinventare la ruota" dato che basterebbe scegliere una tra le tante funzioni già disponibili tra queste

strtod, _strtod_l, wcstod, _wcstod_l
strtol, wcstol, _strtol_l, _wcstol_l
strtoul, _strtoul_l, wcstoul, _wcstoul_l
_strtoi64, _wcstoi64, _strtoi64_l, _wcstoi64_l
_strtoui64, _wcstoui64, _strtoui64_l, _wcstoui64_l

e tra queste

sprintf, _sprintf_l, swprintf, _swprintf_l, __swprintf_l

(standard e non standard) che sono ampiamente documentate e (naturalmente) più testate.

Però il tuo lavoro merita apprezzamento e ti direi di migliorarlo aggiungendo, almeno, un paio di caratteristiche

1. gestione degli errori relativi al superamento dei limiti numerici

2. uso esclusivo di funzioni "sicure" per eliminare la possibilità di buffer overflow


Ricorda che nessuno è obbligato a risponderti e che nessuno è perfetto ...
---
Il grande studioso italiano Bruno de Finetti ( uno dei padri fondatori del moderno Calcolo delle probabilità ) chiamava il gioco del Lotto Tassa sulla stupidità.
PM Quote
Avatar
AldoBaldo (Member)
Guru


Messaggi: 699
Iscritto: 08/01/2015

Segnala al moderatore
Postato alle 12:37
Lunedì, 01/05/2017
Grazie, nessuno, mi hai indotto a fare qualche "ricerchina" alla quale non avrei pensato da solo (ad esempio su https://www.techonthenet.com/c_language/index.php), così che ho potuto scoprire che quel che sospettavo è vero: quel tipo di operazioni sono già svolte da funzioni consolidate e, meglio ancora, addirittura standard. Più che le funzioni che mi hai indicato, molte delle quali se non ho capito male sono tipiche dell'ambiente windows, mi intriga aver scoperto funzioni tipo strtoll() (che non conoscevo perché sul testo per il C che ho studiato io decenni fa non è citata, c'è solo strtol() e le sue compagne "unsigned" e "double" strtoul() e strtod() ). Altre funzioni sulla stessa linea che ho scoperto sono atoll(), strtold() e strtoull(). Ovviamente tutte quante includono i meccanismi per segnalare gli errori di "sforamento" che citi, documentati in dettaglio.

Se non ho capito male, si tratta di funzioni disponibili a partire dal C 90 (non è che io sia particolarmente pratico dell'evoluzione del C attraverso i secoli, però ho visto che in Code::Blocks c'è un'opzione per segnalare al compilatore la volontà di usare 'sto C 90).

Che ti devo dire, mi hai fatto scoprire cose utilissime che impiegherò senz'altro. Nello stesso tempo mi hai procurato una bella doccina di quelle gelate tirandomi coi piedi per terra :blush:. Ritengo di non aver comunque buttato via il mio tempo, se non altro baloccandomi ho fatto un po' d'esercizio e (importantissimo per me) MI SONO DIVERTITO. Sperimenterò.

Abbandono l'idea di inserire il codice tra i programmi di pierotofy.it, sarebbe una cosa vacua.

Ultima modifica effettuata da AldoBaldo il 01/05/2017 alle 12:38


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