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

C语言二维字符数组单词匹配计数及strcmp使用问题求解

问题背景

我创建了两个用于存储单词的二维字符数组,示例初始化代码如下:

loadKey[25][30] = 
{
   {'J','a','v','a','\0'},
   {'P','y','t','h','o','n','\0'},
   {'C','+','+','\0'},
   {'H','T','M','L','\0'},
   {'S','Q','L','\0'}
   // ... 剩余20个单词
};
resume[189][30] = 
{
   {'L','a','l','a','\0'},
   {'H','i','h','i','h','i','\0'},
   {'C','+','+','\0'},
   {'Y','o','Y','o','\0'},
   {'S','Q','L','\0'}
   // ... 剩余184个单词
};

开发需求:将loadKey数组存储的25个关键词逐一与resume数组中的所有单词进行比对,统计loadKey内的关键词在resume中匹配成功的总次数。
尝试调用strcmp(loadKey[i], resume[j])实现比对逻辑时,遇到数组指针相关问题导致运行结果不符合预期,附完整程序代码如下,求排查问题、提供解决方案。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PAUSE myPause()
#define KEYWORD 25
#define WORDS 250
#define MAX_LETTER 30

//*********************************************
// 函数声明
void compAndCount(char loadKey[][MAX_LETTER], char resume[][MAX_LETTER]);
void myPause();
void readAndLoadKeyword(char loadKey[][MAX_LETTER]);

// 主函数
int main() {
    char loadKey[KEYWORD][MAX_LETTER];
    char resume[WORDS][MAX_LETTER];
    // 从keywords文件加载关键词到loadKey数组
    readAndLoadKeyword(loadKey);
    for (int j = 0; j < KEYWORD; j++)
    {
        puts(loadKey[j]);
    }
    puts("\n");
    // 比对并统计关键词在简历文件中的出现次数
    compAndCount(loadKey, resume);
}

// 函数定义
void compAndCount(char loadKey[][MAX_LETTER], char resume[][MAX_LETTER]) {
    FILE* fpr;
    fpr = fopen("resumes.txt", "r");
    int r = 0, count = 0, num = 0, res = 0;
    char temp;
    while ((temp = fgetc(fpr)) != EOF) {
        if (temp != ' ' && temp != '\n') {
            resume[res][r] = temp;
            r++;
        }
        else
        {
            resume[res][r] = '\0';
            r = 0;
            res++;
        }
    }
    printf("words in resume file %i\n", res);
    for (int j = 0; j < res; j++)
    {
        puts(resume[j]);
    }
    puts("\n");
/*
    // 比对统计方式1(认为有问题?)
    for (int i = 0; i < res; i++) {
        if (i < KEYWORD) {
            scanf(" %[^\n]", loadKey[i]);
        }   
        scanf(" %[^\n]", resume[i]);
    }
    for (int k = 0; k < KEYWORD; k++) {
        for (int l = 0; l < res; l++) {
            if (strcmp(loadKey[k], resume[l]) == 0)
                count++;
        }
    }*/
    /*
    // 比对统计方式2(认为有问题?)
    char key[MAX_LETTER] = {'\0'}, r[MAX_LETTER] = {'\0'};
    for (int i = 0; i < KEYWORD; i++) {
        strcpy(key, loadKey[i]);
        for (int l = 0; l < res; l++) {
            strcpy(r, resume[l]);
            if (strcmp(key, r) == 0)
                count++;
        }
    }
    */
    printf("Resume Rating: %i\n", count);
    fclose(fpr);
} // compAndCount函数结束

void myPause() {
    puts("\nPress ENTER to continue\n");
    exit(0);
}

void readAndLoadKeyword(char loadKey[][MAX_LETTER]) {
    FILE* fp;
    fp = fopen("keywords.txt", "r");
    char ch;
    int row = 0, col = 0;   
    if (fp == NULL) {
        puts("Not able to open keyword file!");
        PAUSE;
    }
    // 加载25个关键词和分隔逗号到line数组
    char line[181]; // 180个字符 + '\0'终止符
    fgets(line, 181, fp);
    puts(line);
    puts("\n");
    // 将line数组中的25个单词存入loadKey数组
    for (int i = 0; i < 180; i++) {
        ch = line[i];
        if (ch != ',') {
            loadKey[row][col] = ch;
            col++;
        }
        else {
            loadKey[row][col] = '\0';
            col = 0;
            row++;
        }
    }
    fclose(fp);
} // readAndLoadKeyword函数结束

问题排查结果

问题不是strcmp参数传错或者二维数组指针用法不对——二维数组作为函数形参传递时,loadKey[k]、resume[l]本身就是合法的char*类型指针,完全符合strcmp的入参要求。运行结果异常是以下5个逻辑bug导致的:

  • 缺少简历文件打开校验:compAndCount函数里打开resumes.txt后没有判断文件是否打开成功,如果文件路径错误、权限不足导致fopen返回NULL,后续fgetc操作空指针会直接触发内存访问错误,就是遇到的指针类报错。
  • 简历文件读取漏了最后一个单词的终止符:现有读取逻辑只有遇到空格、换行时才会给当前单词加\0终止符,循环读到文件末尾EOF时,最后一个单词不会触发终止符赋值逻辑,strcmp比对时会顺着数组地址一直读到栈上的随机垃圾值,必然返回错误的比对结果。
  • 关键词读取逻辑越界:readAndLoadKeyword里固定循环180次读取line数组内容,如果关键词行实际长度不足180,会读到line数组未初始化的垃圾值写入loadKey。
  • 最后一个关键词漏加终止符:关键词是用逗号分隔的,最后一个关键词末尾没有逗号,循环结束后不会给最后一个关键词补\0,同样会导致strcmp读到垃圾值比对失败。
  • fgetc返回值类型错误:fgetc返回值是int类型,用char类型变量存储会导致读取到值为0xFF的字符时和EOF(值为-1)判断冲突,提前结束文件读取。

修复方案

针对以上问题做对应修改即可,核心修改点如下:

  1. 所有文件打开操作后立刻加空指针校验,打开失败直接报错退出
  2. 读完简历文件的EOF后,判断如果当前有未收尾的单词(r>0),手动补\0终止符,单词计数加1,同时过滤连续空格、换行生成的空字符串
  3. 关键词读取时不要固定循环180次,循环到line数组的\0终止符就停止
  4. 关键词读取循环结束后,给最后一个关键词手动补\0终止符
  5. 取消比对逻辑的注释,两种写法都可以正常运行,不需要额外拷贝字符串到临时数组,直接传loadKey[k]和resume[l]给strcmp即可
  6. 增加单词长度防护,避免单个单词长度超过数组上限导致内存越界

修复后的核心代码片段参考:

// 修复后的compAndCount函数
void compAndCount(char loadKey[][MAX_LETTER], char resume[][MAX_LETTER]) {
    FILE* fpr;
    fpr = fopen("resumes.txt", "r");
    // 新增文件打开校验
    if (fpr == NULL) {
        puts("无法打开简历文件!");
        PAUSE;
    }
    int r = 0, count = 0, res = 0;
    int temp; // 修正fgetc返回值存储类型
    while ((temp = fgetc(fpr)) != EOF) {
        if (temp != ' ' && temp != '\n') {
            resume[res][r] = (char)temp;
            r++;
            // 长度防护,避免单词超过数组上限越界
            if (r >= MAX_LETTER - 1) {
                resume[res][r] = '\0';
                r = 0;
                res++;
            }
        }
        else
        {
            // 过滤连续空格/换行生成的空字符串
            if (r > 0) {
                resume[res][r] = '\0';
                r = 0;
                res++;
            }
        }
    }
    // 处理文件末尾最后一个未收尾的单词
    if (r > 0) {
        resume[res][r] = '\0';
        res++;
    }
    printf("简历文件中读取到单词数:%i\n", res);
    // 直接调用strcmp比对即可
    for (int k = 0; k < KEYWORD; k++) {
        for (int l = 0; l < res; l++) {
            if (strcmp(loadKey[k], resume[l]) == 0)
                count++;
        }
    }
    printf("简历匹配度:%i\n", count);
    fclose(fpr);
}

// 修复后的readAndLoadKeyword函数
void readAndLoadKeyword(char loadKey[][MAX_LETTER]) {
    FILE* fp;
    fp = fopen("keywords.txt", "r");
    char ch;
    int row = 0, col = 0;   
    if (fp == NULL) {
        puts("无法打开关键词文件!");
        PAUSE;
    }
    char line[181] = {0}; // 初始化全0,避免垃圾值干扰
    fgets(line, 181, fp);
    puts(line);
    puts("\n");
    int i = 0;
    // 循环到line的字符串结束符为止,不固定循环次数
    while (line[i] != '\0' && line[i] != '\n' && row < KEYWORD) {
        ch = line[i];
        if (ch != ',') {
            loadKey[row][col] = ch;
            col++;
            // 长度防护
            if (col >= MAX_LETTER -1) {
                loadKey[row][col] = '\0';
                col = 0;
                row++;
            }
        }
        else {
            loadKey[row][col] = '\0';
            col = 0;
            row++;
        }
        i++;
    }
    // 给最后一个关键词补终止符
    if (col > 0 && row < KEYWORD) {
        loadKey[row][col] = '\0';
    }
    fclose(fp);
}

内容的提问来源于stack exchange,提问作者C beginner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 17:39:22