如何只计算字符串中的字母?
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] != '[=10=]') {
if (isalnum(text[letters])) {
letters++;
}
}
printf("%i \n", letters);
}
此处显示了如何定义正确的循环
size_t letters = 0;
for ( size_t i = 0; text[i] !='[=10=]'; i++ )
{
if ( isalnum( ( unsigned char )text[i] ) )
{
letters++;
}
}
printf( "%zu\n", letters );
如果你想使用 while 循环,那么它可以看起来像
size_t letters = 0;
size_t i = 0;
while ( text[i] !='[=11=]' )
{
if ( isalnum( ( unsigned char )text[i++] ) )
{
letters++;
}
}
printf( "%zu\n", letters );
注意函数isalnum
检测字母和数字。如果你只需要计算字母,那么使用函数 isalpha
.
你说是字母,然后你用isalnum
?
所以在你的情况下数字将被算作字母
然后你增加循环中字母的值
仅当它是字母时才对字符串进行迭代
这样当条件为假时会导致无限循环
这是一个解决方案:
#include <stdio.h>
#include <ctype.h>
int countLetters(char *str) {
int letters = 0, ind = 0;
while(str[ind] != '[=10=]')
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
如果你只想计算字母:
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 函数 Returns non-zero value if c is an alphabet, else it returns 0.
尝试将这样的逻辑移动到单独的函数中。这是C
语言
中非常重要的技能
你 while
循环有一个缺陷:你使用同一个变量来计算字母数字字符和索引字符串。它仅在所有字符都是字母数字时才有效,否则会出现无限循环,因为您停止递增 letter
.
您应该使用索引为 i
的 for
循环。
另请注意 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] != '[=10=]'; i++) {
if (isalnum((unsigned char)text[i])) {
letters++;
}
}
printf("%i\n", letters);
return 0;
}
正如您所发现的,由于并非字符串中的所有字符都保证是字母数字字符,因此您使用 letters
作为计数器和索引是有缺陷的。当您遇到一个不是 alpha 且不是数字的字符时,if (isalnum(text[letters]))
测试为 false 并且 letters
永远不会递增,从而导致此时出现无限循环。 (你在下一次迭代中再次测试同一个角色——结果相同——而且场景永远不会改变....)
正如所有其他非常好的和非常正确的答案所建议的那样,只需使用一个单独的循环计数器变量(或指针)并递增它以迭代您的字符串。
你可以做的另一件事来验证你的逻辑(同时输出 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);
}
例子Use/Output
$ ./bin/ltrcountcs50-1
Text: 123.abc-456_def_*.*_789
123abc456def789, 15
将计数移入函数
您可以轻松地将字母数字字符的计数移动到一个函数中。将字符串传递给函数的一个好处是函数接收到的指针是 main()
指针的副本(C 是按值传递)。这使您可以简单地使用参数和 return 字母数字字符的计数进行迭代,例如
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
。如果您不更改函数中传递的值,则传递为 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));
}
例子Use/Output
$ ./bin/ltrcountcs50-1
Text: 123.abc-456_def_*.*_789
123.abc-456_def_*.*_789, 15
但是在这里,使用该函数会导致在 main()
中打印整个原始字符串。您可以根据需要调整输出。
我正在尝试编写一个计算字符串中多个元素的程序。第一个是字母。
该作业是 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] != '[=10=]') {
if (isalnum(text[letters])) {
letters++;
}
}
printf("%i \n", letters);
}
此处显示了如何定义正确的循环
size_t letters = 0;
for ( size_t i = 0; text[i] !='[=10=]'; i++ )
{
if ( isalnum( ( unsigned char )text[i] ) )
{
letters++;
}
}
printf( "%zu\n", letters );
如果你想使用 while 循环,那么它可以看起来像
size_t letters = 0;
size_t i = 0;
while ( text[i] !='[=11=]' )
{
if ( isalnum( ( unsigned char )text[i++] ) )
{
letters++;
}
}
printf( "%zu\n", letters );
注意函数isalnum
检测字母和数字。如果你只需要计算字母,那么使用函数 isalpha
.
你说是字母,然后你用isalnum
?
所以在你的情况下数字将被算作字母
然后你增加循环中字母的值
仅当它是字母时才对字符串进行迭代
这样当条件为假时会导致无限循环
这是一个解决方案:
#include <stdio.h>
#include <ctype.h>
int countLetters(char *str) {
int letters = 0, ind = 0;
while(str[ind] != '[=10=]')
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
如果你只想计算字母:
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 函数 Returns non-zero value if c is an alphabet, else it returns 0.
尝试将这样的逻辑移动到单独的函数中。这是C
语言
你 while
循环有一个缺陷:你使用同一个变量来计算字母数字字符和索引字符串。它仅在所有字符都是字母数字时才有效,否则会出现无限循环,因为您停止递增 letter
.
您应该使用索引为 i
的 for
循环。
另请注意 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] != '[=10=]'; i++) {
if (isalnum((unsigned char)text[i])) {
letters++;
}
}
printf("%i\n", letters);
return 0;
}
正如您所发现的,由于并非字符串中的所有字符都保证是字母数字字符,因此您使用 letters
作为计数器和索引是有缺陷的。当您遇到一个不是 alpha 且不是数字的字符时,if (isalnum(text[letters]))
测试为 false 并且 letters
永远不会递增,从而导致此时出现无限循环。 (你在下一次迭代中再次测试同一个角色——结果相同——而且场景永远不会改变....)
正如所有其他非常好的和非常正确的答案所建议的那样,只需使用一个单独的循环计数器变量(或指针)并递增它以迭代您的字符串。
你可以做的另一件事来验证你的逻辑(同时输出 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);
}
例子Use/Output
$ ./bin/ltrcountcs50-1
Text: 123.abc-456_def_*.*_789
123abc456def789, 15
将计数移入函数
您可以轻松地将字母数字字符的计数移动到一个函数中。将字符串传递给函数的一个好处是函数接收到的指针是 main()
指针的副本(C 是按值传递)。这使您可以简单地使用参数和 return 字母数字字符的计数进行迭代,例如
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
。如果您不更改函数中传递的值,则传递为 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));
}
例子Use/Output
$ ./bin/ltrcountcs50-1
Text: 123.abc-456_def_*.*_789
123.abc-456_def_*.*_789, 15
但是在这里,使用该函数会导致在 main()
中打印整个原始字符串。您可以根据需要调整输出。