程序添加了一个看似无处不在的字符串
program adds on a string seemingly out of nowhere
我正在编写一个代码,从 stdin 读取一个输入文件,并输出完全相同的内容(到 stdout),除了按照以下方式,以准确的顺序替换在“字典”中找到的任何单词:
- 如果出现与字典中存储的关键字完全相同的单词,则找到相应的值对并将其打印出来。
- 如果正确大写的单词(例如 Thomas,首字母大写,其他字母小写)是字典中的有效键,则打印出相应的值对
- 如果小写版本是一个有效的密钥,打印出它对应的值
- 如果没有匹配项,就按原样打印出来。
(所有非字母字符都只是“正常”打印出来。)
我遇到的一个问题是,当我执行 (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=]';
}
编辑 作为对评论中问题的回复:
不能合并strcpy
和tolower
,但可以先复制字符串,然后就地修改字符。
示例:
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 */
}
我正在编写一个代码,从 stdin 读取一个输入文件,并输出完全相同的内容(到 stdout),除了按照以下方式,以准确的顺序替换在“字典”中找到的任何单词:
- 如果出现与字典中存储的关键字完全相同的单词,则找到相应的值对并将其打印出来。
- 如果正确大写的单词(例如 Thomas,首字母大写,其他字母小写)是字典中的有效键,则打印出相应的值对
- 如果小写版本是一个有效的密钥,打印出它对应的值
- 如果没有匹配项,就按原样打印出来。 (所有非字母字符都只是“正常”打印出来。)
我遇到的一个问题是,当我执行 (2) 时,不知何故,一个字符 ('U') 被标记到“字符串”或 copy2 数组的末尾,当我正在测试“IPSUM”(全部上限)。
例如,查看以下输出:
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=]';
}
编辑 作为对评论中问题的回复:
不能合并strcpy
和tolower
,但可以先复制字符串,然后就地修改字符。
示例:
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 */
}