字符處理包括分類(即屬性判斷)和轉換函數。ASCII字符主要可分類為控制字符、空白字符、可打印字符、數字字符、字母字符(大寫和小寫)、標點符號等。
1、ctype.h: 標準的屬性判斷函數有isalnum,isalpha,iscntrl,isdigit,isxdigit,isgraph,isprint,ispunct,islower,isupper,isspace, isblank(C99中引入)共12個函數。標準的屬性轉換函數有tolower和toupper。當然具體的實現中還會提供一些非標準函數作為擴展,如glibc的實現中提供了isctype,isascii,toascii。
-
- #ifndef _CTYPE_H
- #define _CTYPE_H 1
- #include <features.h> /* 定義了一些表示編譯選項的宏 */
- #include <bits/types.h>
- __BEGIN_DECLS
- #ifndef _ISbit
-
-
-
- # include <endian.h>
- # if __BYTE_ORDER == __BIG_ENDIAN /* 如果是大端字節序 */
- # define _ISbit(bit) (1 << (bit))
- # else /* 否則__BYTE_ORDER==__LITTLE_ENDIAN,是小端字節序 */
- # define _ISbit(bit) ((bit) < 8 ? ((1 << (bit)) << 8) : ((1 << (bit)) >> 8))
- # endif
- enum
- {
- _ISupper = _ISbit (0),
- _ISlower = _ISbit (1),
- _ISalpha = _ISbit (2),
- _ISdigit = _ISbit (3),
- _ISxdigit = _ISbit (4),
- _ISspace = _ISbit (5),
-
- _ISprint = _ISbit (6),
- _ISgraph = _ISbit (7),
- _ISblank = _ISbit (8),
- _IScntrl = _ISbit (9),
- _ISpunct = _ISbit (10),
- _ISalnum = _ISbit (11)
- };
- #endif /* ! _ISbit */
-
-
-
-
-
-
-
-
-
- extern __const unsigned short int **__ctype_b_loc (void)
- __attribute__ ((__const));
- extern __const __int32_t **__ctype_tolower_loc (void)
- __attribute__ ((__const));
- extern __const __int32_t **__ctype_toupper_loc (void)
- __attribute__ ((__const));
- #define __isctype(c, type) /
- ((*__ctype_b_loc ())[(int) (c)] & (unsigned short int) type)
- #define __isascii(c) (((c) & ~0x7f) == 0) /* 如果C是一個7比特的值,說明是一個ascii字符(0~127) */
- #define __toascii(c) ((c) & 0x7f) /* 屏蔽掉高位 */
-
- #define __exctype(name) extern int name (int) __THROW
- __BEGIN_NAMESPACE_STD
-
-
-
-
-
- __exctype (isalnum);
- __exctype (isalpha);
- __exctype (iscntrl);
- __exctype (isdigit);
- __exctype (islower);
- __exctype (isgraph);
- __exctype (isprint);
- __exctype (ispunct);
- __exctype (isspace);
- __exctype (isupper);
- __exctype (isxdigit);
-
- extern int tolower (int __c) __THROW;
-
- extern int toupper (int __c) __THROW;
- __END_NAMESPACE_STD
-
-
- #ifdef __USE_ISOC99
- __BEGIN_NAMESPACE_C99
- __exctype (isblank);
- __END_NAMESPACE_C99
- #endif
- #ifdef __USE_GNU
-
- extern int isctype (int __c, int __mask) __THROW;
- #endif
- #if defined __USE_SVID || defined __USE_MISC || defined __USE_XOPEN
-
- extern int isascii (int __c) __THROW;
-
- extern int toascii (int __c) __THROW;
-
- __exctype (_toupper);
- __exctype (_tolower);
- #endif /* Use SVID or use misc. */
-
- #define __tobody(c, f, a, args) /
- (__extension__ /
- ({ int __res; /
- if (sizeof (c) > 1) /
- { /
- if (__builtin_constant_p (c)) /
- { /
- int __c = (c); /
- __res = __c < -128 || __c > 255 ? __c : (a)[__c]; /
- } /
- else /
- __res = f args; /
- } /
- else /
- __res = (a)[(int) (c)]; /
- __res; }))
- #if !defined __NO_CTYPE && !defined __cplusplus
- # define isalnum(c) __isctype((c), _ISalnum)
- # define isalpha(c) __isctype((c), _ISalpha)
- # define iscntrl(c) __isctype((c), _IScntrl)
- # define isdigit(c) __isctype((c), _ISdigit)
- # define islower(c) __isctype((c), _ISlower)
- # define isgraph(c) __isctype((c), _ISgraph)
- # define isprint(c) __isctype((c), _ISprint)
- # define ispunct(c) __isctype((c), _ISpunct)
- # define isspace(c) __isctype((c), _ISspace)
- # define isupper(c) __isctype((c), _ISupper)
- # define isxdigit(c) __isctype((c), _ISxdigit)
- # ifdef __USE_ISOC99
- # define isblank(c) __isctype((c), _ISblank)
- # endif
- # ifdef __USE_EXTERN_INLINES
- __extern_inline int
- __NTH (tolower (int __c))
- {
- return __c >= -128 && __c < 256 ? (*__ctype_tolower_loc ())[__c] : __c;
- }
- __extern_inline int
- __NTH (toupper (int __c))
- {
- return __c >= -128 && __c < 256 ? (*__ctype_toupper_loc ())[__c] : __c;
- }
- # endif
- # if __GNUC__ >= 2 && defined __OPTIMIZE__ && !defined __cplusplus
- # define tolower(c) __tobody (c, tolower, *__ctype_tolower_loc (), (c))
- # define toupper(c) __tobody (c, toupper, *__ctype_toupper_loc (), (c))
- # endif /* Optimizing gcc */
- # if defined __USE_SVID || defined __USE_MISC || defined __USE_XOPEN
- # define isascii(c) __isascii (c)
- # define toascii(c) __toascii (c)
- # define _tolower(c) ((int) (*__ctype_tolower_loc ())[(int) (c)])
- # define _toupper(c) ((int) (*__ctype_toupper_loc ())[(int) (c)])
- # endif
- #endif /* Not __NO_CTYPE. */
-
-
-
-
- __END_DECLS
- #endif /* ctype.h */
-
- #define __NO_CTYPE
- #include <ctype.h>
-
- #define func(name, type) /
- int name (int c) { return __isctype (c, type); }
- func (isalnum, _ISalnum)
- func (isalpha, _ISalpha)
- func (iscntrl, _IScntrl)
- func (isdigit, _ISdigit)
- func (islower, _ISlower)
- func (isgraph, _ISgraph)
- func (isprint, _ISprint)
- func (ispunct, _ISpunct)
- func (isspace, _ISspace)
- func (isupper, _ISupper)
- func (isxdigit, _ISxdigit)
- #define __ctype_tolower /
- ((int32_t *) _NL_CURRENT (LC_CTYPE, _NL_CTYPE_TOLOWER) + 128)
- #define __ctype_toupper /
- ((int32_t *) _NL_CURRENT (LC_CTYPE, _NL_CTYPE_TOUPPER) + 128)
- int
- tolower (int c)
- {
- return c >= -128 && c < 256 ? __ctype_tolower[c] : c;
- }
- int
- toupper (int c)
- {
- return c >= -128 && c < 256 ? __ctype_toupper[c] : c;
- }
解釋:
(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。
-
- #ifndef _WCTYPE_H
- #include <features.h>
- #include <bits/types.h>
- #ifndef __need_iswxxx
- # define _WCTYPE_H 1
-
- # define __need_wint_t
- # include <wchar.h>
-
- # ifndef WEOF
- # define WEOF (0xffffffffu)
- # endif
- #endif
- #undef __need_iswxxx
-
- #ifndef __iswxxx_defined
- # define __iswxxx_defined 1
- __BEGIN_NAMESPACE_C99
-
- typedef unsigned long int wctype_t;
- __END_NAMESPACE_C99
- # ifndef _ISwbit
-
-
- # include <endian.h>
- # if __BYTE_ORDER == __BIG_ENDIAN
- # define _ISwbit(bit) (1 << (bit))
- # else /* 否則__BYTE_ORDER==__LITTLE_ENDIAN,是小端字節序 */
- # define _ISwbit(bit) /
- ((bit) < 8 ? (int) ((1UL << (bit)) << 24) /
- : ((bit) < 16 ? (int) ((1UL << (bit)) << 8) /
- : ((bit) < 24 ? (int) ((1UL << (bit)) >> 8) /
- : (int) ((1UL << (bit)) >> 24))))
- # endif
- enum
- {
- __ISwupper = 0,
- __ISwlower = 1,
- __ISwalpha = 2,
- __ISwdigit = 3,
- __ISwxdigit = 4,
- __ISwspace = 5,
- __ISwprint = 6,
- __ISwgraph = 7,
- __ISwblank = 8,
- __ISwcntrl = 9,
- __ISwpunct = 10,
- __ISwalnum = 11,
- _ISwupper = _ISwbit (__ISwupper),
- _ISwlower = _ISwbit (__ISwlower),
- _ISwalpha = _ISwbit (__ISwalpha),
- _ISwdigit = _ISwbit (__ISwdigit),
- _ISwxdigit = _ISwbit (__ISwxdigit),
- _ISwspace = _ISwbit (__ISwspace),
- _ISwprint = _ISwbit (__ISwprint),
- _ISwgraph = _ISwbit (__ISwgraph),
- _ISwblank = _ISwbit (__ISwblank),
- _ISwcntrl = _ISwbit (__ISwcntrl),
- _ISwpunct = _ISwbit (__ISwpunct),
- _ISwalnum = _ISwbit (__ISwalnum)
- };
- # endif /* Not _ISwbit */
-
- __BEGIN_DECLS
- __BEGIN_NAMESPACE_C99
-
-
-
-
- extern int iswalnum (wint_t __wc) __THROW;
-
-
- extern int iswalpha (wint_t __wc) __THROW;
-
- extern int iswcntrl (wint_t __wc) __THROW;
-
- extern int iswdigit (wint_t __wc) __THROW;
-
- extern int iswgraph (wint_t __wc) __THROW;
-
-
- extern int iswlower (wint_t __wc) __THROW;
-
- extern int iswprint (wint_t __wc) __THROW;
-
- extern int iswpunct (wint_t __wc) __THROW;
-
- extern int iswspace (wint_t __wc) __THROW;
-
-
- extern int iswupper (wint_t __wc) __THROW;
-
- extern int iswxdigit (wint_t __wc) __THROW;
-
- # ifdef __USE_ISOC99
- extern int iswblank (wint_t __wc) __THROW;
- # endif
-
-
-
-
- extern wctype_t wctype (__const char *__property) __THROW;
-
- extern int iswctype (wint_t __wc, wctype_t __desc) __THROW;
- __END_NAMESPACE_C99
-
-
-
- __BEGIN_NAMESPACE_C99
-
- typedef __const __int32_t *wctrans_t;
- __END_NAMESPACE_C99
- #ifdef __USE_GNU
- __USING_NAMESPACE_C99(wctrans_t)
- #endif
- __BEGIN_NAMESPACE_C99
-
- extern wint_t towlower (wint_t __wc) __THROW;
-
- extern wint_t towupper (wint_t __wc) __THROW;
- __END_NAMESPACE_C99
- __END_DECLS
- #endif /* need iswxxx. */
-
-
- #ifdef _WCTYPE_H
-
-
-
- __BEGIN_DECLS
- __BEGIN_NAMESPACE_C99
-
- extern wctrans_t wctrans (__const char *__property) __THROW;
-
- extern wint_t towctrans (wint_t __wc, wctrans_t __desc) __THROW;
- __END_NAMESPACE_C99
-
-
-
-
- __END_DECLS
- #endif /* __WCTYPE_H defined. */
- #endif /* wctype.h */
-
- #include <ctype.h>
- #include <wctype.h>
- #include <locale/localeinfo.h>
- #include "wchar-lookup.h"
-
- #define func(name, type) /
- extern int __isw##name (wint_t __wc); /
- int /
- __isw##name (wint_t wc) /
- { /
- if (isascii (wc)) /
- return is##name ((int) wc); /
- size_t i = _NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_CLASS_OFFSET) + type; /
- const char *desc = _NL_CURRENT (LC_CTYPE, i); /
- return wctype_table_lookup (desc, wc); /
- } /
- weak_alias (__isw##name, isw##name)
- #undef iswalnum
- func (alnum, __ISwalnum)
- libc_hidden_weak (iswalnum)
- #undef iswalpha
- func (alpha, __ISwalpha)
- libc_hidden_weak (iswalpha)
- #undef iswblank
- func (blank, __ISwblank)
- #undef iswcntrl
- func (cntrl, __ISwcntrl)
- #undef iswdigit
- func (digit, __ISwdigit)
- libc_hidden_weak (iswdigit)
- #undef iswlower
- func (lower, __ISwlower)
- libc_hidden_weak (iswlower)
- #undef iswgraph
- func (graph, __ISwgraph)
- #undef iswprint
- func (print, __ISwprint)
- #undef iswpunct
- func (punct, __ISwpunct)
- #undef iswspace
- func (space, __ISwspace)
- libc_hidden_weak (iswspace)
- #undef iswupper
- func (upper, __ISwupper)
- #undef iswxdigit
- func (xdigit, __ISwxdigit)
- libc_hidden_weak (iswxdigit)
- #undef towlower
-
- wint_t
- towlower (wc)
- wint_t wc;
- {
-
- size_t i = _NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MAP_OFFSET) + __TOW_tolower;
-
- const char *desc = _NL_CURRENT (LC_CTYPE, i);
- return wctrans_table_lookup (desc, wc);
- }
- libc_hidden_def (towlower)
- #undef towupper
-
- wint_t
- towupper (wc)
- wint_t wc;
- {
-
- size_t i = _NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MAP_OFFSET) + __TOW_toupper;
-
- const char *desc = _NL_CURRENT (LC_CTYPE, i);
- return wctrans_table_lookup (desc, wc);
- }
- libc_hidden_def (towupper)
解釋:
(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個比特塊存儲在位表的前面表項中,后面的幾個表項存放了一級子表、二級子表、以及三級子表的指針。