C语言如何判断字符串是否为数字?

99ANYc3cd6
预计阅读时长 31 分钟
位置: 首页 C语言 正文

逐个字符检查(最常用、最直观)

这种方法的核心思想是遍历字符串的每一个字符,检查它是否是数字字符('0'到'9'),如果发现任何一个非数字字符,就可以立即判定该字符串不是数字字符串。

c语言判断字符串是否为数字
(图片来源网络,侵删)

代码实现

#include <stdio.h>
#include <stdbool.h> // 为了使用 bool 类型
#include <ctype.h>   // 为了使用 isdigit() 函数
/**
 * @brief 判断字符串是否完全由数字组成
 * @param str 要检查的字符串
 * @return 如果是数字字符串返回 true,否则返回 false
 */
bool isNumericString(const char *str) {
    // 1. 检查空指针或空字符串
    if (str == NULL || *str == '\0') {
        return false;
    }
    // 2. 遍历字符串中的每一个字符
    for (int i = 0; str[i] != '\0'; i++) {
        // 3. 如果当前字符不是数字,则返回 false
        if (!isdigit(str[i])) {
            return false;
        }
    }
    // 4. 如果所有字符都检查完毕且都是数字,则返回 true
    return true;
}
int main() {
    const char *testStrings[] = {"12345", "0", "-123", "12a34", "12.34", "", NULL};
    int numTests = sizeof(testStrings) / sizeof(testStrings[0]);
    for (int i = 0; i < numTests; i++) {
        const char *s = testStrings[i];
        if (isNumericString(s)) {
            printf("字符串 \"%s\" 是一个数字字符串,\n", s ? s : "NULL");
        } else {
            printf("字符串 \"%s\" 不是一个数字字符串,\n", s ? s : "NULL");
        }
    }
    return 0;
}

代码解释

  1. 头文件:

    • stdio.h: 用于 printf 函数进行输出。
    • stdbool.h: 提供 truefalse 以及 bool 类型,使代码更清晰。
    • ctype.h: 提供 isdigit() 函数,用于检查一个字符是否是十进制数字(0-9)。
  2. 函数 isNumericString:

    • 参数: const char *str,使用 const 是一个好习惯,因为它表示函数不会修改传入的字符串指针,使用指针而不是数组,可以接受任何字符串字面量或字符数组。
    • 空指针/空字符串检查: if (str == NULL || *str == '\0'),这是必要的边界条件检查,如果传入 NULL(指针无效)或空字符串 ,直接返回 false
    • 循环遍历: for (int i = 0; str[i] != '\0'; i++),标准的C语言字符串遍历方式,直到遇到字符串结束符 \0 为止。
    • 字符检查: if (!isdigit(str[i]))isdigit() 函数检查 str[i] 是否在 '0' 到 '9' 之间,如果不是,isdigit() 返回0(假),逻辑非 后变为真,if 条件成立,函数立即返回 false
    • 成功返回: 如果循环正常结束,说明没有遇到任何非数字字符,此时可以安全地返回 true

优点

  • 简单直观: 逻辑非常容易理解。
  • 高效: 一旦发现非数字字符就立即返回,避免了不必要的遍历。
  • 标准库函数: 使用了标准库 isdigit(),可移植性好。

缺点

  • 功能有限: 这种方法只能判断纯整数字符串,它无法识别负号 、正号 、小数点 或科学计数法(如 1e5)。"-123""12.34" 会被判定为非数字字符串。

更健壮的检查(支持整数、浮点数、正负号)

如果需要判断字符串是否能被 atoiatofstrtol 等函数正确转换为数字,则需要更复杂的逻辑,这种方法不仅要检查字符,还要考虑字符的位置

代码实现

#include <stdio.h>
#include <stdbool.h>
#include <ctype.h>
bool isNumber(const char *str) {
    if (str == NULL || *str == '\0') {
        return false;
    }
    bool hasDigit = false;
    bool hasDot = false;
    bool hasE = false;
    // 处理正负号(只能在开头)
    if (*str == '+' || *str == '-') {
        str++;
    }
    // 遍历字符串
    while (*str != '\0') {
        if (isdigit(*str)) {
            hasDigit = true;
        } else if (*str == '.') {
            // 小数点不能出现多次,且不能在 'e' 或 'E' 之后出现
            if (hasDot || hasE) {
                return false;
            }
            hasDot = true;
        } else if (*str == 'e' || *str == 'E') {
            // 'e' 或 'E' 不能出现多次,且前面必须有数字
            if (hasE || !hasDigit) {
                return false;
            }
            hasE = true;
            // 'e' 后面必须跟一个整数,可能还有正负号
            str++;
            if (*str == '+' || *str == '-') {
                str++;
            }
            // 'e' 后面至少要有一个数字
            if (!isdigit(*str)) {
                return false;
            }
        } else {
            // 其他任何字符都表示无效
            return false;
        }
        str++;
    }
    // 字符串结束时,必须至少有一个数字
    return hasDigit;
}
int main() {
    const char *validNumbers[] = {"123", "+456", "-789", "3.14", "-0.5", "1e10", "2E-5", ".5", "1."};
    const char *invalidNumbers[] = {"abc", "12a34", "12.34.56", "e10", "1e", ".", "-", "+", ""};
    printf("--- 有效数字测试 ---\n");
    for (int i = 0; i < sizeof(validNumbers) / sizeof(validNumbers[0]); i++) {
        printf("%s: %s\n", validNumbers[i], isNumber(validNumbers[i]) ? "有效" : "无效");
    }
    printf("\n--- 无效数字测试 ---\n");
    for (int i = 0; i < sizeof(invalidNumbers) / sizeof(invalidNumbers[0]); i++) {
        printf("%s: %s\n", invalidNumbers[i], isNumber(invalidNumbers[i]) ? "有效" : "无效");
    }
    return 0;
}

代码解释

这个方法通过引入状态标志(hasDigit, hasDot, hasE)来跟踪已经遇到了哪些类型的字符,从而进行更严格的判断。

c语言判断字符串是否为数字
(图片来源网络,侵删)
  1. 初始状态: 处理可选的开头 或 。
  2. 循环体:
    • 数字: 标记 hasDigit = true
    • 小数点 :
      • 不能出现多次(if (hasDot))。
      • 不能在科学计数符之后出现(if (hasE))。
    • 科学计数符 eE:
      • 不能出现多次(if (hasE))。
      • 前面必须有数字(if (!hasDigit))。
      • e 后面必须跟一个整数(可能带 /-),所以需要额外检查。
  3. 结束条件: 如果遇到任何不支持的字符,立即返回 false
  4. 最终检查: 循环结束后,必须至少有一个数字(return hasDigit;),否则像 或 "e" 这样的字符串会被错误地判定为有效。

优点

  • 功能强大: 支持整数、浮点数、科学计数法,覆盖了大多数常见的数字格式。
  • 精确控制: 对数字的格式有非常精确的规则定义。

缺点

  • 复杂: 逻辑比方法一复杂得多,容易出错。
  • 维护成本高: 如果需要支持新的数字格式(如十六进制),修改起来比较麻烦。

利用标准库函数(推荐,最可靠)

对于大多数实际应用场景,最好的方法是让C标准库来帮你完成这个工作,你可以尝试将字符串转换为数字,然后检查转换后的剩余部分。

  • strtol (string to long): 用于转换长整型。
  • strtod (string to double): 用于转换双精度浮点型。

这种方法不仅能判断字符串是否是数字,还能告诉你转换后的值是多少。

代码实现 (使用 strtod)

#include <stdio.h>
#include <stdlib.h> // 为了使用 strtod
#include <errno.h>  // 为了使用 errno
#include <stdbool.h>
#include <ctype.h>  // 为了使用 isspace
bool isNumericByLibrary(const char *str) {
    if (str == NULL) {
        return false;
    }
    char *endptr; // 用于存储转换停止的位置
    // 重置 errno,因为 strtod 在转换失败时会设置它
    errno = 0;
    // 尝试将字符串转换为 double
    double val = strtod(str, &endptr);
    // 检查转换是否成功
    // 1. 转换后的值不能是 HUGE_VAL 或 HUGE_VAL_Overflow (表示溢出)
    // 2. errno 不能被设置为 ERANGE (表示溢出)
    // 3. endptr 必须指向字符串的末尾 '\0',说明整个字符串都被转换了
    if (errno != 0 || (val == 0.0 && str == endptr)) {
        // errno == ERANGE 表示溢出
        // val == 0.0 && str == endptr 表示没有进行任何转换(输入是 "abc")
        return false;
    }
    // 检查 endptr 是否指向字符串的末尾
    // 在 endptr 之前可以有空白字符,但之后不能有
    while (isspace((unsigned char)*endptr)) {
        endptr++;
    }
    return (*endptr == '\0');
}
int main() {
    const char *testStrings[] = {"123", "  -456.789  ", "+1e10", "12a34", "12.34.56", "abc", "", "123abc"};
    int numTests = sizeof(testStrings) / sizeof(testStrings[0]);
    for (int i = 0; i < numTests; i++) {
        const char *s = testStrings[i];
        if (isNumericByLibrary(s)) {
            printf("字符串 \"%s\" 是一个有效的数字字符串,\n", s);
        } else {
            printf("字符串 \"%s\" 不是一个有效的数字字符串,\n", s);
        }
    }
    return 0;
}

代码解释

  1. strtod(str, &endptr):

    • str: 要转换的字符串。
    • &endptr: 一个指向 char* 指针的地址。strtod 会将这个指针设置为指向它停止转换的位置。
    • 返回值: 转换得到的 double 值,如果转换失败(第一个字符就不是数字),它会返回 0
  2. 错误检查:

    c语言判断字符串是否为数字
    (图片来源网络,侵删)
    • errno: strtod 在发生溢出(结果太大或太小无法表示)时会设置 errnoERANGEerrno != 0 表示转换有问题。
    • val == 0.0 && str == endptr: 这是一个特殊情况,如果输入字符串是 "0"strtod 会成功转换,endptr 会指向 \0,但如果输入是 "abc"strtod 也会返回 0endptr 仍然指向字符串的开头(str),所以这个条件用来判断是否根本没有进行任何转换。
  3. 剩余字符检查:

    • while (isspace((unsigned char)*endptr)) endptr++;: strtod 会忽略字符串开头和结尾的空白字符,这行代码是为了跳过 endptr 指针后面的所有空白字符。
    • return (*endptr == '\0');: 这是最终判断。endptr 最终指向了字符串的结束符 \0,说明 strtod 成功转换了整个有效数字部分,没有留下任何非空白、非数字的字符。*endptr 不是 \0(输入是 "123abc"endptr 会指向 'a'),则说明字符串不是纯数字。

优点

  • 最可靠: 完全遵循C语言标准库的数字解析规则,处理了各种边界情况(如前导/后导空格、溢出等)。
  • 功能全面: 支持所有标准数字格式,包括科学计数法、正负号、小数点等。
  • 代码简洁: 相比手动实现复杂的逻辑,调用库函数的代码更短、更易读。
  • 附带额外信息: 不仅告诉你“是不是”,还告诉你“是什么值”。

缺点

  • 性能开销: 调用库函数并处理错误状态会比简单的字符循环稍慢,但在绝大多数应用中,这种差异可以忽略不计。
  • 副作用: 会修改 errno,需要手动重置。

总结与选择建议

方法 优点 缺点 适用场景
逐个字符检查 简单、直观、高效 功能有限,仅支持纯整数 需要快速判断字符串是否由0-9组成,不关心符号或小数点。
健壮检查 功能强大,支持多种数字格式 逻辑复杂,容易出错 需要自定义严格的数字格式验证,且不想依赖库函数的特定行为。
标准库函数 最可靠、最全面、代码简洁 有轻微性能开销,需处理errno 强烈推荐,绝大多数情况下,这是最佳选择,因为它能正确处理所有标准格式的数字字符串。

如何选择?

  • 如果你只是想判断字符串里是不是只有 '0' 到 '9':使用方法一
  • 如果你需要判断字符串是否能被 scanf("%d", ...)atoi 正确解析为整数:使用方法一(因为 atoi 也会忽略前导空格和正负号,但遇到非数字字符就停)。
  • 如果你需要判断字符串是否能被 scanf("%lf", ...)atof 正确解析为浮点数:使用方法三,这是最安全、最符合预期的做法。
  • 如果你在做面试题,题目要求“不使用库函数”:那么你需要实现方法二的逻辑来展示你的编程能力。
-- 展开阅读全文 --
头像
dede图集内容摘要如何生成?
« 上一篇 03-07
织梦手机自适应怎么用?快速配置指南?
下一篇 » 03-07

相关文章

取消
微信二维码
支付宝二维码

目录[+]