Как я могу проверить, если строка содержит специальные символы в C++ эффективно?

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

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

8 ответов


попробуй:

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);   

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

Это, как говорится, ASCII позволяет очень легко проверить не альфа-цифры. Взгляните на ascii таблица.

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

  • перебираем каждый символ
  • проверьте, является ли символ десятичным значением 48 - 57, 65 - 90, 97 - 122, или 95 (подчеркивание)

нет способа использовать стандартный 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 С соответствующим объектом функции).


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

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 обычно будет использовать таблицу look up, поэтому она обычно будет (совсем немного) быстрее, чем что-либо на основе find_first_of (который обычно используют линейный поиск). IOW, Это O (N) (N=str.size ()), где что-то основано на find_first_of будет O (N*M), (n=str.size (), M=шаблон.размер.))(

если вы хотите выполнить работу с чистым 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 говорит, что пытается поставить в преобразования набор такой диапазон не переносимо (а '-' в любом месте, кроме начала или конца набора дает реализация определенного поведения). Там есть даже было несколько компиляторов (из Borland), которые потерпели бы неудачу для этого-они бы лечили A-Z как совпадающие ровно три возможных символа: "A", " - " и "Z". Большинство современных компиляторов (или, точнее, стандартных реализаций библиотек) используют подход, который предполагает: "A-Z" соответствует любому символу верхнего регистра.


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


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


Если вы хотите этого, но не хотите идти всю свинью и использовать регулярные выражения, и учитывая, что вы тестируете для символов 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';
}

используя

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

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

получите вам чистую строку s.

Erase обнажит его с всех специальных характеров и сильно customisable с