为什么我的C函数不能处理标点符号?

gmxoilav  于 2023-03-22  发布在  其他
关注(0)|答案(4)|浏览(130)

我在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;

我决定将输入转换为全大写,因为因为像Ag这样的字符与大写/非大写对应字符具有相同的值,我认为将其转换为大写会更简单,这样逻辑更简单,更快,更有效地编写。在我的脑海中也更有意义。然而,当我使用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是专为字母字符工作,它应该排除特殊字符,使他们的值为零。有人能对哪里出了问题有什么建议吗?

aurhwmvo

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的情况,这是可以的,但完全可移植的解决方案需要额外的工作。)

7uzetpgm

7uzetpgm2#

当你向toupper()传递一个标点符号字符时,它将返回该值不变。
然后,您的代码将其用作POINTS数组的数组索引,该数组只有26个条目......导致 undefined behavior
你需要修改代码,使它跳过任何不是大写或小写字母的字符,这是你的尝试和“正确”代码之间的关键区别。
不过,在Scrabble游戏的上下文中,最好的做法可能是用返回值零“保释”compute_score(),因为标点字符无效。
我会这么做

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++)
    {
        if(!isalpha(word[i]))
        {
            // not a valid scrabble word
            return 0;
        }
        score = score + POINTS[toupper(word[i]) - 65];
    }
    return score;
}
oo7oh9g9

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中定义)。
在这段代码中

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'];
    }
}

仅处理“C”区域设置中的字母。
在此代码片段中

//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];
}

处理所有符号。
你可以这样写

//Convert array of chars to uppercase and solve
for (int i = 0, N = strlen(word); i < N; i++)
{
    if ( isalpha( ( unsigned char )word[i] ) )
    {
        score = score + POINTS[toupper(word[i]) - 'A'];
    }
}
2ekbmq32

2ekbmq324#

为什么我的C函数不能处理标点符号?
因为代码使用POINTS[toupper(word[i]) - 65];,并且当word[i]是标点字符时,它尝试访问POINTS[]之外的数组。对于非字母的toupper(word[i])返回word[i],导致数组索引在[0...25]之外。这最终导致 undefined behavior(UB)。
另一种方法是为所有字符指定POINTS[]

// Non-specified initialized result in zeroes for remaining elements.
int points[UCHAR_MAX + 1] = { ['A'] = 1, ['a'] = 1, ['B'] = 3, ['b'] = 3,
    // 23 more pairs
    ['Z'] = 10, ['z'] = 10};

...
const unsigned char *ch = (const unsigned char *) word;
while (*ch) {
  score += points[*ch];
  ch++;
}

不需要int数组来存储小值。

// int points[UCHAR_MAX + 1] = { ['A'] = 1, ['a'] = 1, ['B'] = 3, ...
signed char points[UCHAR_MAX + 1] = { ['A'] = 1, ['a'] = 1, ['B'] = 3, ...

相关问题