Как считать только буквы в строке? - PullRequest
2 голосов
/ 07 мая 2020

Я пытаюсь написать программу, которая считает несколько элементов в строке. Первая из них - буквы.

Задание является частью набора задач CS50 на 2-й неделе, следовательно, включены библиотеки.

Используя условие while, я смог подсчитать каждую символ, но код перестал работать, как только я добавил isalnum (который проверяет, является ли символ буквенно-цифровым).

Что я делаю не так?

#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>

int main(void) {
    string text = get_string("Text: ");
    printf("%s, \n", text);

    int letters = 0;
    while (text[letters] != '\0') {
        if (isalnum(text[letters])) {
            letters++;
        }
    }
    printf("%i \n", letters);
}

Ответы [ 5 ]

2 голосов
/ 07 мая 2020

Если вы хотите считать только буквы:

size_t count_letters(const char *str)
{
    size_t count = 0;

    while(*str)
    {
        count += !!isalpha(*str++);
    }

    return count;
}

или, если хотите, for l oop

size_t count_letters_for_loop(const char *str)
{
    size_t count = 0;

    for(; *str; str++)
    {
        count += !!isalpha(*str);
    }

    return count;
}

Как предложил Дэвид:

Операция !! логически отменяет двукратное значение. Поскольку любая логическая операция дает 0 или 1, это двойное отрицание дает 1 для любого ненулевого значения или 0 для нуля. Это необходимо как isalpha-функция Returns non-zero value if c is an alphabet, else it returns 0.

попробуйте переместить такой лог c в отдельные функции. Это очень важный навык в C языке

2 голосов
/ 07 мая 2020

Здесь показано, как можно определить правильный l oop

size_t letters = 0;
for ( size_t i = 0; text[i] !='\0'; i++ )
{
    if ( isalnum( ( unsigned char )text[i] ) )
    {
       letters++;
    }
}

printf( "%zu\n", letters );

Если вы хотите использовать while l oop, то оно может выглядеть, например, как

size_t letters = 0;
size_t i = 0;
while ( text[i] !='\0' )
{
    if ( isalnum( ( unsigned char )text[i++] ) )
    {
       letters++;
    }
}

printf( "%zu\n", letters );

Обратите внимание, что функция isalnum распознает буквы и цифры. Если вам нужно считать только буквы, воспользуйтесь функцией isalpha.

1 голос
/ 08 мая 2020

Как вы обнаружили, поскольку не все символы в вашей строке гарантированно являются буквенно-цифровыми символами, использование вами letters в качестве счетчика и индекса является ошибочным. Когда вы встречаетесь с символом, который не является альфа и не числом, if (isalnum(text[letters])) проверяет ложь, а letters никогда не увеличивается, что приводит к бесконечному l oop в этой точке. (вы снова тестируете того же персонажа на следующей итерации - с тем же результатом - и декорации никогда не меняются ....)

Как и предполагают все другие очень хорошие и очень правильные ответы, просто используйте отдельную переменную счетчика l oop (или указатель) и увеличивайте ее для итерации по вашей строке.

Еще одна вещь, которую вы можете сделать для проверки вашего logi c (при выводе символов isalnum()) просто избавиться от повторной печати исходной строки с printf (она у вас есть прямо перед вами из вашей записи) и вместо этого вывести символы, соответствующие вашим критериям. Например:

    for (int i = 0; text[i]; i++) {
        if (isalnum((unsigned char)text[i])) {
            putchar (text[i]);
            letters++;
        }
    }

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

Также обратите внимание, что нет необходимости в #include <string.h>, поскольку нет функций, требующих его включения, используемых в вашем коде. С этими изменениями быстрым примером может быть:

#include <cs50.h>
#include <stdio.h>
#include <ctype.h>

int main(void) {

    int letters = 0;
    string text = get_string("Text: ");

    for (int i = 0; text[i]; i++) {
        if (isalnum((unsigned char)text[i])) {
            putchar (text[i]);
            letters++;
        }
    }

    printf (", %d\n", letters);
}

Пример использования / вывода

$ ./bin/ltrcountcs50-1
Text: 123.abc-456_def_*.*_789
123abc456def789, 15

Перемещение счетчика в функцию

Вы можете легко перенести подсчет буквенно-цифровых символов в функцию. Одним из преимуществ передачи вашей строки в функцию является то, что полученный функцией указатель является копией указателя из main() (C - это передача по значению). Это позволяет вам просто выполнить итерацию с параметром и вернуть количество буквенно-цифровых символов, например,

int countalnum (const char *s)
{
    int letters = 0;

    while (*s)
        if (isalnum((unsigned char)*s++))
            letters++;

    return letters;
}

( примечание: вы должны передать параметр как const char *, чтобы использовать указатель на константный символ. Вы не можете использовать const string, используя определение типа cs50. Если вы не будете изменять значение, переданное в функции, передача const позволяет компилятору выполнить оптимизацию, которую иначе он не смог бы сделать)

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

#include <cs50.h>
#include <stdio.h>
#include <ctype.h>

int countalnum (const char *s)
{
    int letters = 0;

    while (*s)
        if (isalnum((unsigned char)*s++))
            letters++;

    return letters;
}

int main(void) {

    string text = get_string("Text: ");

    printf ("%s, %d\n", text, countalnum(text));
}

Пример использования / вывода

$ ./bin/ltrcountcs50-1
Text: 123.abc-456_def_*.*_789
123.abc-456_def_*.*_789, 15

Но здесь, используя результаты функции при печати всей исходной строки в main(). Вы можете настроить мощность по своему усмотрению.

1 голос
/ 07 мая 2020

You while l oop имеет недостаток: вы используете одну и ту же переменную для подсчета буквенно-цифровых c символов и индексации строки. Это работает, только если все символы - буквенно-цифровые c, иначе вы получите бесконечное l oop, потому что вы перестанете увеличивать letter.

Вы должны использовать for l oop с индексом i.

Также обратите внимание, что isalnum() все функции из <ctype.h> не определены для отрицательных значений, кроме EOF. На платформах, где по умолчанию подписано char, некоторые символы в строке могут иметь отрицательные значения, что приведет к неопределенному поведению, если вы передадите их в isalnum(). Чтобы избежать этого, вы должны преобразовать char значения как (unsigned char).

Это измененная версия.

#include <cs50.h>
#include <stdio.h>
#include <ctype.h>

int main(void) {
    string text = get_string("Text: ");
    printf("%s, \n", text);

    int letters = 0;
    for (int i = 0; text[i] != '\0'; i++) {
        if (isalnum((unsigned char)text[i])) {
            letters++;
        }
    }
    printf("%i\n", letters);
    return 0;
}
0 голосов
/ 07 мая 2020

Вы сказали, что это буквы, а затем вы использовали isalnum? поэтому числа будут считаться буквами в вашем случае, и вы увеличиваете значение букв, которые цикл рассчитывает для перебора строки, только когда это буква, так что это приводит к бесконечному l oop, когда условие ложно здесь решение:

#include <stdio.h>
#include <ctype.h>

int countLetters(char *str) {
  int letters = 0, ind = 0;
  while(str[ind] != '\0') 
    isalpha((unsigned char)str[ind++]) && letters++;
  return letters;
}

int main(void) {
  char text[100];
  fgets(text, sizeof(text), stdin);
  printf("%i", countLetters(text));
  return 0;
}
// input: hello 15-p */x
// output: 7
...