判断数字字符——Character.isDigit(char ch)实现源码详解

本文深入剖析了Java中Character.isDigit方法的实现原理,通过源码分析了解字符与数字的ASCII对应关系,并展示了Character.getType和CharacterDataLatin1的角色。核心在于通过ASCII码值判断字符是否为数字,关键在于理解内部的数组A及其生成过程。

目录


函数的使用

☀♫♪ 使用场景
举例:返回字符串s中的所有数字之和。易知s中的数字分别为{3,2,3,5,3,5,2,0},其和为23
s = " f3@$23%5Q3Zh52=0"
源码如下:
    private static int NumbSum(String s){
        int sum = 0;
        char [] data = s.toCharArray();
        for(int i = 0;i < data.length;i++){
            if(Character.isDigit(data[i])){
                sum += data[i] - '0';//将char数字转换为int
            }
        }
        return sum;
    }
☀♫♪ 实现结果

在这里插入图片描述


源码解析

挨个判断已转化为字符数组中的数据是否为数字是解题的关键,即必须理解的Character.isDigit(data[i])的实现过程。在解析源码之前需要掌握字符与数字之间的对应关系,在ASCII编码中,字符与数字的对应关系如下表所示:

在这里插入图片描述

☀♬☾ Character.isDigit(char ch)源码解析
在Java.lang包中Character类中找到isDigit(char ch)方法。isDigit(char ch)方法根据ascii码表将字符类型ch转换为int类型并传给Character类中的isDigit,通过查阅ascii码表知道 ‘0’-‘9’字符对应整数区间为48~57.
    /**
     * General category "Nd" in the Unicode specification.
     * @since   1.1
     */
	public static final byte DECIMAL_DIGIT_NUMBER        = 9;
	
  	public static boolean isDigit(char ch) {
        return isDigit((int)ch);
    }


    public static boolean isDigit(int codePoint) {
        return getType(codePoint) == Character.DECIMAL_DIGIT_NUMBER;
    }

通过源码可以分析出,函数的实现过程为:首先将传入的字符转换为对应的ASCII码值,然后将ASCII码值传入getType方法,看是否返回的值为9,若为9,则标识传入的字符为数字字符,否则反之。那么。getType方法到底如何实现判断字符类型,为什么返回的值要与9进行对比呢?

☀♬☾ Character.getType(int codePoint)源码解析
    public static int getType(int codePoint) {
        return CharacterData.of(codePoint).getType(codePoint);//注:此getType方法为CharacterData.of返回实例的getType方法。
    }

getType方法调用了同在java.lang包中CharacterData的静态方法of(int ch)。

    static final CharacterData of(int ch) {
        if (ch >>> 8 == 0) {     // fast-path
            return CharacterDataLatin1.instance;
        }else{
        		……
      	}

of方法返回了CharacterData的子类CharacterDataLatin1的实例

static final CharacterDataLatin1 instance = new CharacterDataLatin1();

通过源码可以分析出,Character.getType(int codePoint)方法的实现结果为,返回一个CharacterDataLatin1的实例,然后调用CharacterDataLatin1的getType方法。返回CharacterDataLatin1实例instance时,会自动执行CharacterDataLatin1类中的静态代码块中的代码给int类型数组A赋值。

	static final int A[] = new int[256];
	
    static {
                { // THIS CODE WAS AUTOMATICALLY CREATED BY GenerateCharacter:
            char[] data = A_DATA.toCharArray();
            assert (data.length == (256 * 2));
            int i = 0, j = 0;
            while (i < (256 * 2)) {
                int entry = data[i++] << 16;
                A[j++] = entry | data[i++];
            }
        }

    }  

其中,A_DATA为类中定义的字符串(注:下面不是乱码为512个16进制表示的数)。静态代码将A_DATA字符串转换为字符数组,然后通过循环,将相邻两个4个字节表示的数字拼接为8个字节表示的数并存储在数组A中。

static final String A_DATA =
“\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800”+
“\u100F\u4800\u100F\u4800\u100F\u5800\u400F\u5000\u400F\u5800\u400F\u6000\u400F”+
“\u5000\u400F\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800”+
“\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F”+
“\u4800\u100F\u4800\u100F\u5000\u400F\u5000\u400F\u5000\u400F\u5800\u400F\u6000”+
“\u400C\u6800\030\u6800\030\u2800\030\u2800\u601A\u2800\030\u6800\030\u6800”+
“\030\uE800\025\uE800\026\u6800\030\u2000\031\u3800\030\u2000\024\u3800\030”+
“\u3800\030\u1800\u3609\u1800\u3609\u1800\u3609\u1800\u3609\u1800\u3609\u1800”+
“\u3609\u1800\u3609\u1800\u3609\u1800\u3609\u1800\u3609\u3800\030\u6800\030”+
“\uE800\031\u6800\031\uE800\031\u6800\030\u6800\030\202\u7FE1\202\u7FE1\202”+
“\u7FE1\202\u7FE1\202\u7FE1\202\u7FE1\202\u7FE1\202\u7FE1\202\u7FE1\202\u7FE1”+
“\202\u7FE1\202\u7FE1\202\u7FE1\202\u7FE1\202\u7FE1\202\u7FE1\202\u7FE1\202”+
“\u7FE1\202\u7FE1\202\u7FE1\202\u7FE1\202\u7FE1\202\u7FE1\202\u7FE1\202\u7FE1”+
“\202\u7FE1\uE800\025\u6800\030\uE800\026\u6800\033\u6800\u5017\u6800\033\201”+
“\u7FE2\201\u7FE2\201\u7FE2\201\u7FE2\201\u7FE2\201\u7FE2\201\u7FE2\201\u7FE2”+
“\201\u7FE2\201\u7FE2\201\u7FE2\201\u7FE2\201\u7FE2\201\u7FE2\201\u7FE2\201”+
“\u7FE2\201\u7FE2\201\u7FE2\201\u7FE2\201\u7FE2\201\u7FE2\201\u7FE2\201\u7FE2”+
“\201\u7FE2\201\u7FE2\201\u7FE2\uE800\025\u6800\031\uE800\026\u6800\031\u4800”+
“\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u5000\u100F”+
“\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800”+
“\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F”+
“\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800”+
“\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F\u4800\u100F”+
“\u3800\014\u6800\030\u2800\u601A\u2800\u601A\u2800\u601A\u2800\u601A\u6800”+
“\034\u6800\030\u6800\033\u6800\034\000\u7005\uE800\035\u6800\031\u4800\u1010”+
“\u6800\034\u6800\033\u2800\034\u2800\031\u1800\u060B\u1800\u060B\u6800\033”+
“\u07FD\u7002\u6800\030\u6800\030\u6800\033\u1800\u050B\000\u7005\uE800\036”+
“\u6800\u080B\u6800\u080B\u6800\u080B\u6800\030\202\u7001\202\u7001\202\u7001”+
“\202\u7001\202\u7001\202\u7001\202\u7001\202\u7001\202\u7001\202\u7001\202”+
“\u7001\202\u7001\202\u7001\202\u7001\202\u7001\202\u7001\202\u7001\202\u7001”+
“\202\u7001\202\u7001\202\u7001\202\u7001\202\u7001\u6800\031\202\u7001\202”+
“\u7001\202\u7001\202\u7001\202\u7001\202\u7001\202\u7001\u07FD\u7002\201\u7002”+
“\201\u7002\201\u7002\201\u7002\201\u7002\201\u7002\201\u7002\201\u7002\201”+
“\u7002\201\u7002\201\u7002\201\u7002\201\u7002\201\u7002\201\u7002\201\u7002”+
“\201\u7002\201\u7002\201\u7002\201\u7002\201\u7002\201\u7002\201\u7002\u6800”+
“\031\201\u7002\201\u7002\201\u7002\201\u7002\201\u7002\201\u7002\201\u7002”+
“\u061D\u7002”;

☀♬☾ CharacterDataLatin1.getType(int codePoint)源码解析
    int getType(int ch) {
        int props = getProperties(ch);
        return (props & 0x1F);
    }

    int getProperties(int ch) {
        char offset = (char)ch;
        int props = A[offset];
        return props;
    }

根据ACCII编码规则,‘0’-‘9’数字字符对应整数为48-57,将其作为数组偏移量,对应A_DATA字符串的

\u1800\u3609\u1800\u3609\u1800\u3609\u1800\u3609\u1800\u3609\u1800”+
“\u3609\u1800\u3609\u1800\u3609\u1800\u3609\u1800\u3609

其对应A数组中的值为
A[48]:18003609
A[49]:18003609
………………
因此,若ch为数字字符,则getProperties(ch)会返回props = 0x18003609,则

props & ox1F = 9

总结

判断字符是否为数字字符,是根据其ASCII码值在CharacterDataLatin1()中返回值是否为9实现的。CharacterDataLatin1()中存在数组A,以数字字符对应ACII码为数组偏移量返回的值最后两个字节与0x1F结果为9.
<think>好的,我需要帮助用户编写一个检查字符串是否为数字的函数。首先,用户的问题涉及多种编程语言,但根据提供的引用内容,他们提到了C语言和Python的例子。我需要先确定用户主要需要哪种语言,但用户没有明确说明,所以我应该涵盖这两种常见的情况。 首先,回忆C语言的相关知识。在C语言中,字符串是以字符数组的形式存在的,每个字符需要逐个检查。根据引用[1],用户之前接触过字符串转换整数(atoi)的函数,可能希望类似的逻辑来判断是否为数字。标准的atoi函数会跳过前导空格,处理可选的正负号,然后检查后续字符是否都是数字,直到非数字字符出现。但用户需要的是判断整个字符串是否为数字,而不仅仅是转换。因此,需要更严格的检查,确保每个字符都是数字,可能允许前导符号,但不能有其他字符,包括空格。 然后,考虑Python的情况。Python中处理字符串更灵活,可能有多种方法,比如使用内置方法或正则表达式。引用[4]中提到了清理字符串后判断回文,这里可能需要类似的处理,比如去除无关字符,但用户的问题更直接,需要判断整个字符串是否为数字,可能包括整数和浮点数。 接下来,分析用户提供的引用。引用[2]提到统计字符串中的数字字符之和,这可能涉及到逐个字符检查是否为数字(即'0'到'9'之间的字符)。但用户的问题更严格,需要整个字符串构成一个有效的数字,包括可能的符号和小数点。 现在,需要设计函数的逻辑。对于C语言,函数需要遍历每个字符,检查是否符合数字的格式。可能的步骤包括: 1. 处理前导空格(如果有的话,视需求而定。用户可能希望严格判断,不允许空格,所以可能不需要处理空格)。 2. 检查可选的正负号。 3. 检查后续字符是否为数字,可能包括一个小数点,并且小数点后必须有数字。 4. 对于科学计数法,可能还需要处理'e'或'E',但用户的问题可能不涉及这么复杂的情况,需要明确用户需求。假设用户只需要基本的整数或浮点数。 但根据用户的问题,他们可能想判断字符串是否完全由数字字符组成,即整数。例如,"1234"返回是,而"12a3"则不是。或者是否允许符号和小数点?需要进一步明确,但用户引用了atoi函数,该函数允许符号,但会忽略前导空格,并在遇到非数字时停止。但用户想要的是整个字符串是否为数字,所以可能需要更严格的条件。 例如,字符串"+123"是否为数字?可能认为是,但需要看需求。类似地,"123.45"是否算数字?如果是的话,函数需要检查是否有且仅有一个小数点,并且小数点前后至少有一个数字。 因此,可能需要分情况处理: 对于整数: - 可选的正负号开头。 - 后续全是数字。 对于浮点数: - 可选的正负号。 - 数字序列,可能包含一个小数点,且小数点前后至少有一个数字。 如果用户需要支持这两种情况,函数会更复杂。但根据用户的问题描述,可能只需要判断是否全为数字字符(即整数形式,不允许符号或小数点),或者更通用的数字判断。 另外,引用[1]中的atoi函数处理的是整数,允许符号,但会跳过前导空格,并在遇到非数字时停止转换。但用户的问题可能希望整个字符串都符合数字格式,不允许其他字符。 现在,我需要根据这些考虑,分别写出C语言和Python的实现。 在C语言中,可以编写一个函数,遍历每个字符: 1. 检查第一个字符是否是'+'或'-',如果是,则跳过。 2. 后续每个字符必须是数字(0-9)。 3. 整个字符串不能为空或仅有符号。 对于浮点数,还需要检查是否有小数点,并且前后有数字。 例如,函数is_number的C语言实现可能如下: bool is_number(const char *str) { if (str == NULL || *str == '\0') return false; int i = 0; if (str[i] == '+' || str[i] == '-') i++; bool has_digit = false; bool has_dot = false; while (str[i] != '\0') { if (str[i] == '.') { if (has_dot) return false; has_dot = true; } else if (isdigit(str[i])) { has_digit = true; } else { return false; } i++; } return has_digit; } 这个函数允许可选符号,允许一个小数点,但至少需要一个数字。例如,".123"会被认为是true,"123."在有些情况下可能不被接受,但在此函数中是允许的,可能根据需求调整。例如,要求小数点前后至少有一个数字。 但在用户的问题中,可能希望判断字符串是否为整数,即不允许小数点。例如,引用[2]中的统计数字字符之和,其中每个字符数字字符。此时,函数更简单: bool is_integer(const char *str) { if (str == NULL || *str == '\0') return false; int i = 0; if (str[i] == '+' || str[i] == '-') i++; if (str[i] == '\0') return false; // 只有符号 while (str[i] != '\0') { if (!isdigit(str[i])) { return false; } i++; } return true; } 这可能更符合用户的需求,即判断是否为整数,允许符号。 在Python中,可以利用异常处理,尝试将字符串转换为整数或浮点数,如果成功则为数字。例如: def is_number(s): try: float(s) return True except ValueError: return False 但这种方法可能允许科学计数法或其他格式,比如"1e3"。如果用户需要更严格的检查,比如不允许小数点或科学记数法,可以调整。或者使用正则表达式: import re def is_number(s): pattern = r'^[-+]?[0-9]*\.?[0-9]+$' return re.match(pattern, s) is not None 但这也允许像".5"或"123."这样的情况,可能需要调整正则表达式。 或者对于整数: def is_integer(s): return s.lstrip('+-').isdigit() and len(s) > 0 and (s.count('+') + s.count('-')) <=1 and (s[0] in '+-' or s.isdigit()) 但更简单的方式是尝试转换为整数: def is_integer(s): try: int(s) return True except ValueError: return False 但像"123a"这样的字符串会抛出异常,因此返回False。 用户的问题可能想要的是判断字符串是否为数字,可能包括整数和浮点数。因此,可能需要分开处理。 综上,根据用户的需求,给出C语言和Python的实现,分别判断字符串是否为整数或浮点数。需要明确说明函数的限制,例如是否允许前导符号,是否允许小数点等。 现在,结合用户的引用,引用[1]是关于C语言的atoi,引用[2]是统计数字字符的和,引用[3][4]是判断回文,引用[5]是Python素数函数。用户的问题是关于判断字符串是否为数字的函数,可能更偏向C语言,因为引用中有C语言的例子较多,但不确定。因此,应该同时提供两种语言的实现。 在回答中,需要结构清晰,分点说明,可能先介绍C语言的方法,再Python的方法。同时,按照用户的要求,在回答末尾生成相关问题。</think>### 如何编写检查字符串是否为数字的函数 #### 1. C语言实现 在C语言中,需要逐个字符验证字符串是否符合数字格式,支持以下特征: - 可选前导正负号 - 必须包含至少一个数字 - 允许整数或浮点数格式(可选小数点) **代码示例**: ```c #include <ctype.h> #include <stdbool.h> bool is_number(const char *str) { if (str == NULL || *str == '\0') return false; int i = 0; // 处理符号 if (str[i] == '+' || str[i] == '-') i++; // 标志:是否包含数字、是否包含小数点 bool has_digit = false, has_dot = false; while (str[i] != '\0') { if (str[i] == '.') { if (has_dot) return false; // 多个小数点无效 has_dot = true; } else if (isdigit(str[i])) { has_digit = true; } else { return false; // 非数字字符 } i++; } return has_digit; // 至少一个数字才有效 } ``` *说明*:此函数支持整数(如`"-123"`)和浮点数(如`"3.14"`),但会拒绝无效格式(如`"12.3.4"`)[^1]。 --- #### 2. Python实现 Python可通过正则表达式或异常捕获灵活实现: - **正则表达式方法**:精确匹配数字模式 - **异常捕获方法**:直接尝试转换类型 **正则表达式示例**: ```python import re def is_number(s): pattern = r'^[-+]?(\d+\.?\d*|\.\d+)$' # 支持整数、浮点数、前导符号 return re.fullmatch(pattern, s) is not None # 测试 print(is_number("+123")) # True print(is_number("12.3")) # True print(is_number(".45")) # True print(is_number("12a")) # False ``` **异常捕获示例**: ```python def is_number(s): try: float(s) # 兼容整数和浮点数 return True except ValueError: return False ``` *说明*:异常捕获方法支持科学计数法(如`"1e3"`),而正则表达式可根据需求调整[^4]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值