簡體   English   中英

如何只計算字符串中的字母?

[英]How to count only letters in string?

我正在嘗試編寫一個計算字符串中多個元素的程序。 其中第一個是字母。

該作業是 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);
}

這里展示了如何定義正確的循環

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循環,那么它可以看起來像

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

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

注意 function isalnum檢測字母和數字。 如果您只需要計算字母,請使用 function isalpha

如果您只想計算字母:

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

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

    return count;
}

或者如果你更喜歡for循環

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 function Returns non-zero value if c is an alphabet, else it returns 0.

嘗試將這樣的邏輯移動到單獨的函數中。 C語言中非常重要的技能

您的while循環有一個缺陷:您使用相同的變量來計算字母數字字符並索引字符串。 它僅在所有字符都是字母數字的情況下才有效,否則您將獲得無限循環,因為您停止遞增letter

您應該使用帶有索引ifor循環。

還要注意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;
}

正如您所發現的,由於並非字符串中的所有字符都保證是字母數字字符,因此您將letters用作計數器和索引是有缺陷的。 當您遇到一個不是 alpha 也不是數字的字符時, if (isalnum(text[letters]))測試為 false 並且letters永遠不會遞增,從而導致此時出現無限循環。 (你在下一次迭代中再次測試同一個角色——結果相同——而且風景永遠不會改變......)

正如所有其他非常好的和非常正確的答案所建議的那樣,只需使用一個單獨的循環計數器變量(或指針)並遞增它來迭代你的字符串。

您可能會做的另一件事來驗證您的邏輯(在輸出isalnum()字符時)只是取消使用printf重新打印原始字符串(從您的條目中您可以看到它),而不是 output 字符符合您的標准。 例如:

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

這只是您的 output 的一個細微變化,它提供雙重任務,提供您的 output 並提供與所用標准匹配的每個字符的確認。

另請注意,沒有必要#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

將計數移入 A Function

您可以輕松地將字母數字字符的計數移動到 function 中。 將字符串傳遞給 function 的一個好處是 function 接收的指針是來自main()的指針的副本(C 是按值傳遞)。 這使您可以簡單地使用參數進行迭代並返回字母數字字符的計數,例如

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

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

    return letters;
}

注意:您必須將參數作為const char *傳遞以使用指向常量 char 的指針。您不能使用 cs50 typedef 使用const string 。如果您不更改 function 中傳遞的值,則作為const傳遞允許編譯器進行優化,否則將無法進行)

使用上面的 function,您的代碼可簡化為:

#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

但是在這里,使用 function 會導致在main()中打印整個原始字符串。 您可以根據需要調整 output。

你說是字母,然后你用isalnum 因此,在您的情況下,數字將被計為字母,並且您增加循環計數的字母的值,僅當它是字母時才迭代字符串,以便在條件為假時導致無限循環這里是一個解決方案:

#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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM