|簡體中文

比思論壇

 找回密碼
 按這成為會員
搜索



查看: 433|回復: 0
打印 上一主題 下一主題

C标准库<ctype.h>实现

[複製鏈接]

56

主題

0

好友

475

積分

中學生

Rank: 3Rank: 3

  • TA的每日心情

    2024-3-15 19:20
  • 簽到天數: 191 天

    [LV.7]常住居民III

    推廣值
    0
    貢獻值
    0
    金錢
    179
    威望
    475
    主題
    56
    樓主
    發表於 2014-7-18 20:53:58
    type.h是C标准函数库中的头文件,定义了一批C语言字符分类函数(C character classification functions),用于测试字符是否属于特定的字符类别,如字母字符、控制字符等
    我们经常将字符排序并分成不同的类别,为了识别一个字母,可以编写:
    if('A' <= c && c <= 'Z' || 'a' <= c && c <= 'z')    ......
    当执行字符集是ASCII码的时候,可以得到正确的结果,但是这种惯用用法不适合其他字符集
    同样,为了判断一个数字,可以这样编写:
    if('0' <= c && c <= '9')    ......
    判断空白,可以编写代码:
    if(c == ' ' || c == '\t' || c == '\n')    ......
    但是问题来了,我们很快就会厌倦代码中充斥着类似这样的判断语句而变长,最容易联想的解决办法是引入函数来替代这些判断语句,于是将出现如下的代码:
    if(isalpha(c))    ...if(isdigit(c))    ...if(isspace(c))    ...
    貌似问题得到了解决,但是考虑一个典型的文本处理程序对输入流中的每一个字符会平均调用3次这样的函数,就会严重影响程序的执行效率
    于是想到进一步的改进,考虑使用宏来替代这些函数,
    #define isdigit(x) ((x) >= '0' && (x) <= '9')
    这会产生问题,如宏参数x具有副作用;例如,如果调用isdigit(x++)或isdigit(run_some_program()),可能不是很显然,isdigit的参数将被求值两次。早期版本的Linux就使用了这种潜在犯错的方法。关于宏的缺点,本文就不赘述。
    为保障安全和代码紧凑,进一步的改进,使用一个或多个转换表的宏集合,每个宏有如下形式:
    #define _XXXMASK 0x...#define isXXX(c) (_Ctytable[c] & _XXXMASK)
    字符c编入以_Ctytable命名的转换表索引中,每个表项的不同位以索引字符为特征。如果任何一个和掩码_XXXXMASK相对应的位被设置了,那个字符就要在测试类别中,对所有正确的参数,宏展开成一个紧凑的非零表达式。
    这种方法的弊端:当宏的参数不在它的定义域内,就会访问转换表外的存储空间
    2.<ctype.h>的内容
    <ctype.h>定义的宏如下表所示:
    isalnum是否为字母数字
    isalpha是否为字母
    islower受否为小写字母
    isupper是否为大写字母
    isdigit是否为数字
    isxdigit是否为16进制数字
    iscntrl是否为控制字符
    isgraph是否为图形字符(例如,空格、控制字符都不是)
    isspace是否为空格字符(包括制表符、回车符、换行符等)
    isblank是否为空白字符 (C99/C++11新增)(包括水平制表符)
    isprint是否为可打印字符
    ispunct是否为标点
    tolower转换为小写
    toupper转换为大写
    下图来自Plauger和Brodie的Standard C:
    3.<ctype.h>的实现
    P.J.Plauger版本C标准库 Ctype 中判断字符是否属于某个类型,主要是通过转换表来实现的
    以判断是否为小写字母为例:
    /* ctype.h */#ifndef _CTYPE#define _CTYPE /* _Ctype 转换位 */#define _XA 0x200   /* extra alphabetic */#define _XS 0x100   /* extra space */#define _BB 0x80    /* BEL, BS, etc. */#define _CN 0x40    /* CR, FF, HT, NL, VT */#define _DI 0x20    /* '0' - '9' */#define _LO 0x10    /* 'a' - 'z' */#define _PU 0x08    /* punctuation */#define _SP 0x04    /* space */#define _UP 0x02    /* 'A' - 'Z' */#define _XD 0x01    /* '0' - '9', 'A' - 'F', 'a' - 'f' */ /* 声明外部的 _Ctype 转换表 */extern const short *_Ctype;/* 判断是否为小写字母的带参数宏 islower */#define islower(c) (_Ctype[(int)(c)] & _LO) // 其余省略 ...#endif
    _Ctype 转换表:
    /* xctype.c _Ctype 转换表 -- ASCII 版 */#include <limits.h>#include <stdio.h>#include "ctype.h"#if EOF != -1 || UCHAR_MAX != 255#error WRONG CTYPE table#endif/* 组合位 */#define XDI (_DI|_XD)#define XLO (_LO|_XD)#define XUP (_UP|_XD) /* 转换表 */static const short ctype_tab[257] = { 0, /* EOF */    _BB, _BB, _BB, _BB, _BB, _BB, _BB, _BB,    _BB, _CN, _CN, _CN, _CN, _CN, _BB, _BB,    _BB, _BB, _BB, _BB, _BB, _BB, _BB, _BB,    _BB, _BB, _BB, _BB, _BB, _BB, _BB, _BB,    _SP, _PU, _PU, _PU, _PU, _PU, _PU, _PU,    _PU, _PU, _PU, _PU, _PU, _PU, _PU, _PU,    XDI, XDI, XDI, XDI, XDI, XDI, XDI, XDI,    XDI, XDI, _PU, _PU, _PU, _PU, _PU, _PU,    _PU, XUP, XUP, XUP, XUP, XUP, XUP, _UP,    _UP, _UP, _UP, _UP, _UP, _UP, _UP, _UP,    _UP, _UP, _UP, _UP, _UP, _UP, _UP, _UP,    _UP, _UP, _UP, _PU, _PU, _PU, _PU, _PU,    _PU, XLO, XLO, XLO, XLO, XLO, XLO, _LO,    _LO, _LO, _LO, _LO, _LO, _LO, _LO, _LO,    _LO, _LO, _LO, _LO, _LO, _LO, _LO, _LO,    _LO, _LO, _LO, _PU, _PU, _PU, _PU, _BB,};const short *_Ctype = &ctype_tab[1];
    举一个例子来说明:
    当判断‘a’是否为小写字母的时候,使用宏islower,通过宏替换,也即执行(_Ctype[(int)(c)] & _LO)
    预处理之后,假设当前的c是'a'那么变成了: (_Ctype[(int)('a')] & _LO)
    [size=1em]字符'a'的值为97所以接下来便是: (_Ctype[97] & _LO)

    _Ctype[97]的转换宏是 _LO ,通过查_Ctype 转换表, _LO 的值又是 0x10,所以最后是:
    (_LO & _LO)   ---->    0x10 & 0x10    ---->    1, 说明当前字符为小写字母
    其他的字符的判断都可以通过类似的替换与‘&’得到,不一一赘述。
    附上linux内核中的ctype.h实现,基本原理相似

    重要聲明:本論壇是以即時上載留言的方式運作,比思論壇對所有留言的真實性、完整性及立場等,不負任何法律責任。而一切留言之言論只代表留言者個人意見,並非本網站之立場,讀者及用戶不應信賴內容,並應自行判斷內容之真實性。於有關情形下,讀者及用戶應尋求專業意見(如涉及醫療、法律或投資等問題)。 由於本論壇受到「即時上載留言」運作方式所規限,故不能完全監察所有留言,若讀者及用戶發現有留言出現問題,請聯絡我們比思論壇有權刪除任何留言及拒絕任何人士上載留言 (刪除前或不會作事先警告及通知 ),同時亦有不刪除留言的權利,如有任何爭議,管理員擁有最終的詮釋權。用戶切勿撰寫粗言穢語、誹謗、渲染色情暴力或人身攻擊的言論,敬請自律。本網站保留一切法律權利。

    手機版| 廣告聯繫

    GMT+8, 2024-5-17 02:11 , Processed in 0.061895 second(s), 25 queries , Gzip On.

    Powered by Discuz! X2.5

    © 2001-2012 Comsenz Inc.

    回頂部