primo luogo, utilizzando std::cin >> name
sarà fallire se l'utente immette John Smith
perché >>
divide l'input su caratteri di spaziatura. Si dovrebbe usare std::getline()
per ottenere il nome:
std::getline(std::cin, name);
Qui andiamo ...
Ci sono un certo numero di modi per controllare che una stringa contiene solo caratteri alfabetici. Il più semplice è probabilmente s.find_first_not_of(t)
, che restituisce l'indice del primo carattere di s
che non è nel t
:
bool contains_non_alpha
= name.find_first_not_of("abcdefghijklmnopqrstuvwxyz") != std::string::npos;
che diventa rapidamente ingombrante, però. Per abbinare anche caratteri alfabetici maiuscoli, devi aggiungere altri 26 caratteri a quella stringa! Invece, si consiglia di utilizzare una combinazione di find_if
dal <algorithm>
intestazione e std::isalpha
da <cctype>
:
#include <algorithm>
#include <cctype>
struct non_alpha {
bool operator()(char c) {
return !std::isalpha(c);
}
};
bool contains_non_alpha
= std::find_if(name.begin(), name.end(), non_alpha()) != name.end();
find_if
ricerche un intervallo per un valore che corrisponde a un predicato, in questo caso un funtore non_alpha
che restituisce se il suo argomento è un carattere non alfabetico. Se find_if(name.begin(), name.end(), ...)
restituisce name.end()
, non è stata trovata alcuna corrispondenza.
Ma c'è di più!
Per fare questo come una battuta, è possibile utilizzare gli adattatori dalla <functional>
intestazione:
#include <algorithm>
#include <cctype>
#include <functional>
bool contains_non_alpha
= std::find_if(name.begin(), name.end(),
std::not1(std::ptr_fun((int(*)(int))std::isalpha))) != name.end();
Il std::not1
produce un oggetto funzione che restituisce l'inverso logico del suo ingresso; fornendo un puntatore a una funzione con std::ptr_fun(...)
, possiamo dire a std::not1
di produrre l'inverso logico di std::isalpha
. Il cast (int(*)(int))
è lì per selezionare il sovraccarico di std::isalpha
che accetta uno int
(trattato come un carattere) e restituisce un int
(considerato come un booleano).
Oppure, se è possibile utilizzare un compilatore C++ 11, con un lambda pulisce questo un sacco:
#include <cctype>
bool contains_non_alpha
= std::find_if(name.begin(), name.end(),
[](char c) { return !std::isalpha(c); }) != name.end();
[](char c) -> bool { ... }
indica una funzione che accetta un carattere e restituisce un bool
. Nel nostro caso possiamo omettere il tipo di ritorno -> bool
perché il corpo della funzione è costituito solo da un'istruzione return
. Funziona esattamente come gli esempi precedenti, con la differenza che l'oggetto funzione può essere specificato in modo molto più succinto.
e (quasi) finalmente ...
In C++ 11 si può anche usare un'espressione regolare per eseguire il match:
#include <regex>
bool contains_non_alpha
= !std::regex_match(name, std::regex("^[A-Za-z]+$"));
Ma naturalmente ...
Nessuna di queste soluzioni affronta il problema della localizzazione o della codifica dei caratteri! Per una versione locale-indipendente isalpha()
, avresti bisogno di usare il C++ intestazione <locale>
:
#include <locale>
bool isalpha(char c) {
std::locale locale; // Default locale.
return std::use_facet<std::ctype<char> >(locale).is(std::ctype<char>::alpha, c);
}
Idealmente vorremmo usare char32_t
, ma ctype
non sembra essere in grado di classificare, quindi stiamo bloccato con char
. Fortunatamente per noi possiamo ballare interamente sulla questione del locale, perché probabilmente ti interessano solo le lettere inglesi. C'è una comoda libreria di sola intestazione chiamata UTF8-CPP che ci permetterà di fare ciò che dobbiamo fare in un modo più sicuro per la codifica. In primo luogo definiamo la nostra versione di isalpha()
che utilizza UTF-32 punti di codice:
bool isalpha(uint32_t c) {
return (c >= 0x0041 && c <= 0x005A)
|| (c >= 0x0061 && c <= 0x007A);
}
Poi possiamo usare l'adattatore utf8::iterator
di adattare il basic_string::iterator
da ottetti in UTF-32 punti di codice:
#include <utf8.h>
bool contains_non_alpha
= std::find_if(utf8::iterator(name.begin(), name.begin(), name.end()),
utf8::iterator(name.end(), name.begin(), name.end()),
[](uint32_t c) { return !isalpha(c); }) != name.end();
Per un po 'meglio le prestazioni a scapito della sicurezza, è possibile utilizzare utf8::unchecked::iterator
:
#include <utf8.h>
bool contains_non_alpha
= std::find_if(utf8::unchecked::iterator(name.begin()),
utf8::unchecked::iterator(name.end()),
[](uint32_t c) { return !isalpha(c); }) != name.end();
Questo falliranno su alcuni inpu non valida t.
L'utilizzo di UTF8-CPP in questo modo presuppone che la codifica dell'host sia UTF-8 o una codifica compatibile come ASCII. In teoria questa è ancora una soluzione imperfetta, ma in pratica funzionerà sulla stragrande maggioranza delle piattaforme.
Spero che questa risposta sia finalmente completa!
Se questo è compito ... dovrebbe essere contrassegnato come tale. –
Qual è la tua domanda? Il tuo codice è sbagliato o manca qualcosa? In tal caso, che cosa? –
'* it'. btw anche provarlo con un layout di dvorak. –