PHP: Separare il nome dal cognome con PCRE

Come separare il nome dal cognome in php utilizzando le PCRE (Perl Compatible Regular Expressions)

Quando si lavora con dati di origine eterogenea, come ad esempio nei processi di importazione/esportazione, può capitare di dover separare il nome dal cognome, presenti entrambi nella stessa stringa.

La difficoltà maggiore consiste nel fatto che non è possibile, con assoluta certezza, distinguere un cognome da un nome. Pensate ad esempio ai casi in cui il cognome è anche un nome personale.

La soluzione più efficace, a mio parere, è cercare di stabilire una corrispondenza con un pattern che rappresenti i casi più comuni, in modo da avere una percentuale di incongruenze abbastanza bassa da permette agevolmente una correzione manuale.

Il codice che segue fa uso delle PCRE (Perl Compatible Regular Expressions) e prevede sia i cognomi preceduti da articoli o preposizioni, sia i secondi e terzi nomi.

Nel caso in esame ci aspettiamo che, nella stringa da elaborare, compaia prima il cognome e successivamente il nome. E’ possibile cambiare l’ordine semplicemente cambiando la posizione degli elementi presenti nell’array “$model”.

Ecco il codice:

class splitFullName {

  /**
   * Pattern
   *
   * @var     string
   * @access  private
   */
  private $pattern = null;

  /**
   * Modello utilizzato per comporre il pattern e distinguere il cognome dal nome
   *
   * @var     array
   * @access  private
   */
  private $model = array(
      'lastname' => "([adeilo']{0,4}\s*[\w']+)",
      'firstname' => "([\w']+\s*[\w'\.]*\s*[\w'\.]*)"
  );

  public function __construct() {
    // Concatena gli elementi di $model per creare il pattern
    $buffer = array();
    foreach ($this->model as $key => $pattern) {
      $buffer[] = $pattern;
    }
    $this->pattern = "/^" . implode('\s+', $buffer) . "$/i";
  }

  /**
   * Elabora la stringa contenente il cognome e il nome provando a separare i due 
   * elementi in un array
   *
   * @param  string $fullName Stringa contenente il cognome e il nome
   * @access public
   * @return array Restituisce un array sulla base dell'attributo $model, se il
   * pattern non viene riconosciuto, l'array conterra' $fullName nel primo 
   * elemento
   */
  public function split($fullName) {
    $fullName = trim($fullName);
    $matches = array();
    if (preg_match($this->pattern, $fullName, $matches)) {
      $buffer = array();
      for ($i = 1, $l = count($matches); $i < $l; $i++) { $buffer[] = $matches[$i]; } $result = array_combine(array_keys($this->model), $buffer);
      return $result;
    }
    $buffer = array_fill(0, count($this->model), '');
    $buffer[0] = $fullName;
    $result = array_combine(array_keys($this->model), array($buffer, ''));
    return $result;
  }

}

 Esempio di utilizzo in cui il riconoscimento avviene con successo:
$s = new splitFullName();
$fullName = "Fantozzi Ugo";
$result = $s->split($fullName);

questo è il risultato:

Array
(
  [lastname] => "Fantocci"
  [firstname] => "Ugo"
)

Questo invece è un esempio in cui il riconoscimento non avviene correttamente. In questo caso è necessaria una correzione manuale:

$s = new splitFullName();
$fullName = "Serbelloni Mazzanti Viendalmare Maria";
$result = $s->split($fullName);

questo è il risultato:

Array
(
  [lastname] => "Serbelloni"
  [firstname] => "Mazzanti Viendalmare Maria"
)

Il codice è certamente migliorabile, ad esempio manca una vera e propria gestione delle eccezioni, tuttavia se si tratta di gestire poche migliaia di nominativi si comporta egregiamente.