You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,提问作者ᛉᛉᛉ ᛉᛉᛉ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 17:12:06