C语言Hangman游戏文件读取异常:\0字符与UTF-8编码问题
Hangman游戏文件读取问题修复
问题描述
实现从文本文件随机选取单词的Hangman游戏时,直接硬编码单词(如char wort[] = "Apfelbäumchen";)程序可正常运行,但从文件读取单词后出现两个核心问题:
- 读取的字符串长度比实际单词多1,实际是
fgets读取了行尾的换行符 - 德语特殊字符
ä、ü、ö无法正确比较,因编码不匹配
原问题代码
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #define MAX_WORD_LENGTH 50 char* WortAusDatei(const char* filename) { FILE* file = fopen(filename, "r"); if (file == NULL){ printf("Datei konnte nicht gefunden werden.\n"); return NULL; } srand(time(0)); int wordCount = 0; char word[MAX_WORD_LENGTH]; char* selectedWord = NULL; while (fgets(word, MAX_WORD_LENGTH, file) != NULL) { if (rand() % ++wordCount == 0) { selectedWord = strdup(word); //selectedWord = strcpy(word); } } fclose(file); printf("Debug: Das Wort heißt %s", selectedWord); printf("Debug: , es ist %d Buchstaben lang. \n" , strlen(selectedWord)); return selectedWord; } int main(){ const char* filename = "worte.txt"; char* wort = WortAusDatei(filename); printf ("%s", wort); char eingabe[100]; int leben = 10; int zeichenAnzahl = strlen(wort)-1; int eraten[strlen(wort)]; int i; anfang: printf("\n"); for (i = 0; i < strlen(wort) - 1; i++){ if (eraten[i] == 1){ printf("%c ", wort[i]); } else { printf("_ "); } } printf("\n\n"); scanf("%s", eingabe); if (strcmp(eingabe, wort) == 0){ printf("Gewonnen!\n"); return 0; } if (strlen(eingabe) == 1){ int i; int trefferanzahl = 0; for (i = 0; wort[i] != '\0'; i++){ if (wort[i] == eingabe[0]){ trefferanzahl++; printf("Treffer\n"); eraten[i] = 1; int alleEraten = 1; for (int i = 0; i < strlen(wort); i++){ if (eraten[i] != 1){ alleEraten = 0; break; } } if (alleEraten){ printf("Gewonnen, das Lösungswort ist: %s\n", wort); return 0; } } } if (trefferanzahl == 0){ leben--; printf("Du hast nurnoch %d Leben\n", leben); if (leben == 0){ printf("Verloren"); return 0; } } } else { printf("Eingabe ungültig\n"); } goto anfang; return 0; }
解决方案
1. 移除fgets读取的换行符
fgets会将每行末尾的\n(换行符)一同读取到字符串中,导致长度增加1,且后续完整单词比较会失败。处理方式:在复制字符串前,检查并移除换行符:
// 在strdup前添加换行符处理 size_t word_len = strlen(word); if (word_len > 0 && word[word_len - 1] == '\n') { word[word_len - 1] = '\0'; // 替换换行符为字符串结束符 } selectedWord = strdup(word);
2. 统一德语特殊字符编码
德语的ä、ü、ö如果以UTF-8编码存储,每个字符占2字节,而C语言默认的char是单字节,直接用==或strcmp比较会因字节不匹配失败。解决方法:
- 将文本文件
worte.txt保存为**ISO-8859-1(Latin-1)**编码,该编码中德语特殊字符均为单字节,与C的char完全兼容 - 若坚持使用UTF-8,需改用宽字符API(
wchar_t、fgetws、wcscmp等),但会增加代码复杂度
3. 其他代码修复
- 初始化变长数组:
int eraten[strlen(wort)]未初始化会导致随机值,改为循环初始化所有元素为0 - 优化随机数初始化:
srand(time(0))移到main函数开头,避免多次调用WortAusDatei时重复初始化 - 减少
strlen调用:将strlen(wort)的结果存到变量中,避免循环内重复计算 - 修复缓冲区溢出:
scanf("%s", eingabe)改为scanf("%99s", eingabe),限制输入长度防止溢出 - 内存泄漏修复:每次选中新单词时释放之前的内存
修改后的完整代码
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #define MAX_WORD_LENGTH 50 char* WortAusDatei(const char* filename) { FILE* file = fopen(filename, "r"); if (file == NULL){ printf("Datei konnte nicht gefunden werden.\n"); return NULL; } int wordCount = 0; char word[MAX_WORD_LENGTH]; char* selectedWord = NULL; while (fgets(word, MAX_WORD_LENGTH, file) != NULL) { // 移除换行符 size_t word_len = strlen(word); if (word_len > 0 && word[word_len - 1] == '\n') { word[word_len - 1] = '\0'; } if (rand() % ++wordCount == 0) { // 释放之前选中的单词,避免内存泄漏 if (selectedWord != NULL) { free(selectedWord); } selectedWord = strdup(word); } } fclose(file); printf("Debug: Das Wort heißt %s", selectedWord); printf("Debug: , es ist %d Buchstaben lang. \n" , strlen(selectedWord)); return selectedWord; } int main(){ srand(time(0)); // 只初始化一次随机数生成器 const char* filename = "worte.txt"; char* wort = WortAusDatei(filename); if (wort == NULL) { return 1; // 文件读取失败时退出 } printf ("%s\n", wort); char eingabe[100]; int leben = 10; int word_len = strlen(wort); int eraten[word_len]; // 初始化eraten数组为0 for (int i = 0; i < word_len; i++) { eraten[i] = 0; } anfang: printf("\n"); // 显示占位符 for (int i = 0; i < word_len; i++){ if (eraten[i] == 1){ printf("%c ", wort[i]); } else { printf("_ "); } } printf("\n\n"); scanf("%99s", eingabe); // 检查是否直接猜对完整单词 if (strcmp(eingabe, wort) == 0){ printf("Gewonnen!\n"); free(wort); return 0; } // 处理单个字符输入 if (strlen(eingabe) == 1){ int trefferanzahl = 0; for (int i = 0; wort[i] != '\0'; i++){ if (wort[i] == eingabe[0]){ trefferanzahl++; printf("Treffer\n"); eraten[i] = 1; } } // 检查是否所有字符都被猜中 int alleEraten = 1; for (int i = 0; i < word_len; i++){ if (eraten[i] != 1){ alleEraten = 0; break; } } if (alleEraten){ printf("Gewonnen, das Lösungswort ist: %s\n", wort); free(wort); return 0; } // 处理未命中情况 if (trefferanzahl == 0){ leben--; printf("Du hast nurnoch %d Leben\n", leben); if (leben == 0){ printf("Verloren! Das Lösungswort war: %s\n", wort); free(wort); return 0; } } } else { printf("Eingabe ungültig - bitte gib einen einzelnen Buchstaben oder das gesamte Wort ein\n"); } goto anfang; }
内容的提问来源于stack exchange,提问作者ᛉᛉᛉ ᛉᛉᛉ
相关产品推荐
相关产品推荐

