【嵌入式 C 语言实战】字符串单词统计功能详解(附完整代码解析)

28 阅读7分钟

【嵌入式 C 语言实战】字符串单词统计功能详解(附完整代码解析)

大家好,我是学嵌入式的小杨同学。在嵌入式开发中,字符串处理是高频需求(如串口数据解析、配置项解析等),今天带大家解析一份完整的字符串单词个数统计代码,理解其核心逻辑、实现细节以及嵌入式场景下的优化建议。

一、代码整体功能概述

这份代码实现了一个核心功能:接收用户输入的字符串,统计其中的单词个数(以空格作为单词分隔符),具备输入处理、字符串截断、单词计数三大核心模块,代码简洁高效,适合入门字符串处理学习。

二、逐模块详细解析

1. 头文件引入说明

c

运行

#include <stdio.h>
#include <string.h>
  • stdio.h:提供标准输入输出函数支持,如printf(打印)、fgets(读取字符串);
  • string.h:提供字符串处理函数支持,如strcspn(用于截断换行符),这是 C 语言字符串处理的必备头文件。

2. 核心函数:count_words 单词统计函数

这是整个程序的核心,负责实现单词个数的统计逻辑,采用状态标记法实现,效率为 O (n)(n 为字符串长度,仅需遍历一次字符串)。

c

运行

int count_words(const char *str) {
    int count = 0;       // 单词计数器,记录最终单词个数
    int in_word = 0;     // 状态标记:0表示当前处于单词外(空格/字符串开头),1表示当前处于单词内

    // 遍历字符串,直到遇到字符串结束符'\0'
    while (*str != '\0') {
        // 情况1:当前字符是空格,标记为"单词外"状态
        if (*str == ' ') {
            in_word = 0; 
        } 
        // 情况2:当前字符不是空格,且处于"单词外"状态(说明遇到了新单词的开头)
        else if (in_word == 0) 
        {
            count++;      // 单词计数+1
            in_word = 1;  // 标记为"单词内"状态,避免同一单词重复计数
        }
        str++;  // 指针后移,遍历下一个字符
    }
    return count;
}
关键细节解析
  1. 函数参数const char *str:使用const修饰字符串指针,表明函数内部不会修改字符串内容,是 C 语言的良好编码习惯,提升代码安全性和可读性;
  2. 状态标记in_word:核心设计亮点,避免了对每个单词的重复遍历,仅通过 0/1 状态切换,就能准确识别新单词的开头,高效且简洁;
  3. 遍历逻辑while (*str != '\0'):通过指针遍历字符串,无需知道字符串长度,符合 C 语言字符串以'\0'结尾的特性;
  4. 计数规则:仅当 “遇到非空格字符” 且 “当前处于单词外” 时,才进行计数,确保了多个连续空格不会被误判为多个单词分隔符。
注意点

该函数当前仅支持单个空格作为单词分隔符,若输入多个连续空格(如"hello world"),仍能正确统计为 2 个单词,但不支持制表符\t、换行符\n等其他分隔符(后续可优化)。

3. 主函数:main 输入处理与功能调用

主函数负责搭建用户交互流程,完成字符串输入、预处理、单词统计和结果输出。

c

运行

int main() {
    char str[1024];  // 定义字符数组,用于存储用户输入的字符串,容量1024足够日常使用
    printf("请输入字符串:");
    
    // 步骤1:读取用户输入的字符串
    fgets(str, sizeof(str), stdin);
    
    // 步骤2:截断字符串末尾的换行符(关键处理)
    str[strcspn(str, "\n")] = '\0';
    
    // 步骤3:调用单词统计函数,获取结果
    int res = count_words(str);
    
    // 步骤4:输出统计结果
    printf("单词个数:%d\n", res);
    return 0;
}
核心步骤详解
  1. 字符串读取:fgets 函数的使用

    • 选用fgets而非scanffgets支持读取包含空格的字符串,而scanf遇到空格会停止读取,无法满足 “统计多单词” 的需求;
    • 参数说明:str(存储输入的字符数组)、sizeof(str)(读取的最大长度,避免数组越界)、stdin(从标准输入(终端)读取数据);
    • 安全特性:fgets会自动在字符串末尾添加'\0',无需手动添加,有效避免数组越界风险。
  2. 换行符截断:strcspn 函数的核心作用

    • 问题背景:fgets读取用户输入时,会将用户按下的Enter键(换行符\n)也读取到字符串中,例如输入"hello world"str中实际存储的是"hello world\n",换行符会被count_words函数判定为 “非空格字符”,可能影响统计结果;
    • strcspn(str, "\n")功能:计算str中首次出现'\n'的位置索引(返回值为换行符在字符串中的下标);
    • 处理逻辑:str[strcspn(str, "\n")] = '\0':将换行符所在位置替换为字符串结束符'\0',实现 “截断换行符” 的效果,确保字符串格式正确。
  3. 结果输出:调用count_words函数,打印统计结果,完成整个流程。

三、编译运行与测试案例

1. 编译命令(Linux/macOS)

将代码保存为word_count.c,在终端执行以下命令:

bash

运行

gcc word_count.c -o word_count
./word_count

2. 测试案例与预期结果

案例 1:正常输入(单个空格分隔)

plaintext

请输入字符串:hello world
单词个数:2
案例 2:多个连续空格分隔

plaintext

请输入字符串:I love  embedded  C
单词个数:4
案例 3:首尾带空格

plaintext

请输入字符串:  this is a test  
单词个数:4
案例 4:空字符串(直接按 Enter)

plaintext

请输入字符串:
单词个数:0

四、嵌入式开发优化建议(贴合实际应用场景)

  1. 优化字符数组容量,避免内存浪费

    • 嵌入式设备内存资源受限,无需定义 1024 长度的字符数组,可根据实际需求调整(如char str[64]),满足需求的同时减少内存占用;
    • 若需要处理不定长字符串,可采用 “静态内存池” 替代固定数组,避免动态分配malloc导致的内存碎片化。
  2. 支持多种分隔符,提升实用性

    • 实际嵌入式场景中,字符串分隔符可能包含\t(制表符)、\n(换行符)、,(逗号)等,可修改count_words函数的判断逻辑,支持多分隔符:

    c

    运行

    // 优化:判断是否为分隔符(空格、制表符)
    if (*str == ' ' || *str == '\t') {
        in_word = 0;
    }
    
  3. 添加输入长度校验,提升健壮性

    • fgets最多读取sizeof(str)-1个字符(预留'\0'的位置),若用户输入超出数组容量,会截断字符串,可添加校验逻辑,提示用户输入过长:

    c

    运行

    // 检查是否读取了完整的输入(是否包含换行符)
    if (str[strcspn(str, "\n")] != '\n' && strlen(str) == sizeof(str)-1) {
        printf("警告:输入字符串过长,已截断!\n");
    }
    
  4. 避免栈溢出,采用全局 / 静态存储

    • 嵌入式设备的栈空间较小(通常几 KB),main函数中的局部字符数组str存储在栈上,若长度过大可能导致栈溢出,可改为全局数组或静态数组(存储在全局 / 静态存储区):

    c

    运行

    // 全局数组,避免栈溢出
    char str[1024];
    int main() {
        // ... 后续逻辑不变
    }
    
  5. 添加内存释放逻辑(若使用动态分配)

    • 若后续优化为动态分配字符串内存,使用完毕后必须调用free释放内存,避免内存泄漏,确保嵌入式系统长期稳定运行。

五、总结

  1. 这份单词统计代码的核心是 **in_word状态标记法 **,仅遍历一次字符串即可完成统计,效率高效,是字符串处理的经典设计;
  2. fgets+strcspn的组合是安全读取带空格字符串的最佳实践,有效避免了换行符和数组越界问题;
  3. 嵌入式开发中使用字符串处理功能,需重点关注内存占用、健壮性、栈溢出三大问题,兼顾功能实现和系统稳定性;
  4. 该代码可作为嵌入式字符串解析的基础模板,后续可扩展为配置项解析、串口数据解析等实用功能。

我是学嵌入式的小杨同学,关注我,解锁更多嵌入式 C 语言字符串处理的进阶技巧!