当前位置: 首页 > news >正文

【C语言常用字符串解析】

总结一下在 C 语言中用于字符串解析(特别是从文件中读取行并提取数据)的常用函数、

核心任务: 通常是从文件中读取一行文本(一个字符串),然后从这个字符串中提取出需要的数据(比如数字、单词等)。

常用函数总结:

  1. fgets() - 读取行

    • 头文件: <stdio.h>

    • 原型: char *fgets(char *str, int n, FILE *stream);

    • 作用: 从指定的文件流 stream 中读取一行,并将其存储到 str 指向的字符数组中。

    • 行为:

      • 最多读取 n-1 个字符(为末尾的空字符 \0 留出空间)。

      • 在读到换行符 \n、文件结束符 (EOF) 或达到 n-1 个字符时停止。

      • 如果读到了换行符 \n,它会被存储在字符串 str 中。

      • 总会在读取的字符后面添加一个空字符 \0。

      • 安全: 防止缓冲区溢出,因为读取的字符数有上限。

    • 返回值: 成功时返回 str 指针;如果遇到文件结尾且未读取任何字符,或者发生错误,则返回 NULL。——不需要额外接受值等于,会返回在str里面

    • 解析相关: 获取需要解析的原始字符串行的主要方式。 注意: 通常需要手动去除末尾可能存在的换行符 \n。

      char buffer[256];
      if (fgets(buffer, sizeof(buffer), fp) != NULL) {// 移除可能的换行符buffer[strcspn(buffer, "\n")] = '\0'; // 查找第一个换行符并替换为\0// 现在 buffer 包含了一行没有换行符的文本,可以进行解析
      }
    • strcspn() (来自 <string.h>) 在这里用来查找第一个换行符的位置。

  2. strlen() - 获取字符串长度

    • 头文件: <string.h>

    • 原型: size_t strlen(const char *str); (size_t 通常是 unsigned int 或 unsigned long)

    • 作用: 计算字符串 str 的长度,即第一个空字符 \0 之前的字符数。

    • 行为: 不包括结尾的 \0。

    • 返回值: 字符串的长度。

    • 解析相关: 虽然不直接解析内容,但在处理字符串(如检查是否为空、设置循环边界等)时非常有用。

  3. atoi() - 字符串转整数 (ASCII to Integer)

    • 头文件: <stdlib.h>

    • 原型: int atoi(const char *str);

    • 作用: 将表示整数的字符串 str 转换为 int 类型。

    • 行为:

      • 跳过开头的空白字符(如空格、制表符)。

      • 读取连续的数字字符,直到遇到非数字字符或字符串末尾 \0。

      • 不进行错误检查! 如果字符串不是有效的整数表示(例如是空的、"abc"、或者超出 int 范围),行为是:

        • 无法转换或为空:通常返回 0。你无法区分输入是 "0" 还是无效输入。

        • 超出范围:行为未定义(可能溢出并得到错误结果)。

    • 返回值: 转换后的 int 值,或者在无法转换时返回 0。

    • 解析相关: 用于从字符串片段中提取整数。仅适用于简单情况且不关心错误处理时。

  4. atof() - 字符串转双精度浮点数 (ASCII to Float)

    • 头文件: <stdlib.h>

    • 原型: double atof(const char *str);

    • 作用: 将表示浮点数的字符串 str 转换为 double 类型。

    • 行为:

      • 类似 atoi,跳过空白,读取数字、小数点、可选的 e 或 E 指数部分。

      • 同样不进行错误检查!

        • 无法转换或为空:通常返回 0.0。你无法区分输入是 "0.0" 还是无效输入。

        • 超出范围:行为未定义。

    • 返回值: 转换后的 double 值,或者在无法转换时返回 0.0。

    • 解析相关: 用于从字符串片段中提取浮点数。仅适用于简单情况且不关心错误处理时。

  5. strtod() - 字符串转双精度浮点数 (更健壮)

    • 头文件: <stdlib.h>

    • 原型: double strtod(const char *nptr, char **endptr);

    • 作用: 将字符串 nptr 的初始部分转换为 double 值。

    • 行为:

      • 比 atof 更强大、更安全。

      • endptr 是一个二级指针。如果它不是 NULL,strtod 会将指向转换结束后的第一个字符的指针存储在 *endptr 中。这允许你:

        • 检查是否发生了转换(如果 *endptr == nptr,则没有字符被转换)。

        • 继续从 *endptr 开始解析字符串的剩余部分。

      • 进行错误检查:

        • 如果发生上溢,返回 HUGE_VAL(或 -HUGE_VAL),并将全局变量 errno 设置为 ERANGE(需要 #include <errno.h> 和 #include <math.h>)。

        • 如果发生下溢,返回 0.0,并将 errno 设置为 ERANGE。

        • 如果无法转换,返回 0.0,并且 *endptr 会等于 nptr。

    • 返回值: 转换后的 double 值。

    • 解析相关: 强烈推荐使用此函数代替 atof 来进行字符串到 double 的转换,因为它提供了错误检测和更精细的控制。类似地,还有 strtof() (转 float) 和 strtold() (转 long double),以及 strtol() / strtoul() (转 long/unsigned long,代替 atoi)。

      char *line = "3.14 1.59 rest";
      char *end;
      double val1, val2;
      errno = 0; // 清除 errnoval1 = strtod(line, &end);
      if (errno == ERANGE || line == end) { /* 处理错误 */ }
      printf("First value: %f, Remaining string: '%s'\n", val1, end);// 继续解析 end 指向的剩余部分
      line = end; // 更新 line 指针
      errno = 0;
      val2 = strtod(line, &end);if (errno == ERANGE || line == end) { /* 处理错误 */ }
      printf("Second value: %f, Remaining string: '%s'\n", val2, end);
  6. strtok() - 分割字符串 (Tokenize)

    • 头文件: <string.h>

    • 原型: char *strtok(char *str, const char *delim);

    • 作用: 将字符串 str 分割成一系列的“令牌”(tokens),这些令牌由 delim 字符串中包含的任何一个字符分隔开。

    • 行为:

      • 会修改原始字符串 str! 它会在找到的分隔符位置写入 \0。因此,你通常需要一个可写的字符串副本。

      • 非可重入,非线程安全: 它使用一个内部静态指针来记住下次调用的位置。

      • 第一次调用: str 参数是要分割的字符串,函数返回第一个令牌的指针,如果没有令牌则返回 NULL。

      • 后续调用: str 参数必须是 NULL,函数会从上次停止的位置继续查找下一个令牌,返回令牌指针或 NULL。

    • 返回值: 指向下一个令牌的指针,或者在没有更多令牌时返回 NULL。

    • 解析相关: 用于按分隔符(如空格、逗号)拆分字符串。例如,拆分一行坐标 "x1 y1 x2 y2 ..."。

      char line[] = "10.0 20.5 30.0 15.0"; // 可写副本
      const char *delimiters = " "; // 按空格分割
      char *token;token = strtok(line, delimiters);
      while (token != NULL) {printf("Token: %s\n", token);// 在这里可以用 strtod() 将 token 转换为 doubledouble coord = strtod(token, NULL); // 简单用法,忽略错误检查printf("Coordinate: %f\n", coord);token = strtok(NULL, delimiters); // 获取下一个 token
      }
    • 注意: 因为它修改原字符串且非线程安全,有时会避免使用它,尤其是在复杂或多线程程序中。可以考虑使用 sscanf 或手动循环配合 strtod。

  7. sscanf() - 从字符串读取格式化输入

    • 头文件: <stdio.h>

    • 原型: int sscanf(const char *str, const char *format, ...);

    • 作用: 类似于 scanf,但是从字符串 str 而不是标准输入 stdin 读取数据。

    • 行为: 根据 format 字符串中的格式说明符,尝试从 str 中解析数据并存储到后续的参数(必须是指针)中。

    • 返回值: 成功赋值的参数个数。如果输入在第一次赋值前就结束或失败,返回 EOF。

    • 解析相关: 非常强大,可以一次性解析多个不同类型的值,只要它们的格式是预期的。

      char data[] = "Point 1 10.5 25.3";
      char label[10];
      int id;
      double x, y;int count = sscanf(data, "%s %d %lf %lf", label, &id, &x, &y);if (count == 4) {printf("Parsed: Label=%s, ID=%d, X=%.2f, Y=%.2f\n", label, id, x, y);
      } else {printf("Parsing failed or incomplete. Items matched: %d\n", count);
      }

    • 在循环中解析坐标序列: 可以结合 %n 实现。%n 不消耗输入,而是将在该点消耗的字符数写入其对应的 int* 参数。这可以帮助你移动解析的“光标”。

      char coords[] = "1.1 2.2 3.3 4.4 5.5 6.6";
      char *ptr = coords;
      int offset;
      double val;
      int items_read;while (sscanf(ptr, "%lf%n", &val, &offset) == 1) {printf("Read coordinate: %f\n", val);ptr += offset; // 移动指针到已解析部分的后面// 跳过可能的空格while (*ptr == ' ') {ptr++;}if (*ptr == '\0') break; // 到达字符串末尾
      }

如何选择?

  • 读取文件行: 始终使用 fgets。

  • 获取字符串长度: strlen。

  • 字符串转数字:

    • 优先使用 strtod, strtol, strtoul 因为它们提供错误检查和更好的控制。

    • atoi, atof 仅用于非常简单、不需错误处理的场景(或快速原型开发),生产代码中应避免。

  • 分割字符串:

    • strtok: 简单,但修改原串且非线程安全。适用于简单场景且拥有可写副本时。

    • sscanf: 适合格式相对固定的输入,可以一次性读取多个值。不修改原串。

    • 循环 + strtod: 最灵活、最健壮的方法,尤其当分隔符复杂或需要精细错误处理时。通过 strtod 的 endptr 可以精确控制解析位置。

例子:

多边形坐标解析任务(一行包含多个空格分隔的浮点数),以下两种方法比较常用:

  1. fgets + strtok + strtod:

    • fgets 读取行。

    • 创建行的可写副本。

    • 用 strtok 以空格为分隔符循环获取每个坐标字符串 (token)。

    • 在循环内用 strtod 将每个 token 转换为 double 并进行错误检查。

  2. fgets + 循环 + strtod:

    • fgets 读取行。

    • 使用一个指针 char *current_pos 指向当前解析位置(初始指向行首)。

    • 在循环中:

      • 调用 val = strtod(current_pos, &end_pos)。

      • 检查错误 (errno, current_pos == end_pos)。

      • 如果成功,存储 val。

      • 更新 current_pos = end_pos,让下一次循环从这里开始。

      • 如果 current_pos 指向 \0,结束循环。

相关文章:

  • 解锁 C++26 的未来:从语言标准演进到实战突破
  • 负载均衡深度实践:基于Nginx+Keepalived的高可用方案与Zabbix监控设计
  • archlinux wine 运行windows程序
  • Python学习笔记(第三部分)
  • 人工智能对未来工作的影响
  • 三生原理的离散生成逻辑如何与复分析结合?
  • Python字典(dict)详解:从创建到操作全掌握
  • 产品设计三板斧与抓住事物本质的关键意义
  • 前端面试每日三题 - Day 21
  • 三元运算符与扩展运算符
  • 正则表达式:精准匹配,高效处理文本
  • (C题|社交媒体平台用户分析问题)2025年第二十二届五一数学建模竞赛(五一杯/五一赛)解题思路|完整代码论文集合
  • jupyterlab建议安装的两个插件
  • 《软件设计师》复习笔记(11.1)——生命周期、CMM、开发模型
  • react学习笔记3——基于React脚手架
  • 数字智慧方案6166丨智慧医养结合大数据平台方案(50页PPT)(文末有下载方式)
  • yum源配置文件CentOS-Base.repo完整内容
  • Python数据分析课程实验-1
  • pycharm安装的插件怎么显示在右侧
  • 深入解析三大查找算法:线性查找、二分查找与哈希查找的原理与应用
  • 国台办:民进党当局所谓“对等尊严”,就是企图改变两岸同属一中
  • 湖北鄂州通报4所小学学生呕吐腹泻:供餐企业负责人被采取强制措施
  • 当初没有珍惜巴特勒的热火,被横扫出局后才追悔莫及
  • 监狱法修订草案提请全国人大常委会会议审议
  • 修订占比近30%收录25万条目,第三版《英汉大词典》来了
  • 马上评|“AI神医宇宙”欺诈,连演员都不请了