AS
www.asinformatica.net
consulenza e sviluppo software
Il codice fiscale degli italiani (per le persone fisiche)
Verificare un codice fiscale

Dal punto di vista dello sviluppatore di software, la verifica può essere pensata su diversi livelli:

  • il codice è correttamente formato e rispetta le regole di costruzione?
  • il codice è coerente con gli altri dati anagrafici forniti?
  • il codice appartiene effettivamente alla persona di cui ci sono forniti gli altri dati anagrafici?

Per le ragioni discusse in altre parti di questo sito, soltanto alla prima domanda è possibile dare una risposta certa. Alla seconda, se è pur vero che è possibile rilevare alcune manifeste incongruenze (si pensi a cognome, nome, data di nascita, sesso), non è possibile dare con certezza una risposta positiva. Per quanto riguarda la terza, il solo modo certo e corretto di verificare l’esistenza di un codice fiscale e la sua corrispondenza con i dati anagrafici della persona, in mancanza di documento, è consultare una fonte autentica quale questa pagina del sito dell’Agenzia delle Entrate.

Analizziamo qui il primo livello di verifica, l'esame cioè della corretta costruzione del codice sul piano della struttura formale intrinseca, prescindendo dal confronto con altri dati:

  • corretta lunghezza;
  • corretta posizione di lettere e cifre;
  • appartenenza dei dati indicanti sesso e la data di nascita all’insieme dei valori ammissibili;
  • corretto carattere di controllo finale.

Verifica

Inserire qui il codice fiscale da verificare. Inserire un punto interrogativo in luogo dell'ultimo carattere se si desidera calcolarlo.


Appunti di programmazione

Il codice di verifica del codice fiscale è stato scritto in Dart, moderno e potente linguaggio creato da Google, che affianca ad una sintassi familiare ai programmatori Java o C# la prerogativa di generare codice Javascript; le applicazioni così realizzate non richiedono quindi configurazioni particolari del server web, e, essendo ospitate dal browser come qualsiasi altro codice Javascript, sono efficienti e veloci.

Nulla di transcendentale ovviamente, ma può essere di interesse, per questo lo includo.

Il codice consta di una funzione generale che esegue le operazioni di verifica e di una classe che contiene gli elementi costitutivi del codice fiscale e le funzionalità di verifica.

Queste sono le impostazioni iniziali e il codice eseguito all'avvio:

import 'dart:html';

void main() {
  impostaElementi();
}

// imposta la gestione della pagina HTML: crea i riferimenti agli elementi
// della pagina web e predispone la cattura dell'evento di avvio della verifica
void impostaElementi() {
  var btnEsegui = querySelector("#btnVerificaCF");
  btnEsegui.onClick.listen((MouseEvent e) {
     var txtCodice = querySelector("#txtCF");
     eseguiControllo(txtCodice.value, "#esitoVerificaCF");
  });
}
            

Si impostano qui i riferimenti agli elementi nella pagina web tramite la funzione querySelector(). L'evento associato al pulsante di avvio della verifica avvia le operazioni di controllo associandovi il nome dell'elemento destinato ad accoglierne l'esito.

Questa è la routine principale che esegue la verifica:

// esegue le operazioni di verifica
void eseguiControllo(String codice, String output) {
  bool ok = true;
  String messaggio = "";
  
  CF cf = new CF(codice);
  if (!cf.controllaLunghezza()) {
    messaggio = "Il codice fiscale non è stato indicato o ha una lunghezza errata";
    ok = false;
    // verifica della lunghezza del codice
  } else {
    cf.invertiCorrezioneOmocodia();
    // eliminazione delle eventuali correzioni per omocodia
  }
  // controlli preliminari
  if (ok) {
    if (!cf.verificaCognomeNome()) {
      messaggio = "L'indicazione di cognome e nome (primi 6 caratteri) non è corretta";
      ok = false;
    }
  }
  // verifica di cognome e nome
  if (ok) {
    if (!cf.verificaDataNascitaSesso()) {
      messaggio = "L'indicazione di data di nascita e sesso (caratteri da 7 a 11) non è corretta";
      ok = false;
    }
  }
  // verifica di data di nascita e sesso
  if (ok) {
    if (!cf.verificaCodLuogoNascita()) {
      messaggio = "L'indicazione del codice del comune o stato estero di nascita (caratteri da 12 a 15) non è corretta";
      ok = false;
    }
  }
  // verifica del codice del luogo di nascita
  if (ok) {
    if (!cf.verificaControllo()) {
       messaggio = "L'indicazione del carattere finale di controllo non è corretta";
       ok = false;
     }
  }
  // verifica del carattere di controllo 
  if (ok) {
    StringBuffer stbMessaggio = new StringBuffer("Il codice fiscale del");
    stbMessaggio.write(cf.IsSessoFemminile ? "la signora " : " signor ");
    stbMessaggio.write(cf.Nome);
    stbMessaggio.write(" ");
    stbMessaggio.write(cf.Cognome);
    stbMessaggio.write(", nat");
    stbMessaggio.write(cf.IsSessoFemminile ? "a" : "o");
    stbMessaggio.write(" a (o in) ");
    stbMessaggio.write(cf.CodLuogoNascita);
    stbMessaggio.write(" il ");
    stbMessaggio.write(cf.DataNascita);
    stbMessaggio.write(", è formalmente corretto");
    if (cf.IsControlloMancante) {
      stbMessaggio.write(" - Il carattere di controllo finale è ");
      stbMessaggio.write(cf.Controllo);
    }
    messaggio = stbMessaggio.toString();
  }
  //print(messaggio);
  var outputEsito = querySelector(output);
  outputEsito.text = messaggio;
  // esito finale
}
            

Essa definisce un'istanza della classe CF passando la stringa contenente il codice fiscale indicato dall'utente. Le operazioni di controllo sono eseguite in cascata, passando alla successiva solo se la precedente ha avuto buon esito. Al termine è comunicato il risultato.

La classe CF:

// classe per l'analisi del codice fiscale
class CF {
  String _codice;
  String _codiceOrig;
  String Cognome;
  String CodLuogoNascita;
  String Controllo;
  String DataNascita;
  bool IsControlloMancante = false;
  bool IsSessoFemminile = false;
  String Nome;
  // (nota: i membri che iniziano con _ sono automaticamente privati in Dart) 
            

I membri pubblici sono utilizzati dalla routine di verifica nella comunicazione dell'esito.

Il codice fiscale indicato dall'utente è passato nel costruttore della classe:

  CF(String cf) {
    _codice = cf.toUpperCase().trim();
    _codiceOrig = _codice;
  }
             

si eliminano eventuali spazi, si converte il codice in maiuscolo e lo si registra in due variabili: una è destinata ad ospitare il codice depurato delle correzioni per omocodia.

La prima verifica riguarda la lunghezza:

  // controllo della lunghezza
  bool controllaLunghezza() {
    return (_codice.length == 16);
  }
             
Qualora siano state apportate correzioni per omocodia, si ricostruisce il codice con i dati di partenza:

  // riporta il codice passato allo stato precedente
  //  la correzione per omocodia
  bool invertiCorrezioneOmocodia() {
    StringBuffer stbModificati = new StringBuffer();
    stbModificati.write(_codice[6]);
    stbModificati.write(_codice[7]);
    stbModificati.write(_codice[9]);
    stbModificati.write(_codice[10]);
    stbModificati.write(_codice[12]);
    stbModificati.write(_codice[13]);
    stbModificati.write(_codice[14]);
    String modificati = stbModificati.toString();
    // si crea una stringa con i soli caratteri numerici del codice,
    // quelli cioè suscettibili di essere stati sostituiti con caratteri
    // alfabetici secondo la regola prescritta in caso di omocodia
    modificati = modificati
        .replaceAll('L', '0')
        .replaceAll('M', '1')
        .replaceAll('N', '2')
        .replaceAll('P', '3')
        .replaceAll('Q', '4')
        .replaceAll('R', '5')
        .replaceAll('S', '6')
        .replaceAll('T', '7')
        .replaceAll('U', '8')
        .replaceAll('V', '9');
    // si applica a tutti i caratteri della stringa così ottenuta
    // la correzione inversa, si sostituiscono cioè i caratteri alfabetici
    // con i caratteri numerici originari
    StringBuffer stbCF = new StringBuffer();
    stbCF.write(_codice.substring(0,6));
    stbCF.write(modificati.substring(0,2));
    stbCF.write(_codice[8]);
    stbCF.write(modificati.substring(2,4));
    stbCF.write(_codice[11]);
    stbCF.write(modificati.substring(4));
    stbCF.write(_codice[15]);
    _codice = stbCF.toString();
    // si ricostruisce il codice includendovi le eventuali correzioni
    return (_codice != _codiceOrig);
    // true se sono state necessarie correzioni
  }
           
Per le operazioni di verifica è necessario distinguere tra caratteri alfabetici e numerici:

  // true se il (primo) carattere il passato è alfabetico
  // (nota: assume che la stringa passata non sia vuota)
  bool isAlfabetico(String valore) {
    int aCodeUnit = valore.codeUnitAt(0);
    return (aCodeUnit >= 65 && aCodeUnit <= 90);
  }

  // true se il (primo) carattere passato è numerico
  // (nota: assume che la stringa passata non sia vuota)
  bool isNumerico(String valore) {
    int aCodeUnit = valore.codeUnitAt(0);
    return (aCodeUnit >= 48 && aCodeUnit <= 57);
  }
            

In queste funzioni ausiliarie e nel resto del codice ho preferito il riferimento ai valori ASCII dei caratteri all'uso delle meno leggibili espressioni regolari.

Del cognome e nome è verificato solo il tipo di carattere:

  bool verificaCognomeNome() {
   bool ok;
   for (int i = 0; i <= 5; i++) {
     ok = isAlfabetico(_codice[i]);
     if (!ok) {
       break;
     }
     else {
       Cognome = _codice.substring(0, 3);
       Nome = _codice.substring(3, 6);
     }
    }
    return ok;
  }
             
Verifica dell'indicazione di data di nascita e sesso:

  // verifica la correttezza formale dei caratteri
  // rappresentanti data di nascita e sesso
  bool verificaDataNascitaSesso() {
    bool ok = true;
    int giorno;
    int mese;
    int anno;
    
    for (int i = 6; i <= 10; i++) {
      if (i == 8) {
        ok = isAlfabetico(_codice[i]);
      } else {
        ok = isNumerico(_codice[i]);
      }
      if (!ok) {
        break;
      }
    }
    // verifica il corretto tipo dei caratteri
    if (ok) {
      giorno = int.parse(_codice.substring(9,11));
      if (giorno > 40) {
        IsSessoFemminile = true;
        giorno -= 40;
      }
      ok = (giorno >= 1);
    }
    // verifica del valore minimo ammissibile del giorno di nascita
    // e rilevazione del sesso
    if (ok) {
      mese = " ABCDEHLMPRST".indexOf(_codice[8], 1);
      ok = (mese > 0);
    }
    // verifica della corretta indicazione del mese di nascita
    // (corrisponde numericamente all'indice della lettera nella stringa)
    if (ok) {
      anno = int.parse(_codice.substring(6,8));
      int ultimoGiornoMese = 31;
      if (mese == 4 || mese == 6 || mese == 9 || mese == 11) {
        ultimoGiornoMese = 30;
      } else if (mese == 2) {
        if (anno % 4 == 0) {
          ultimoGiornoMese = 29;
        } else {
          ultimoGiornoMese = 28;
        }
      }
      ok = giorno <= ultimoGiornoMese;
    }
    // verifica del valore massimo del giorno del mese
    // in funzione del mese e dell'eventuale anno bisestile
    if (ok) {
      StringBuffer stbData = new StringBuffer();
      stbData.write(giorno.toString());
      stbData.write('/');
      stbData.write(mese.toString());
      stbData.write('/');
      stbData.write(anno.toString().padLeft(2, '0'));
      DataNascita = stbData.toString();
    }
    return ok;
  }
             

La verifica è un po' più accurata: si controlla che la lettera indicante il mese e i numeri indicanti il giorno di nascita rientrino nell'ambito dei valori ammissibili.

Anche per il codice del comune o stato estero di nascita è verificato solo il tipo di carattere:

  // verifica la correttezza formale dei caratteri
  // rappresentanti il codice del luogo di nascita
  bool verificaCodLuogoNascita() {
    bool ok = true;
    ok = isAlfabetico(_codice[11]);
    if (ok) {
      for (int i = 12; i <= 14; i++) {
        ok = isNumerico(_codice[i]);
        if (!ok) {
          break;
        }
      }
    }
    if (ok) {
      CodLuogoNascita = _codice.substring(11, 15);
    }
    return ok;
  }
            
Verifica (o calcolo se richiesto) del carattere di controllo:

  // verifica ed eventualmente imposta, se mancante, il carattere di controllo finale
  // nota: la verifica va eseguita tenendo conto che il carattere di controllo è calcolato
  // sul codice corretto per l'omocodia
  bool verificaControllo() {
    bool ok = true;
    int somma = 0;
    {
      for (int i = 0; i <= 14; i += 2) {
        somma += calcolaValorePerCarattereControllo(_codiceOrig[i], true);
      }
    }
    {
      for (int i = 1; i <= 13; i += 2) {
        somma += calcolaValorePerCarattereControllo(_codiceOrig[i], false);
      }
    }
    // somma i valori associati ai caratteri
    // in funzione del tipo e della posizione
    int indiceControllo = somma % 26;
    String controllo = new String.fromCharCode(indiceControllo + 65);
    if (_codiceOrig[15] == "?") {
      Controllo = controllo;
      IsControlloMancante = true;
    } else {
      ok = (_codiceOrig[15] == controllo);
    }
    // calcola (se richiesto) o verifica il carattere di controllo risultante
    return ok;
  }

  // calcola il valore associato ad un carattere per il calcolo del carattere di controllo
  // sulla base del valore, tipo e posizione
  int calcolaValorePerCarattereControllo(String car, bool dispari) {
    int valore = 0;
    if (isAlfabetico(car)) {
      if (dispari) {
        valore = "BAKPLCQDREVOSFTGUHMINJWZYX".indexOf(car);
      } else {
        valore = car.codeUnitAt(0) - 65;
      }
    }
    else {
      if (dispari) {
        valore = "10   2 3 4   5 6 7 8 9    ".indexOf(car);
      } else {
        valore = car.codeUnitAt(0) - 48;
      }
    }
    // la posizione del carattere nella stringa (in base 0) equivale al valore
    // da utilizzare nel calcolo del carattere di controllo per i caratteri in posizione dispari
    return valore;
  }
          

Le stringhe utilizzate per la verifica sono sostanzialmente le tabelle di corrispondenza tra caratteri (alfabetici e numerici) e valori associati rovesciate: il valore associato corrisponde all'indice nella stringa. virgin hair extensions

Verifica (con appunti di programmazione)