как я могу проверить строку против только допустимых символов, таких как буквы a-z?...
Ответ 2
Во-первых, использование std::cin >> name
завершится неудачей, если пользователь войдет в John Smith
, потому что >>
разделяет ввод на символы пробелов. Вы должны использовать std::getline()
для получения имени:
std::getline(std::cin, name);
Здесь мы идем...
Существует несколько способов проверить, что строка содержит только буквенные символы. Простейший, вероятно, s.find_first_not_of(t)
, который возвращает индекс первого символа в s
, который не находится в t
:
bool contains_non_alpha
= name.find_first_not_of("abcdefghijklmnopqrstuvwxyz") != std::string::npos;
Тем не менее, это быстро становится громоздким. Чтобы также соответствовать строчным буквенным символам, вам нужно добавить еще 26 символов в эту строку! Вместо этого вы можете использовать комбинацию find_if
из заголовка <algorithm>
и std::isalpha
из <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
ищет диапазон для значения, соответствующего предикату, в этом случае функтор non_alpha
, который возвращает, является ли его аргумент неалфавитным символом. Если find_if(name.begin(), name.end(), ...)
возвращает name.end()
, то совпадения не найдено.
Но theres больше!
Чтобы сделать это как однострочный, вы можете использовать адаптеры из заголовка <functional>
:
#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();
std::not1
создает объект функции, который возвращает логический инверсный вход; путем указания указателя на функцию с std::ptr_fun(...)
, мы можем сказать std::not1
создать логический обратный символ std::isalpha
. Листинг (int(*)(int))
предназначен для выбора перегрузки std::isalpha
, которая принимает int
(рассматривается как символ) и возвращает int
(рассматривается как логическое).
Или, если вы можете использовать компилятор С++ 11, использование лямбды очень много очищает:
#include <cctype>
bool contains_non_alpha
= std::find_if(name.begin(), name.end(),
[](char c) { return !std::isalpha(c); }) != name.end();
[](char c) -> bool { ... }
обозначает функцию, которая принимает символ и возвращает a bool
. В нашем случае мы можем опустить возвращаемый тип -> bool
, потому что тело функции состоит только из оператора return
. Это работает так же, как и предыдущие примеры, за исключением того, что объект функции может быть указан гораздо более лаконично.
И (почти) наконец...
В С++ 11 вы также можете использовать регулярное выражение для выполнения соответствия:
#include <regex>
bool contains_non_alpha
= !std::regex_match(name, std::regex("^[A-Za-z]+$"));
Но, конечно...
Ни одно из этих решений не затрагивает проблему языковой или кодировки символов! Для независимой от языка версии isalpha()
вам необходимо использовать заголовок С++ <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);
}
В идеале мы использовали бы char32_t
, но ctype
, похоже, не в состоянии его классифицировать, поэтому застряли в char
. К счастью для нас, мы можем полностью танцевать по поводу локали, потому что вы, вероятно, интересуетесь только английскими буквами. Theres удобная библиотека только для заголовков, называемая UTF8-CPP, которая позволит нам делать то, что нам нужно сделать в более безопасном для кодирования виде. Сначала мы определяем нашу версию isalpha()
, которая использует кодовые точки UTF-32:
bool isalpha(uint32_t c) {
return (c >= 0x0041 && c <= 0x005A)
|| (c >= 0x0061 && c <= 0x007A);
}
Затем мы можем использовать адаптер utf8::iterator
для адаптации basic_string::iterator
из октетов в кодовые точки UTF-32:
#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();
Для немного лучшей производительности за счет безопасности вы можете использовать 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();
Это приведет к ошибке при некорректном вводе.
Использование UTF8-CPP таким образом предполагает, что хост-кодирование является UTF-8 или совместимым кодированием, таким как ASCII. Теоретически это еще несовершенное решение, но на практике оно будет работать на подавляющем большинстве платформ.
Я надеюсь, что этот ответ окончательно завершен!