字符串中的大写字符不能转换为小写,减去它们的 ASCII 值不会使它们在字母索引中

Posted

技术标签:

【中文标题】字符串中的大写字符不能转换为小写,减去它们的 ASCII 值不会使它们在字母索引中【英文标题】:Uppercase characters in a string cannot be converted to lowercase and subtracting their ASCII value doesnt make them in alphabetical index 【发布时间】:2018-08-25 17:59:44 【问题描述】:

我是一名尝试学习编码的初学者。 目前我正在上CS50课程。我遇到了 Vigenere 密码问题;请在下面的 github 链接上查看我的代码。

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

#define ASCII_VALUE_LOWER 97
#define ASCII_VALUE_UPPER 65
#define NR_OF_LETTERS 26

int main(int argc, string argv[])

    char key[strlen(argv[1]) + 1];
    strcpy(key, argv[1]);
    int keyLen = strlen(key);

    for (int k = 0; k < keyLen; k++)
    
        if (!isalpha(key[k]))
        
            printf("ERROR: Secret key has to be alphabetical string, program will be terminated now!\n");
            return 1; // main should return 1 (signify an error)
        
        //converting key letters to respective values
        if (isupper(key[k]))
            
                key[k] -= ASCII_VALUE_UPPER;
            
            key[k] -= ASCII_VALUE_LOWER;
    
    //if program is executed without an argument, or with more than one arguments
    if (argc != 2)
    
        printf("ERROR: You need to give a secret key as an argument, program will be terminated now!\n");
        return 1; // main should return 1 (signify an error)
    
    else
    
        string plaintext = get_string("plaintext: "); //get a plaintext from a user using cs50 custom function from their library
        int stringLen = strlen(plaintext);
        int keyIndex = 0;

        for (int j = 0; j < keyLen; j++)
        

        

        //for each character in the plaintext string
        for (int i = 0; i < stringLen; i++)
        
            //check if is alphabetic (tolower, toupper)
            if (isalpha(plaintext[i]))
            
                //cypher_character = (plain_character + key_character)% 26
                if (islower(plaintext[i]))
                
                    keyIndex %= keyLen;
                    plaintext[i] = ((plaintext[i] - ASCII_VALUE_LOWER + key[keyIndex]) % NR_OF_LETTERS) + ASCII_VALUE_LOWER;
                
                else
                
                    plaintext[i] = ((plaintext[i] - ASCII_VALUE_UPPER + key[keyIndex]) % NR_OF_LETTERS) + ASCII_VALUE_UPPER;
                
                keyIndex++;
            
            //else leave as is
        

        //print ciphertext in a format "ciphertext: " + ciper
        printf("ciphertext: %s\n", plaintext);
        return 0;
    

问题如下:

    如果您在键中以大写字母形式传递参数,则值很奇怪并且转换不起作用。这个想法是在字符串键中获取每个字符,如果大写则减去 65,如果小写则减去 97,因此它们的 ASCII 值将变为 0 - 25。然后我可以在 Vigenere 密码公式中使用它们:cipher[i_index] = (plaintext[i_index] + key[j_index]) % 26

    不处理缺少argv[1],即使有一个IF条件(!argc == 2),所以如果你什么都不通过,它不应该通过。

    "failed to execute program due to segmentation fault".
    

我已经尽我所能尝试了一切,我很累,也许明天解决方案会立即弹出。 求你给我一些提示,可能不会透露全部,但可能会指导我,让我从中学习。

【问题讨论】:

你为什么不把所有的东西都改成大写然后减去65? 你试过调试你的程序吗? ...然后减去'A' 就是@RobertHarvey 的意思。 您的第二个问题出现是因为当您检查是否argc != 2 时,您已经访问了argv[1],因此假设argc &gt;= 2。如果调用者没有提供参数,则argv[1] 超出了数组的末尾,并且评估该表达式会导致未定义的行为。 @Joe Farrell,感谢您的提醒,这解决了问题! 【参考方案1】:
    if (isupper(key[k]))
        
            key[k] -= ASCII_VALUE_UPPER;
        
        key[k] -= ASCII_VALUE_LOWER;

如果字符为大写,则减去ASCII_VALUE_UPPER。然后,无论如何,它都会减去ASCII_VALUE_LOWER。从周围的代码中,我假设您的意思是:

    if (isupper(key[k])) 
        key[k] -= ASCII_VALUE_UPPER;
     else 
        key[k] -= ASCII_VALUE_LOWER;
    

【讨论】:

谢谢!过去几个小时我一直在盯着它,试图找出问题出在哪里……遗憾的是,我对调试器的效率不是很高,所以我无法确定问题的根源。这使得程序功能更好,但仍然存在一些错误。例如,在单词 BaRFoo 上给出键 BaZ 会给出 CaQFon,但它应该是 CaQGon。如果我将 FFFfff 放在同一个键(BaZ)上,它会正常工作,给 GFEgfe。 使用printf(),而不是与调试器斗争(这是一项重要的技能,但我们暂时把它放在一边)。在此过程中的每一步,打印出当前值是什么,并确保它们是您想要的。 好的,所以我按照你的建议做了,最后我找到了罪魁祸首。它在 if 条件内而不是在它之外放置的变量操作很糟糕。再次感谢您的帮助!【参考方案2】:

正如其他人所建议的那样,一切都已解决,如果有人好奇到底哪里出错了:

1.] @Rob Napier 指出的错误

for (int i = 0; i < stringLen; i++)
    
        //check if is alphabetic (tolower, toupper)
        if (isalpha(plaintext[i]))
        
            keyIndex %= keyLen; // makes sure that keyIndex doesnt exceeds actual string length
            //cypher_character = (plain_character + key_character)% 26
            if (islower(plaintext[i]))
            
                plaintext[i] = ((plaintext[i] - ASCII_VALUE_LOWER + key[keyIndex]) % NR_OF_LETTERS) + ASCII_VALUE_LOWER;
            
            else
            
                plaintext[i] = ((plaintext[i] - ASCII_VALUE_UPPER + key[keyIndex]) % NR_OF_LETTERS) + ASCII_VALUE_UPPER;
            
            keyIndex++;
        
        //else leave as is
    

keyIndex %= keyLen;被放置在它下方的 if 条件内,因此它不会在 FOR 循环的每次迭代中执行。

2.] @Joe Farrel 回答: 因为当我检查是否 argc != 2 时,我已经访问了 argv[1],因此假设 argc >= 2。如果调用者没有提供参数,那么 argv[1] 已经过了数组并评估该表达式会导致未定义的行为。 - 所以我首先将 if 条件移到 main 中。

【讨论】:

以上是关于字符串中的大写字符不能转换为小写,减去它们的 ASCII 值不会使它们在字母索引中的主要内容,如果未能解决你的问题,请参考以下文章

C语言编写程序,将一个字符串中的大写字母转换为对应的小写字母,小写字母转换为对应的大写字母,并统计数

如何将数组中的字符串转换为小写[关闭]

大写字母转换为小写字母

从键盘获取字符,字符大小写转换

java怎样将大写字母转换成小写字母

在python中,如何将一个字符串中的小写字母全部转换为大写?