C标准库源码解剖(3):字符处理函数ctype.h和wctype.h

来源:互联网 发布:mongodb和mysql 编辑:程序博客网 时间:2024/05/16 10:34

    字符处理包括分类(即属性判断)和转换函数。ASCII字符主要可分类为控制字符、空白字符、可打印字符、数字字符、字母字符(大写和小写)、标点符号等。
    1、ctype.h:标准的属性判断函数有isalnum,isalpha,iscntrl,isdigit,isxdigit,isgraph,isprint,ispunct,islower,isupper,isspace, isblank(C99中引入)共12个函数。标准的属性转换函数有tolower和toupper。当然具体的实现中还会提供一些非标准函数作为扩展,如glibc的实现中提供了isctype,isascii,toascii。

    解释:
    (1)字符的所有属性类被封装在一个enum中,每个属性对应一个枚举常量。
    (2)在作为接口的头文件中,由于各个函数的类型相同,都接受int型字符,返回int型的值,因此原型声明可用宏__exctype(name)来完成,name为函数名,这样可以简化代码。
    (3)所有的属性判断函数的实现都是用宏__isctype(c, type)和返回数组指针的外部函数__ctype_b_loc()来完成。在实现文件ctype.c中可以看到,所有的判断函数都只有一条语句“return __isctype (c, type);”。这个函数式宏用来判断字符c是否具有属性type(为枚举常量),它直接以字符c为下标,获得__ctype_b_loc()数组相应位置处的元素,并与属性作逻辑与运算,若结果为非0,说明字符具有该属性,若结果为0则说明字符没有该属性。__ctype_b_loc()函数在glibc库的ctype-info.c文件中定义,它直接使用了glibc 2.0中已经实现的内置函数。可见,这里用宏来实现ctype,使之可以扩展,增加任意的属性。当然,我们也可以自己来实现这些属性函数,代码都很简单,只要判断其ASCII编码范围即可。
    (4)ctype.c中的tolower和toupper函数实现使用了宏__ctype_tolower,这个宏会被映射成一个数组。它直接根据字符c的范围__ctype_tolower[c]或c本身。
    (5)ctype.h下面的优化实现用于需要优化的环境中(比如用带优化选项的gcc来编译)。它直接把属性判断函数定义为宏,宏体就是__isctype (c, type)。定义成宏时就少了一层函数调用。tolower和toupper根据需要,或者用外部函数__ctype_tolower_loc()和__ctype_toupper_loc()来实现,并实现成内联函数;或者直接定义成宏,用这里定义的__tobody(c, f, a, args)来实现。这两者都差不多,因为内联函数也相当于具有宏的特征。
    (6)__BEGIN_DECLS/__END_DECLS宏用来表示数据结构、全局变量、函数原型声明的开始和结束。这类似于MFC中的BEGIN_MESSAGE_MAP/END_MESSAGE_MAP。__BEGIN_NAMESPACE_STD/__END_NAMESPACE_STD宏表示C标准库函数原型声明的开始和结束。__BEGIN_NAMESPACE_C99/__END_NAMESPACE_C99表示C99标准中的函数声明。
   2、wctype.h:C89增补1中引入,是ctype.h中各个函数的宽字符处理版本,能对宽字符进行属性分类和转换。还定义了通用属性类型wctype_t,表示字符转换的类型wctrans_t,构造属性的函数wctype,测试属性的通用函数iswctype,构造转换的函数wctrans,实行转换的通用函数towctrans。

   解释:
   (1)wctype.h的实现更通用,它定义一个描述宽字符属性的类型wctype_t,为unsigned long标量类型,实现了可扩展的、特定区域设置的宽字符分类功能。同样它也把所有属性类封装在一个enum中,每个属性对应一个枚举常量。wctype函数用来构造一个字符属性,参数为标识这个属性的字符串,主要有"alnum"、"alpha"、 "cntrl"、"digit"、"graph"、"lower"、"print"等,对应iswxxx属性分类函数。iswctype函数测试宽字符WC是否属于DESC属性类。调用iswctype时LC_CTYPE类别的设置应与wctype构造desc值时的LC_CTYPE设置相同。
   (2)宽字符集的分类取决于区域设置,其标准属性类映射到ASCII中的关系要理清楚:
   字母或数字 = 字母 || 数字
   大(小)写字母 = !控制字符 && !数字 && !标点符号 && !空白字符
   字母 = (大写字母 || 小写字母) && !控制字符 && !数字 && !标点符号 && !空白字符  
   图形字符 = 可打印字符 && !空格
   标点符号 = 可打印字符 && !字母 && !数字 && !空白字符
   空白字符 = !字母 && !数字 && !图形字符 && !标点符号
   (3)对字符属性的转换,wctype.h也定义了一个描述宽字符转换的类型wctrans_t,为32位整型指针。wctrans函数用来构造一个字符转换,参数为标识这个转换的字符串,主要有"tolower"、"toupper",对应towxxx转换函数,这个转换针对当前区域设置的LC_CTYPE类别值。towctrans函数使用DESC所示的转换来对宽字符WC进行转换。
   (4)在函数实现文件wcfuncs.c中,同样用了一个宏func(name, type)来简化实现。实现代码主要用到了_NL_CURRENT_WORD宏、LC_CTYPE类别宏、NL_CTYPE_MAP_OFFSET偏移宏。这些宏的功能在编译器内部或其附带的库中实现了。wctrans_table_lookup函数在wchar-lookup.h中定义,用于查询映射表,以获得转换后的宽字符。映射表有点类似于Unix的文件结构,用32位的字作为下标索引。宽字符集的每个字符被切割成4个比特块存储在位表的前面表项中,后面的几个表项存放了一级子表、二级子表、以及三级子表的指针。

原创粉丝点击