Определение, если строки в стиле C является буквой


Я ищу самый быстрый способ определить, если строки в стиле C содержит только алфавитные символы.

На этот вопрос есть несколько предположений:

  1. strlen(c) > 0
  2. Строки с завершающим нулем
  3. Непрерывная кодировка от a-z и A-Z не гарантирую, но скорее всего.
  4. Строка не является нулевой указатель, или указатель ее "по умолчанию".
#include <stdbool.h>
#include <ctype.h>

bool stralpha(const char *c)
{
    bool alphabetic = true;

    while(*c)
        alphabetic &= !!isalpha(*c++);

    return alphabetic;
}

/* so gcc does not complain */
int main(void){}

Чтобы было понятно: я знаю, что любая разница в производительности в такой функции будет минимальным в лучшем случае. Это просто для удовольствия.

В моем случае, я был возникли проблемы с функцией, возвращающей значение false, даже когда c был чисто алфавитным. Вот почему я использовать двойное отрицание !! на isalpha()потому что это не гарантирует возврат последовательно 0 или 1 согласно стандарту.



2097
10
задан 28 марта 2018 в 02:03 Источник Поделиться
Комментарии
8 ответов

В основном проблема с ваш код (в плане производительности) заключается в том, что он обрабатывает каждый символ в строке независимо от. Это, наверное, лучше на самом деле выйти рано, если вы не найдете номера Альфа-символ, что-то вроде:

bool isStrAlpha (const char *chPtr) {
// if (*chPtr == '\0') return false;
while (*chPtr != '\0')
if (! isalpha (*chPtr++))
return false;
return true;
}

И еще пару заметок:


  • Этот код прекрасно работает даже на нулевую длину строки, предполагая, что целью является обеспечение все символы в строке являются Альфа. Другими словами, он считает, что пустая строка является допустимым, поскольку он не содержит не-алфавитных символов. Если вы хотите, чтобы строка должна иметь по крайней мере один альфа, просто восстановить закомментированные строки в начале функции.

  • Ваше второе предположение является излишним. Ряд персонажей, которые не прекращаются с \0 не на самом деле строки в C.

  • Будет ли Альфа-персонажи скорее всего будет подряд не актуальна на мой взгляд, я обычно предпочитаю правильный, переносимый код, за скорость. Но, в любом случае, вы можете быть уверены, что люди, которые создали реализация оптимизированной дохрена из классификации характером.

13
ответ дан 28 марта 2018 в 08:03 Источник Поделиться

Прежде всего, я должен отметить, что если вы делаете работы, необходимо профилировать код, чтобы увидеть, где замедление. Что позволит вам объективно определить, являются ли внесенные изменения на самом деле помогает или нет.

У вас есть 2 отрицания на ваш звонок isalpha(). Если вы удалите их оба, вы получите тот же результат, но быстрее. Я изначально думал, что двойное отрицание isalpha() было бессмысленно, но другие указали, что это необходимо из-за закидонов в языке Си. Учитывая, что, и учитывая, что я не помню, чтобы когда-нибудь нарваться на это в дикой природе, я бы рекомендовал переформулировать его. Есть несколько вариантов:


  1. Избавиться от &= как упомянуто ниже.

  2. Написать функцию, которая возвращает фактическое bool стоимости, поэтому вам не придется беспокоиться об этом бзик.

  3. Изменение местный звонок-сайт для расчета bool делая что-то вроде alphabetic &= (isalpha(*c++) != 0);

  4. По крайней мере, прокомментировать, почему вы нужны !! в первую очередь, так как это не сразу видно.

Вы могли выйти из цикла раньше на первом неалфавитный символ. Что-то вроде:

while ((*c) && (alphabetic))
{
alphabetic &= isalpha(*c++);
}

В он-лайн реализации isalpha() скорее всего, будет быстрее, чем система вызова библиотеки. Конечно, что делает обслуживание сложнее, потому что вы должны иметь различные версии для каждой кодировки, дали возможность несмежные a-z персонажей. Простой способ, если вы можете гарантировать 8-битные символы, иметь 256 элемент подстановки-таблица, где каждый элемент является истинным или ложным. Можно автоматически построить его, делая что-то вроде:

bool alphaTable[256];
for (int nextChar = 0; nextChar < 256; nextChar++)
{
alphaTable[nextChar] = isalpha(nextChar);
}

Тогда ваш цикл будет:

bool alphabetic = true;
while((*c) && (alphabetic))
{
alphabetic = alphaTable[*c++];
}

Вы можете избавиться от &= потому что всякий раз, когда оно становится ложным, 1) мы закончим, и 2) все последующие значения будут ложными.

Это может быть сделано даже быстрее, используя SIMD-инструкции. Детали будут варьироваться в зависимости от архитектуры, так опять же, техническое обслуживание. Если у вас очень большое количество строк для расчета на одно время, вы также можете выполнять несколько одновременных вычислений, запустив функцию на нескольких ядрах одновременно.

8
ответ дан 28 марта 2018 в 04:03 Источник Поделиться

Не много, чтобы рассмотреть, кроме опечатка (должно быть <ctype.h>). Как отмечалось в другой ответ, !! имеет сомнительную ценность.

Как ради забавы, настоящий ностальгический цикл семидесятых будет выглядеть

    while (isalpha(*c++)) {
;
}
return !c[-1];

8
ответ дан 28 марта 2018 в 04:03 Источник Поделиться

По-настоящему быстрым способом является какой-то таблице, как показано в другой ответ. Такая таблица могла бы даже быть сгенерирована во время компиляции:

const bool is_alpha [256] =
{
['A'] = true,
['B'] = true,
...
['Z'] = true,
['a'] = true,
...
};

Элементы, которые не задано значение true гарантированно быть по умолчанию инициализируется 0 = ложно.


Это, как говорится, вот мой отзыв код:

Ошибки


  • bool alphabetic = true; означает, что ваша функция будет возвращать true, если первый элемент массива \0.

  • Вы не останавливайтесь, как только вы найдете недопустимый характер, поэтому вся функция неоправданно медленно.

Стиль кодирования


  • В !! нормально. Не другие отзывы, чтобы понять, что C стандарт гарантирует только: (С11 7.4.1)


    Функции в этот раздел возвращает ненулевое значение (true), если и только если значение параметр C отвечает, что в описании функции.

    Этот текст остается неизменным с С90, как прежде, С типа boolean. Так это не значит, что функция обязательно возвращает логическое значение true, а то, что она возвращает что-то ноль.

    Однако, код будет более читабельным, если вы пишете is_alpha(...) != false. Использование !! часто критикуют как запутывание.


  • Использование ++ (заранее или постфикс) смешанной с другими операторами в одно и то же выражение широко признан как плохая практика. Есть много опасностей, с этим, как приоритет операторов, ошибки и риск вызывая плохо определенное поведение. Кроме того, это делает код трудным для чтения.

    Предположим, например, что кто-то приходит и поддерживает код, хотите разрешить пробелы, осознает, что is... функции возвращают что-то ноль, и поэтому пишет что-то вроде !!(isalpha(*c++) | isspace(*c)). Бум, серьезные ошибки, неопределенное поведение.

    Поэтому никогда не пишите такие вещи, как *c++ даже если это случается очень часто используются с подвохом. Нет никакой выгоды какого-либо смешивания ++ С другими операторами, только опасности.


  • Использование while(*c) это обсуждается стиль кодирования. Как таковой, это немного субъективно - некоторые думают, что этот стиль хорош, потому что он является "традиционной". Другие, как Мишра-с стандарт кодирования (и я) предпочитают while(*c != '\0') так как это более самодокументированный, улучшает возможности статического анализа кода и предотвращает путаницы с c != NULL, что, конечно, означает нечто совершенно другое.

  • Всегда использовать составные операторы { } ведь контроль и петли выражения, без исключения. Чтобы пропустить это при наличии только одной строки кода трейлинг-плохая и опасная практика. Такой стиль кодирования, причиненного самых дорогих ошибок, когда-либо написанных в истории программирования, после которого нет никаких аргументов не осталось, что оправдывает стиль.


Фиксированная версия:

bool stralpha(const char *c)
{
if(*c == '\0')
{
return false;
}

while(*c != '\0' && isalpha(*c))
{
c++;
}

return *c == '\0';
}

6
ответ дан 28 марта 2018 в 07:03 Источник Поделиться

Оператор++, когда используется в качестве выражения, подвержена ошибкам в случае, если кто-то позже добавляет или удаляет некоторые строки. Просто использовать обычный цикл, оптимизатор будет большинство ответов здесь все равно один и тот же код компилируется:

bool isalpha_str(char const* s)
{
for(; *s; ++s)
{
if(!isalpha(*s))
{
return false;
}
}
return true;
}

2
ответ дан 28 марта 2018 в 04:03 Источник Поделиться

Если char занимает 8 байт и шорт 16 байт (вам придется применять это заранее) вы могли бы сделать некоторые развертывание цикла и тест до двух персонажей сразу на несколько скромных улучшений. Я проверил это с 27 символов строки (с Альфы-версии в конце, чтобы не воспользоваться отсутствием исходного кода короткого замыкания) и сравнила его с основным 256-запись таблицы подстановки версия. Раскатанное версия была примерно на 25% быстрее на моей машине и с помощью GCC-O3 для обоих.

bool alphaTable16[65536];

void init16 ()
{
for (int i=0; i<256; ++i)
{
for (int j=0; j<256; ++j)
{
char k[2]={i,j};
alphaTable16[*(unsigned short *)k]=isalpha(k[0]) && isalpha(k[1]);
}
}
for (int j=0; j<256; ++j)
{
alphaTable16[j]=isalpha(j);
}
}

bool stralpha2 (const char *c)
{
while (true)
{
const char *d=c;
if (*c++==0) /* test no characters */
return true;
if (*c++==0) /* test one character */
return alphaTable16[*d];
/* test two characters */
if (!alphaTable16[*(unsigned short *)d]) return false;
}
}

-1
ответ дан 28 марта 2018 в 03:03 Источник Поделиться

Ваш код вызывает неопределенное поведение. При работе на const char *аргумент к ctype для функции должны всегда быть приведен к unsigned char.

-1
ответ дан 29 марта 2018 в 05:03 Источник Поделиться

Вышеуказанные ответы, используя массив is_alpha дорогие с точки зрения памяти.

вы можете использовать:

bool is_alpha(char c) {
//A-Z
if( (c>=65) && (c<=90) )
return true;
if( (c>=97) && (c<=122) )
return true;
return false;
}

все условия могут быть в одну строку, это для ясности.

-3
ответ дан 28 марта 2018 в 01:03 Источник Поделиться