程序添加了一个看似无处不在的字符串

program adds on a string seemingly out of nowhere

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

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

我遇到的一个问题是,当我执行 (2) 时,不知何故,一个字符 ('U') 被标记到“字符串”或 copy2 数组的末尾,当我正在测试“IPSUM”(全部上限)。

例如,查看以下输出: 我的输出在带有“<”的行中,“>”表示应该是什么。根据我检查的顺序,因为 IPSUM 不在字典中(字典内容见本 post 的末尾),它转到 (2) IPSUM 应该变成 Ipsum 的地方,它应该打印出相应的 Ipsum 值。但是我得到的是 IpsumU,所以字典无法识别这个词。但是我不确定 'U' 是从哪里来的,因为输入正好是

IPSUM (all cap).

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

//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] = '[=10=]';
         if (word[0] != '[=10=]') {
             //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 copy2[60];
             copy2[0] = toupper(copy[0]);
             for(int i = 1; copy[i]; i++){
                 copy2[i] = tolower(copy[i]);
             }

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

如果数组范围内没有 '[=14=]',当您将数组作为字符串访问时,此错误可能会导致对数组的越界访问。 (未定义的行为)

如果输入字符串太长,您的代码本身可能会导致越界访问。您应该添加一个检查以防止访问位于 i >= 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] = '[=11=]';
             }

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

不能合并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] = '[=13=]';
    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 */
    }