ctype.h

ctype.h是c标准函数库中的头文件   定义了一批c语言字符分类函数   (c character classification functions) 用于测试字符是否属于特定的字符类别 ,如字母字符、控制字符等等。既支持单字节   byte字符  也支持宽字符
文件中包含两类字符函数   字符测试函数    例如   int  isxxx(int)   
字符映射函数(转换)   例如    int   toxxx(int)
  1   背景
宏的意外
a 宏可能比函数调用执行得快   但是展开后的代码可能回避函数调用的多几倍    如果你的程序在很多地方扩展宏   程序会很大
b 宏可能会展开为一个子表达式    但是它不像函数调用那样紧凑   所以需要在宏定义中使用圆括号  消除弊端
c 宏展开后某些参数可能执行了不止一次  或者根本就不执行   一个具有副作用的宏参数会导致意外的发生   只有两个c标准库函数getc和putc  使用时可能会产生这种不安全行为的宏
 
转换表
一个宏的集合来代替这些字符分类函数      translation table
例子:   每个宏存在的形式
#define _XXXMASK  0x...
#define isxxx(c) (_Ctyptab[c] &  _XXXMASK)
 
字符c编入以_Ctyptab命名的转换表索引中   每个表项的不同位以索引字符位特征  如果任何一个和掩码_XXXMASK相对应的位被设置了   那个字符就在要测试的类别中   对所有正确的参数  宏展开成一个紧凑的非零表达式
        有弊端
区域设置
转换表仍然是很多字符分类函数的当今实现的基础    他们会为实现者提供高效的宏   
一个c程序总是在“c”区域设置中开始执行   调用函数setlocale可以改变区域设置    当设置改变时    头文件中声明的函数的某些属性就可能改变它的行为
C语言中的区域设置是C标准委员会的发明,当时加进区域设置(相关声明在locale.h中)是为了支持欧洲那里与美国不同的字符集,现在的区域设置同时也支持亚洲字符集。
 
一.标准内容
头文件的内容省略
ctype.h  头文件声明了几个可以用于识别和转换字符的函数
对于所有参数是int类型的情况  参数值可以表示为unsigned char类型  或者和宏EOF的值是相等的   如果参数为其他的值   则它的行为未定义。
这些函数的行为受当前区域设置的影响   当处于c之外的区域设置时   有些函数的某些方面时由实现定义的     
比如
打印字符   指的是由实现定义的字符集的一个元素   每个打印字符在显式设备上都占据一个打印位置   
控制字符   是实现定义的字符集中不是打印字符的元素
1.字符判断函数
当且仅当参数c的值和函数描述中的一致时才返回非0
isalnum  alphanumeric 的缩写     要求每个名字以字母开头     后面可以使数字或者字母
#include <ctype.h>
int isalnum(int c)
函数isalnum判别所有isalpha或者isdigit 判别为真的字符
 
isalpha alphabetic的缩写  不区分大小写   测试本地字母表中的字母   对于c区域 就是熟悉的26个字母包括大小写
判别所有isupper或者islower判别为真的字符    或者那些实现定义的字符集中的iscntrl  isdigit  ispunct  和isspace判别都不为真的字符     在c区域设置中    只对 isupper或者islower判别为真的字符返回真
iscntrl   控制字符   报警  退格   回车   换页   水平制表符   换行和垂直制表符
isdigit     判别所有十进制     最稳定的函数  之一   只包括10个十进制数字    
例如  
for(value=0;isdigit(*s);++s)
value=value*10+(*s-'0');    简化实现数字转化的代码
isgraph   除空格外的所有打印字符
islower  小写
isprint   包括空格在内的所有打印字符
ispunct 除空格和isalnum判别为真的字符之外的所有打印字符  最好认为标点符号属于图形字符 而不属于字母数字
isspace    空白字符   或者由实现定义的字符集中isalnum判别为假的字符  比较重要    库函数使用此函数来判定把哪些字符作为空格对待   在c区域设置中    此函数用来识别输出到显式设备时    所有改变打印位置   却没有显式图形的字符
isupper  大写   
isxdigit   16进制 不随着区域设置而改变    专门用来识别十六进制数    
在所有的区域设置中   对十六进制转换的代码
#include <ctype.h>
#include <string.h>
...
static const xd[]={"0123456789abcdef}";
static const char xv[]={
0,1.2,3,4,5,6,7,8,9,10,11,12,13,14,15,10,11,12,13,14,15};
for(value=0;isxdigit(*s);++s)
value=(value<<4)+xv[strchr(xd,*s)-xd];
}
          注意溢出的检查
 
 
2.字符大小写转换函数
tolower   大变小       也可将哪些没有大写形式的字母和大小写形式都没有的字母处理为小写字母     简单的加上或者减去一个常量就能把大写字母转换为对应的小写字母   仅仅适用于ASCII和EBCDIC这两种常用的字符集    c标准并没有此要求
toupper 小变大     同上    仅仅功能相反
 
二   头文件的使用
此头文件中声明的函数      可以用来对字符进行判断和转换    这些字符由 fgetc    getc    getchar等函数读入   
需要声明数据对象为整形
如果参数值不是上述的函数读入   需要谨慎    头文件中的函数只对stdio.h中定义的EOF值和unsigned char类型可以代表的值正确工作    仅当基本c字符集的字符表示为char类型的时候   他们为正值
 
字符类别
字符分类函数定义的字符类别有:
a 数字   0-9  十进制
b 十六进制   0-f
c 小写字母    a-z     在c区域设置外可能会加上其他的字符
d 大写字母 A-Z
e 字母  小写字母或者大写字母   在c区域设置外可能会加上其他的字符
f 字母数字   字母或者数字
g 图形字符   占据一个打印位置   输出到显式设备时可见的字符
h 标点符号   非字母数字的图形字符   至少包括表示c源程序文本的29个符号
i 打印字符   图形字符或者空格符
j 空格    空格字符  和5个标准的运动控制字符   换页符 FF   换行符  NL     回车符  CR     水平制表符  HT   垂直制表符 VT    在c区域设置外可能会加上其他的字符
k 控制字符   5个标准的运动字符   (j项)    退格符BS   和劲爆符  BEL   加上其他可能的字符中的一个字符
ctype.h    第2章 - 骡子 - stupidmule@126 的博客
  上图说明了字符分类函数之间的联系    
矩形框中的字符都属于基本c字符集      表示任意的c源文件的字符    
函数名下有一个加号表明这个函数在c区域设置外的区域设置中可以表示附加的字符   
两个加号表明这个函数即使在c区域设置下也可以表示附加的字符
执行字符集可以包含其他分类下的字符    同一个字符只能位于图表中的一个位置    如果是小写字母  那么它也可 以通过集成而属于多个类     但是一个字符不能既是标点符号 有事控制字符
几乎所有的函数都能在区域设置变动的程序中改变行为    只有isdigit和isxdigit保持不变   
区域设置改变
使用字符分类函数增加任何测试  以删除任意的不在分类中的字符
或者   在程序改变它的区域设置为非c区域设置之前   去掉所有独立与区域设置中的判别代码   使他们不会影响程序结果。
 

三  头文件的实现
一个转换表描述了执行字符集的特征   每个函数把它的参数作为表的入口    然后在把选择的表元素和一个唯一的掩码比较来确定参数字符是否在字符类别中
大小由包含的元素个数和每个元素的大小决定的     标准c定义了3中字符类型   char     signed char  和unsigned char
至少需要8位才能表示执行字符集中的所有字符
 
取值范围
每个字符分类函数都接收一个int类型的参数   但是参数值需要在一定的范围内    其中unsigned char 类型所能表示的所有值都是有效的   还有宏EOF所确定的值。    大部分实现都把EOF表示为-1
   8为表示一个字符类型  所以一个转换表一定要包含257个元素
在上图中至少有两个增补的字符集处于c区域之外
函数isalpha可以识别islower和isupper都不能识别的字符
函数sispace可以识别iscntrl和isprint都不能识别的字符
 
共享库 库可以使用指向表的指针的可写的静态存储空间 也就是翻译器必须包含c标准库中的代码  
c标准库中的所有代码都占据一个独立的内存空间
一个链接好并在这个环境中运行的c程序把控制权转移给共享库中的函数 而不是把库中的代码复制到自己的程序中 这样做
的好处是程序更小而且链接得更快
可写的静态存储空间
当一个或者多个函数需要维持一个属于库私有的可写的静态数据对象时,不能在不同的程序或者同一个程序的不同控
制线程之间共享一个相同的数据对象,需要为每一个程序或者线程分配一个唯一的可写的静态数据对象 并且要初始化
操作系统和链接器使用特定的机器系统使共享库工作 有些直接禁用可写的静态存储空间 其他的则要求启用特定的
机器系统来建立 和访问可写的静态存储空间 所以我们必须用一种特殊的方式来编写代码
字符分类函数需要适应区域设置的改变 就需要可写的静态存储空间 一种方法是区域改变时重新写表 另种是改变指
向表的指针 表为只读 从而加快区域设置改变的速度
 
ctype.h 头文件 声明了函数的代码都是围绕3个转换表 3个可写的指针一直指向对应与当前区域设置的表
每个函数都有一个对应的宏 那些定义了分类位的宏名晦涩但是节省空间 可以加速对标准头文件的处理
 
/*ctype.h standard header*/
#ifndef _CTYPE
#define _CTYPE
/*_Ctype code bits*/ 代码位宏定义
 
#define _XA 0x200    /*extra alphabetic*/
#define _BB 0x80 /*BEL,BS,ETC*/
...
...
 
/*declarations*/声明函数和指向表的指针
int isalnum(int),isalpha(int),iscntrl(int)...
extern const short *_Ctype,*_Tolower,*_Toupper;
 
/*macro overrides*/  函数宏
#define isalnum(c) (_Ctype[(int)(c)]&(_DI|_LO|_UP|_XA))
#define isalpha(c) (_Ctype[(int)(c)]&(_LO|_UP|_XA))
...
endif
 
isalnum.c代码
/*isalnum function*/
#include <ctype.h>
int (isalnum)(int c)
{ //测试字符
return (_Ctype[c]&(_DI|_LO|_UP|_XA));
}
 
xtolower.c
/*_Tolower conversion table --ASCII version*/
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#if EOF!=-1||UCHAR_MAX!=255
#error WRONG TOLOWER TABLE
#endif
 
/*static data*/
static const short tolow_tab[257]={
EOF,0x00,0x01,0x02.......,'a','b','c',...,0x5b,0x5c,0x5d,0x5e,0x5f,0x60,'a','b','c','d',...0x7b,0x7c,0x7d,0x7e,0x7f,
0x80,0x81,0x82.....0xff
};
const short *_Tolower=&tolow_tab[1];
注意#error指令的使用  保证了只有在假设成立的情况下代码才被正确的翻译   <limits.h>中定义的宏 UCHAR_MAX给出了unsigned char类型所能表示的最大值
xtoupper.c  源代码省略
 
数据对象  _Ctype
xctype.c  所有的字符分类函数都公用一个_Ctype指向的转换表    这个文件就定义了这个表和指针
源代码:
/*_Ctype conversion table -- ASCII version*/
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#if EOF!=-1 || UCHAR_MAX !=255
#error WRONG CTYPE TABLE
#endif
 
/*macros*/
#define XDI (_DI|_XD)
#define XLO(_LO|_XD)
#define XUP(_UP|_XD)
 
/*static data*/
static const short ctyp+tab[257]={0,/*EOF*/
_BB,_BB,_BB,..._CN,_CN..._BB,_BB..._PU,_PU...};
/*rest all match mothing*/
const short *_Ctype=&ctyp_tab[1];
 
 
五  <ctype/h>的测试
 
posted on 2017-11-27 08:43  学习记录园  阅读(288)  评论(0编辑  收藏  举报