/**=============================================================================
NUMERI E STRINGHE
v1.0 - aprile 2017
Gestisce valori compresi tra 9223372036854775807 e -9223372036854775807 (interi
a 64 bit) con base compresa tra 2 e 36.
=============================================================================**/
#include "numeri_e_stringhe.h"
/** ===> COSTANTI <========================================================= **/
static const char *NS_Cifre = "0123456789abcdefghijklmnopqrstuvwxyz";
static const char NS_SegnoNegativo = '-';
static const char *NS_StrErrBaseNonValida = "(la base eccede i limiti)";
/** ===> VARIABILI GLOBALI PER QUESTO FILE <================================ **/
bool NS_usa_maiuscole = false;
/** ===> DEFINIZIONE DELLE FUNZIONI <======================================= **/
/*==============================================================================
Chiede l'immissione in console di un numero intero compreso tra min e max, dove
min non puo' essere inferiore a NS_MINVAL e max non puo' essere superiore a
NS_MAXVAL. Restituisce il numero immesso come valore di ritorno di tipo int64_t.
La funzione insiste nella sua richiesta fintanto che non viene immesso un dato
accettabile, quindi ritorna.
Per usare questa funzione occorre che sia definita la macro NS_USACHIEDINUMERO.
==============================================================================*/
#ifdef NS_USACHIEDINUMERO
int64_t NS_chiedi_numero( int64_t min, int64_t max, int8_t base ) {
char buff[NS_DIMBUFFNUM]; /* 64 cifre + il segno negativo */
bool ripeti, pp = true; /* pp = primo passaggio */
int16_t l, qCifre;
int64_t n;
do {
if( !pp ) /* se non e' il primo passaggio... */
printf( "Input non valido, riprova: " );
else pp = false; /* se e' il primo passaggio */
fgets( buff, NS_DIMBUFFNUM, stdin ); /* riceviamo l'input */
for( l=0; buff[l]; ++l ); /* la lunghezza dell'input */
if( buff[l-1] == '\n' ) {
n = NS_sn( buff, &qCifre, base ); /* converte in int64_t */
ripeti = n<min || n>max || qCifre==0;
}
else {
/* se l'ultimo carattere non e' '\n' significa che
l'input e' stato troncato, il che non va bene;
eliminiamo i caratteri ancora "pendenti" */
while( getchar() != '\n' );
ripeti = true; /* la condizione per ripetere */
}
} while( ripeti );
return n; /* restituisce il numero immesso */
}
#endif // NS_USACHIEDINUMERO
/*==============================================================================
Converte una stringa nel numero intero corrispondente, secondo la base di
numerazione specificata.
Se la stringa inizia con caratteri non idonei a rappresentare le cifre del
sistema di numerazione specificato, quei caratteri non vengono considerati.
Se la stringa contiene una rappresentazione numerica valida, la funzione
restituisce il valore ricavato dalla stringa (in formato int64_t) e immette la
quantita' delle cifre riconosciute all'indirizzo indicato dal parametro qCifre.
Se la stringa NON contiene una rappresentazione numerica valida, la funzione
restituisce 0 e immette 0 anche all'indirizzo indicato dal parametro qCifre.
Se il parametro qCifre e' un puntatore NULL, la funzione non puo' fornire
informazioni circa la quantita' di cifre riconosciute, rendendo di fatto
impossibile al chiamante sapere se la conversione e' stata portata a termine
correttamente oppure no.
==============================================================================*/
int64_t NS_sn( const char *s, int16_t *qCifre, int8_t base ) {
int16_t totCifre = 0; /* si presuppone il fallimento */
int64_t r = 0; /* il risultato dell'operazione */
/* se i parametri sono accettabili, analizziamo la stringa */
if( s != NULL && (base>=NS_MINBASE&&base<=NS_MAXBASE) ) {
const char *p = s; /* puntatore ausiliario */
bool negativo;
/* scartiamo tutti i caratteri non numerici iniziali */
while( !(NS_cifra(*p,base)>=0||*p==NS_SegnoNegativo) && *p!='\0' ) ++p;
if( *p != '\0' ) { /* se sono rimasti caratteri */
int16_t i, j; /* contatori */
if( (negativo=*p==NS_SegnoNegativo) ) ++p; /* saltiamo il segno */
while( *p == '0' ) ++p; /* saltiamo gli zeri iniziali */
while( NS_cifra(p[totCifre],base) >= 0 ) /* contiamo le cifre */
++totCifre;
if( totCifre > 0 ) { /* se ci sono cifre valide, calcoliamo */
for( i=totCifre; i>0; --i ) { /* partendo dal fondo */
int64_t tmp=1;
for( j=0; j<totCifre-i; ++j ) tmp *= base; /* potenza */
r += NS_cifra(p[i-1],base) * tmp;
}
if( negativo ) r = -r; /* se era negativo, ripristiniamo */
}
}
}
if( qCifre != NULL ) *qCifre = totCifre; /* solo se non e' NULL */
return r;
}
/*==============================================================================
Converte un numero intero in formato int64_t nella sua rappresentazione in forma
di stringa, secondo la base di numerazione specificata.
Se il parametro buff e' un puntatore valido, la funzione impiega lo spazio di
memoria puntato per collocarvi la stringa risultante dalla conversione. Spetta
al chiamante assicurarsi che buff punti a uno spazio di memoria sufficiente per
contenere la stringa (il caso piu' "esigente" riguarda il valore a 64 bit
massimo in formato binario: 64 caratteri piu' il terminatore, eventualmente con
l'aggiunta d'un segno negativo -- 66 caratteri in tutto).
Se il parametro buff e' NULL, la funzione inserisce la stringa risultante in un
buffer statico costituito da NS_DIMBUFFNUM char. Ovviamente, ogni nuova chiamata
alla funzione sovrascrive il buffer cancellando l'esito delle eventuali
conversioni precedenti.
La funzione fallisce (restituendo una stringa che descrive l'errore) solo se
il parametro base eccede i limiti consentiti da NS_MINBASE e NS_MAXBASE.
==============================================================================*/
char *NS_ns( int64_t n, char *buff, int8_t base ) {
static char sBuff[NS_DIMBUFFNUM]; /* max 64 cifre + il segno negativo */
uint64_t un; /* necessario per il valore negativo minimo */
int16_t i; /* deve poter contenere almeno la lunghezza della stringa */
/* se il parametro buff e' nullo, usiamo il buffer statico */
if( buff == NULL ) buff = sBuff;
if( base>=NS_MINBASE && base<=NS_MAXBASE ) {
int16_t l, lMezzi;
bool negativo;
int8_t cifra;
negativo = n < 0; /* memorizziamo il segno */
un = negativo ? -n : n; /* usiamo sempre un valore positivo */
/* N.B. la stringa viene generata "all'indietro" */
for( l=0; un!=0; ++l ) {
cifra = un%base;
buff[l] = NS_Cifre[cifra];
un /= base;
}
if( negativo ) buff[l++] = NS_SegnoNegativo; /* apponiamo il segno? */
buff[l] = '\0'; /* terminiamo la stringa */
/* "rovesciamo" la stringa (generata all'indietro) */
for( lMezzi=l/2, i=0; i<lMezzi; ++i ) {
char aux = buff[i];
buff[i] = buff[l-i-1];
buff[l-i-1] = aux;
}
/* se necessario, usiamo le cifre in caratteri maiuscoli */
if( NS_usa_maiuscole )
for( i=0; i<l; ++i )
buff[i] = toupper( buff[i] );
}
else { /* la base eccede i limiti imposti da NS_MINBASE e NS_MAXBASE */
for( i=0; NS_StrErrBaseNonValida[i]; ++i )
buff[i] = NS_StrErrBaseNonValida[i]; /* copiamo l'errore */
buff[i] = '\0'; /* terminiamo la stringa */
}
return buff; /* restituiamo un puntatore al buffer effettivamente usato */
}
/*==============================================================================
Verifica se c corrisponde a una delle cifre valide nel sistema di numerazione
che impiega la base specificata dal parametro base. Se c e' una cifra valida
restituisce il valore della cifra; se c NON e' una cifra valida restituisce
NS_CIFRANONVALIDA.
==============================================================================*/
int8_t NS_cifra( char c, int8_t base ) {
c = tolower(c);
int8_t i;
if( base>=NS_MINBASE && base<=NS_MAXBASE )
for( i=0; i<base; ++i )
if( c == NS_Cifre[i] )
return i;
return NS_CIFRANONVALIDA;
}
/*==============================================================================
Imposta una variabile statica che fa si' che le altre funzioni impieghino (nelle
chiamate successive) caratteri maiuscoli o minuscoli come cifre per quei sistemi
di numerazione che richiedono l'uso di oltre 10 cifre.
==============================================================================*/
void NS_usa_cifre_maiuscole( bool usa_cifre_maiuscole ) {
NS_usa_maiuscole = usa_cifre_maiuscole;
}