Как я могу проверить, имеет ли строка специальные символы в C ++ эффективно? - PullRequest
8 голосов
/ 07 июля 2011

Я пытаюсь найти лучший способ проверить, есть ли в строке специальные символы. В моем случае все, кроме буквенно-цифровых символов и «_», считается специальным символом. В настоящее время у меня есть строка, которая содержит специальные символы, такие как std :: string = "! @ # $% ^ &". Затем я использую алгоритм std :: find_first_of (), чтобы проверить, присутствует ли какой-либо из специальных символов в строке.

Мне было интересно, как это сделать на основе белого списка. Я хочу указать строчные / прописные символы, цифры и подчеркивание в строке (я не хочу их перечислять. Есть ли способ указать диапазон ascii некоторого вида, например [a-zA-Z0-9_] ). Как мне этого добиться? Тогда я планирую использовать std :: find_first_not_of (). Таким образом, я могу упомянуть, что я действительно хочу, и проверить на обратное.

Ответы [ 8 ]

13 голосов
/ 07 июля 2011

Попробуйте:

std::string  x(/*Load*/);
if (x.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890_") != std::string::npos)
{
    std::cerr << "Error\n";
}

Или попробуйте увеличить регулярные выражения:

// Note: \w matches any word character `alphanumeric plus "_"`
boost::regex test("\w+", re,boost::regex::perl);
if (!boost::regex_match(x.begin(), x.end(), test)
{
    std::cerr << "Error\n";
}

// The equivalent to \w should be:
boost::regex test("[A-Za-z0-9_]+", re,boost::regex::perl);   
3 голосов
/ 07 июля 2011

Я думаю, я бы сделал эту работу немного по-другому, рассматривая std::string как коллекцию и используя алгоритм.При использовании лямбды C ++ 0x это будет выглядеть примерно так:

bool has_special_char(std::string const &str) {
    return std::find_if(str.begin(), str.end(),
        [](char ch) { return !(isalnum(ch) || ch == '_'); }) != str.end();
}

По крайней мере, когда вы имеете дело с char (не wchar_t), isalnum обычно будет использовать таблицупосмотрите, так что обычно (немного) быстрее, чем что-либо, основанное на find_first_of (вместо этого обычно используется линейный поиск).Итак, это O (N) (N = str.size ()), где что-то, основанное на find_first_of, будет O (N * M), (N = str.size (), M = pattern.size ()).

Если вы хотите выполнить работу с чистым C, вы можете использовать scanf с преобразованием набора сканирования, которое теоретически непереносимо, но поддерживается практически всеми недавними / популярными компиляторами:

char junk;
if (sscanf(str, "%*[A-Za-z0-9_]%c", &junk))
    /* it has at least one "special" character
else
    /* no special characters */

Основная идея здесь довольно проста: скансет пропускает все последовательные не специальные символы (но не присваивает результат чему-либо из-за *), затем мы пытаемся прочитать еще один символ.Если это удается, это означает, что был по крайней мере один символ, который был не пропущен, поэтому мы должны иметь по крайней мере один специальный символ.Если это не удается, это означает, что преобразование набора сканирования соответствует всей строке, поэтому все символы были «не специальными».

Официально стандарт C говорит, что попытка поместить диапазон в преобразование набора сканирования, как это't portable (a' - 'в любом месте, кроме начала или конца набора сканирования, дает поведение, определяемое реализацией).Было даже несколько компиляторов (из Borland), которые потерпели бы неудачу из-за этого - они рассматривали бы A-Z как совпадающие ровно с тремя возможными символами, «A», «-» и «Z».Большинство современных компиляторов (или, точнее, реализации стандартной библиотеки) используют подход, который предполагает: «AZ» соответствует любому символу верхнего регистра.

3 голосов
/ 07 июля 2011

Первое, что вам нужно рассмотреть, это "только этот ASCII"?Если вы ответите «да», я бы посоветовал вам по-настоящему задуматься, следует ли разрешать только ASCII.В настоящее время я работаю в компании, у которой действительно есть некоторые головные боли, выходящие на зарубежные рынки, потому что мы не думали поддерживать юникод с самого начала.

При этом, ASCII действительно облегчает проверку на отсутствиебуквенные числа.Посмотрите на диаграмму ascii.

http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters

  • Итерация по каждому символу
  • Проверьте, является ли символ десятичным значением 48 - 57, 65 - 90, 97 - 122 или 95 (подчеркивание)
3 голосов
/ 07 июля 2011

Нет способа использовать стандартный C или C ++, чтобы сделать это, используя диапазоны символов, вы должны перечислить все символы.Для строк C вы можете использовать strspn(3) и strcspn(3), чтобы найти первый символ в строке, которая является членом или не является членом данного набора символов.Например:

// Test if the given string has anything not in A-Za-z0-9_
bool HasSpecialCharacters(const char *str)
{
    return str[strspn(str, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_")] != 0;
}

Для строк C ++ вы можете эквивалентно использовать функции-члены find_first_of и find_first_not_of.

Другой вариант - использовать isalnum(3) исвязанные функции из <ctype.h> для проверки, является ли данный символ буквенно-цифровым или нет;обратите внимание, что эти функции зависят от локали , поэтому их поведение может (и меняется) в других локалях.Если вы не хотите такого поведения, не используйте их.Если вы решите использовать их, вам также придется отдельно проверять подчеркивание, поскольку нет функции, которая проверяет «алфавитное, числовое или подчеркивание», и вам также придется кодировать свой собственный цикл для поиска строки (или используйте std::find с соответствующим функциональным объектом).

1 голос
/ 07 июля 2011

Функции (макросы) зависят от настроек локали, но вы должны исследовать isalnum() и родственников из <ctype.h> или <cctype>.

0 голосов
/ 23 сентября 2012

Используя

    s.erase(std::remove_if(s.begin(), s.end(), my_predicate), s.end());

    bool my_predicate(char c)
    {
     return !(isalpha(c) || c=='_');
    }

, вы получите чистую строку s.

Стереть удалит все специальные символы и настраивается с помощью функции my_predicate.

0 голосов
/ 07 июля 2011

Если вы хотите этого, но не хотите использовать всю задержку и использовать регулярные выражения, и, учитывая, что вы тестируете символы ASCII - просто создайте функцию для генерации строки для find_first_not_of ...

#include <iostream>
#include <string>

std::string expand(const char* p)
{
    std::string result;
    while (*p)
        if (p[1] == '-' && p[2])
        {
            for (int c = p[0]; c <= p[2]; ++c)
                result += (char)c;
            p += 3;
        }
        else
            result += *p++;
    return result;
}

int main()
{
    std::cout << expand("A-Za-z0-9_") << '\n';
}
0 голосов
/ 07 июля 2011

Я бы просто использовал встроенное средство Си здесь.Перебирайте каждый символ в строке и проверяйте, является ли он _ или isalpha(ch) истинно.Если это так, то это действительно, в противном случае это специальный символ.

...