【问题标题】:program adds on a string seemingly out of nowhere程序添加了一个看似无处不在的字符串
【发布时间】:2021-11-13 23:07:19
【问题描述】:

我正在编写一个代码,该代码从标准输入读取输入文件,并输出完全相同的内容(到标准输出),除了按以下方式替换“字典”中找到的任何单词,按确切顺序:

  1. 如果出现与字典中作为键存储的单词完全相同的单词,则找到相应的值对并将其打印出来。
  2. 如果正确大写的单词(例如,Thomas,首字母大写,其他所有字母小写)是字典中的有效键,则打印出相应的值对
  3. 如果小写版本是有效键,则打印出其对应的值
  4. 如果没有匹配项,则按原样打印出来。 (所有非字母字符都只是“正常”打印出来。)

我一直遇到的一个问题是,当我在做 (2) 时,当我测试“ IPSUM”(全部大写)。

例如,看这个输出: 我的输出在带有“”表示应该是什么。根据我检查的顺序,由于 IPSUM 不在字典中(请参阅本文末尾的字典内容),它转到(2) IPSUM 应该成为 Ipsum 的地方,它应该打印出相应的值伊普苏姆。但相反,我得到了 IpsumU,所以字典无法识别这个词。但我不确定“U”是从哪里来的,因为输入是准确的

IPSUM(全部大写)。

谁能帮我弄清楚我的代码可能有什么问题?

//for reference:
typedef struct HashBucketEntry {
  void *key;
  void *data;
  struct HashBucketEntry *next;
} HashBucketEntry;

typedef struct HashTable {
  int size;
  unsigned int (*hashFunction)(void *);
  int (*equalFunction)(void*, void*);
  HashBucketEntry  **buckets;
} HashTable;

//We have a Hashtable *dictionary.

void processInput() {
 //char c;
 int c; 
 int i = 0;
 //char * word = (char *) malloc(60 * sizeof(char));
 char word[60]; 
 while (c = getchar()) {
     if (isalpha(c)) {
         word[i] = c;
         i++;
     } else {
         word[i] = '\0';
         if (word[0] != '\0') {
             //char * copy = (char *) malloc(60 * sizeof(char));
             char copy[60];
             strcpy(copy, word);
            
             unsigned int location = (dictionary->hashFunction)(copy) % (dictionary->size);
             char * word_in_dict;
             if (dictionary->buckets[location] != NULL) {
                word_in_dict = (char *) dictionary->buckets[location]->data;
             } else {
                word_in_dict = NULL;
             }
             char copy2[60];
             copy2[0] = toupper(copy[0]);
             for(int i = 1; copy[i]; i++){
                 copy2[i] = tolower(copy[i]);
             }
             unsigned int location2 = (dictionary->hashFunction)(copy2) % (dictionary->size);
             char * word_in_dict2;
             if (dictionary->buckets[location2] != NULL) { //somehow this is NULL when IPSUM, even though copy2 has correct string
                word_in_dict2 = (char *) dictionary->buckets[location2]->data; 
             } else {
                word_in_dict2 = NULL;
             }
 
             char copy3[60];
             for(int i = 0; copy[i]; i++){
                 copy3[i] = tolower(copy[i]);
             }
             unsigned int location3 = (dictionary->hashFunction)(copy3) % (dictionary->size);
             char * word_in_dict3;
             if (dictionary->buckets[location3] != NULL) {
                word_in_dict3 = (char *) dictionary->buckets[location3]->data;
             } else {
                word_in_dict3 = NULL;
             }

             if (word_in_dict != NULL) {
                 fprintf(stdout, "%s", word_in_dict);
             } else if (word_in_dict2 != NULL) {
                 fprintf(stdout, "%s", word_in_dict2);
             } else if (word_in_dict3 != NULL) {
                 fprintf(stdout, "%s", word_in_dict3);
             } else {
                 //fprintf(stdout, "%s", copy);
                 printf("%s", copy);

             }
             putchar(c);
             i = 0;
         } else if (c != EOF) {
             putchar(c);
         } else {
             break;
         }
     }
 }
}

字典只包含以下条目:

ipsum i%#@!
fubar fubar
IpSum XXXXX24
Ipsum YYYYY211

任何帮助将不胜感激!

响应答案更新: 我将 copy2 的代码更改为:

for(j = 1; j < strlen(copy); j++) {
     if (j < sizeof(copy2)) {
          copy2[j] = tolower(copy[j]);
     }
} 

(并做了与 copy3 类似的事情)。第二种情况有效,但现在第三种情况失败;只有当我改变第二种情况而不是第三种情况时,事情似乎才有效。有谁知道为什么会这样?

【问题讨论】:

  • 首先,char c 必须是int c,因为fgetc() 返回int。认为“字符必须是char”(或“数字必须是整数”)是初学者的错误。其次,循环结束条件不正确。应该是while ((c = getchar()) != EOF)
  • ...我看到你已经处理了循环体末尾的EOF,使代码变得笨拙且难以理解。
  • 请以文字形式发布Minimal Reproducible Example,最短的完整代码,显示您尝试过的内容。
  • edit您的问题,并以文本形式显示(复制和粘贴)预期和实际,并明确哪个是什么。 IPSUM (all cap). 是唯一的输入吗?
  • @WeatherVane 我已经修复了代码,希望它更好一点!

标签: arrays c string pointers char


【解决方案1】:

用于创建输入字符串的修改副本的代码,例如

             char copy2[60];
             copy2[0] = toupper(copy[0]);
             for(int i = 1; copy[i]; i++){
                 copy2[i] = tolower(copy[i]);
             }

不复制终止的'\0'。由于自动变量没有被隐式初始化,相应的内存可能包含任何可能显示为尾随字符的数据(来自先前的循环周期或来自不相关的代码)。您必须在字符串的最后一个字符之后附加一个 '\0' 字符。

如果数组边界内没有'\0',则当您将数组作为字符串访问时,此错误可能会导致对数组的越界访问。 (未定义的行为)

如果输入字符串太长,您的代码本身可能会导致越界访问。您应该添加一个检查以防止访问i &gt;= sizeof(copy2) 处的数组元素。

我建议是这样的:

             char copy2[60];
             copy2[0] = toupper(copy[0]);
             /* avoid reading past the end of an empty string */
             if(copy[0]) {
                 for(int i = 1; copy[i] && (i < sizeof(copy)-1); i++){
                     copy2[i] = tolower(copy[i]);
                 }
                 /* variable i will already be incremented here */
                 copy2[i] = '\0';
             }

编辑作为对评论中问题的回应:

strcpytolower 不能合并,但可以先复制字符串,然后就地修改字符。

例子:

    char copy2[60];
    if(strlen(copy) y sizeof(copy2)) {
        strcpy(copy2, copy);
        copy2[0] = toupper(copy2[0]);
        if(copy[0]) {
            /* The length has been checked before, no need to check again here */
            for(int i = 1; copy[i]; i++) {
                copy2[i] = tolower(copy2[i]);
            }
            /* the string is already terminated */
        }
    } else {
        /* string too long, handle error */
    }

或者用截断而不是报告错误

    char copy2[60];
    strncpy(copy2, copy, sizeof(copy)-1);
    copy2[sizeof(copy2)-1] = '\0';
    copy2[0] = toupper(copy2[0]);
    if(copy[0]) {
        /* A long string would have been truncated before, no need to check the length here */
        for(int i = 1; copy[i]; i++) {
            copy2[i] = tolower(copy2[i]);
        }
        /* the string is already terminated */
    }

【讨论】:

  • 谢谢你的建议——这就是你的意思吗(请看我原始帖子的结尾,我已经更新了一些代码 sn-p?我似乎仍然有同样的问题,但是,我仍然有那个 U 标记的地方。
  • 不,@norwegian_forest,您添加的代码不是此答案描述的正确实现。当copy[j] 引用字符串copy 的终止符时,该代码中的循环终止。因此copy2 的终止符应该进入copy2[j],但您将其放入copy2[j+1]
  • 另外,@norwegian_forest,如何通过使用内置的strcpy() 函数,或者strcat()strncat() 来简化您的代码并避免为此类错误留出位置?
  • @JohnBollinger 哦,我不知道这是可能的。基于快速搜索,在第一个字符之后是否会出现类似 ``` strcpy(copy2[1], tolower(copy[1])) ``` 的内容?
  • 另外,在看到您的建议之前,我对代码进行了一些其他更改,并且有一个问题——您能再参考一下原始帖子吗?
猜你喜欢
  • 2015-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-26
  • 1970-01-01
  • 2021-09-07
  • 1970-01-01
  • 2013-10-27
相关资源
最近更新 更多