我在cs50 pset Week 2中尝试做一个简单的拼字游戏,但是函数int compute_score(string word)
无法处理使用标点符号的输入,即使它与正确答案大致相同,只需使用较少的代码行,将所有输入转换为大写。下面是代码,但您真正需要查看的是我上面命名的函数:
#include <ctype.h>
#include <cs50.h>
#include <stdio.h>
#include <string.h>
// Points assigned to each letter of the alphabet
int POINTS[] = { 1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10 };
int compute_score(string word);
int main()
{
// Get input words from both players
string word1 = get_string("Player 1: ");
string word2 = get_string("Player 2: ");
// Score both words
int score1 = compute_score(word1);
int score2 = compute_score(word2);
// TODO: Print the winner
if (score1 > score2)
{
printf("Player 1 wins!\n");
}
else if (score1 < score2)
{
printf("Player 2 wins!\n");
}
else
{
printf("Tie!\n");
}
}
int compute_score(string word)
{
// TODO: Compute and return score for string
//Initialize score
int score = 0;
//Convert array of chars to uppercase and solve
for (int i = 0, N = strlen(word); i < N; i++)
{
score = score + POINTS[toupper(word[i]) - 65];
}
return score;
}
在这之前,我在单个字符上使用toupper
时遇到了麻烦,直到我看了一个视频,解释了使用ASCII图表的逻辑以及如何迭代字符串中的字符。所以在for循环中,我写道:
//Convert array of chars to uppercase and solve
for (int i = 0, N = strlen(word); i < N; i++)
{
score = score + POINTS[toupper(word[i]) - 65];
}
return score;
我决定将输入转换为全大写,因为因为像A
和g
这样的字符与大写/非大写对应字符具有相同的值,我认为将其转换为大写会更简单,这样逻辑更简单,更快,更有效地编写。在我的脑海中也更有意义。然而,当我使用check 50时,除了有标点符号的内容(最后有一个例外),所有内容都被显示为绿色。下面是终端测试显示的内容:
terminal results
现在我完全不明白,因为在我看来,这和正确答案几乎是一样的,那就是:
for (int i = 0, N = strlen(word); i < N; i++)
{
if (isupper(word[i]))
{
score += POINTS[word[i] - 'A'];
}
else if (islower(word[i]))
{
score += POINTS[word[i] - 'a'];
}
}
我不知道为什么它不工作。我想,出于某种原因,它是分级标点符号。这是没有意义的,因为既然toupper
是专为字母字符工作,它应该排除特殊字符,使他们的值为零。有人能对哪里出了问题有什么建议吗?
4条答案
按热度按时间aurhwmvo1#
... toupper被设计为只处理字母字符,它应该排除特殊字符,将其值渲染为零。
toupper
不会更改非字母字符。toupper('!')
的计算结果与'!
'相同,而不是零。toupper
在C 2018 www.example.com中指定76.4.2.2,其中第3段说(粗体添加):如果参数是一个
islower
为真的字符,并且有一个或多个对应的字符,如当前区域设置所指定的,isupper
为真,则toupper
函数返回一个对应的字符(对于任何给定的区域设置,总是相同的字符);否则,参数返回不变。即使
toupper
对非字母字符的结果为零,表达式POINTS[toupper(word[i]) - 65]
的计算结果也将为POINTS[0 - 65]
,这不是您想要的结果。(此外,除非在特殊情况下,例如在字符集之间进行转换,否则不要为'A'
编写65
。在普通源代码中,使用'A'
作为“A”的代码。)您必须编写代码来测试非字母字符,并且不添加它们的分数。
(Also注意,使用
x - 'A'
并不是一种完全可移植的将字母转换为0到25之间的值的方法,因为C标准不要求字母的字符代码是连续的。对于简单的学生项目和您知道使用ASCII的情况,这是可以的,但完全可移植的解决方案需要额外的工作。)7uzetpgm2#
当你向
toupper()
传递一个标点符号字符时,它将返回该值不变。然后,您的代码将其用作
POINTS
数组的数组索引,该数组只有26个条目......导致 undefined behavior。你需要修改代码,使它跳过任何不是大写或小写字母的字符,这是你的尝试和“正确”代码之间的关键区别。
不过,在Scrabble游戏的上下文中,最好的做法可能是用返回值零“保释”
compute_score()
,因为标点字符无效。我会这么做
oo7oh9g93#
来自C标准(7.4.1.2 isalpha函数)
2 isalpha函数测试isupper或islower为true的任何字符,或者测试iscntrl、isdigit、ispunct或isspace都不为true的特定于区域设置的字母字符集中的任何字符。200)在“C”区域设置中,isalpha仅对isupper或islower为true的字符返回true。
和(7.4.1.7 islower函数)
2 islower函数测试任何小写字母字符,或者特定于区域设置的字符集中的一个字符,对于该字符集,iscntrl、isdigit、ispunct或isspace都不为true。在“C”区域设置中,islower仅对小写字母返回true(如5.2.1中定义的)。
和(7.4.1.11 isupper函数)
2 isupper函数测试任何大写字母字符,或特定于区域设置的字符集中iscntrl、isdigit、ispunct或isspace均不为true的字符。在“C”区域设置中,isupper仅对大写字母返回true(如5.2.1中定义)。
在这段代码中
仅处理“C”区域设置中的字母。
在此代码片段中
处理所有符号。
你可以这样写
2ekbmq324#
为什么我的C函数不能处理标点符号?
因为代码使用
POINTS[toupper(word[i]) - 65];
,并且当word[i]
是标点字符时,它尝试访问POINTS[]
之外的数组。对于非字母的toupper(word[i])
返回word[i]
,导致数组索引在[0...25]之外。这最终导致 undefined behavior(UB)。另一种方法是为所有字符指定
POINTS[]
。不需要
int
数组来存储小值。