C99に対応した標準Cライブラリの実装レポートを行っていきます。

プロフィール 

Author:高木信尚

ホームページ
ブログ

最近の記事 

最近のコメント 

最近のトラックバック 

月別アーカイブ 

カテゴリー 

友達申請フォーム 

この人と友達になる

ホーム 全記事一覧 次ページ >>

 

2008/07/06 21:07|

 

<ctype.h>のところで、書くべきことは書いてしまったので、おさらいになりますが、一応to〜系の関数についても触れておきます。

通常、to〜系関数は、

int tolower(int c)
{
  return isupper(c) ? c - 'A' + 'a' : c;
}

のように実装されるのですが、この方法には問題があります。英文字の連続性については、文字コードを特定することで回避できますが、"C"ロケール以外をにらんだ場合、例えばドイツ語のß(エスツェット)は小文字しかないため、対応する大文字が存在しません。日本語の半角カナも類似の問題があります。

そこで、to〜関数も表引きの手法をとることにします。to〜系関数はEOFを受け入れる必要はないのですが、念のためis〜系関数と同様に、-1〜255までの257要素の配列にしたいと思います。

/* tolower.c */
const unsigned char __tolower_C[257] = { ... };
const unsigned char *__tolower = __tolower_C + 1;

/* toupper.c */
const unsigned char __toupper_C[257] = { ... };
const unsigned char *__toupper = __tolower_C + 1;

ヘッダ側は、

#ifdef __cplusplus
extern "C" {
#endif

extern const unsigned char __tolower_C[];
extern const unsigned char __toupper_C[];

extern const unsigned char *__tolower;
extern const unsigned char *__toupper;

#ifdef __cplusplus
}
#endif

とします。そして、各関数はインライン関数として、

static __inline__ int tolower(int c)
{
#ifdef __ONLY_C_LOCALE
  return isupper(c) ? c - 'A' + 'a' : c;
#else
  return __tolower[c];
#endif
}

static __inline__ int toupper(int c)
{
#ifdef __ONLY_C_LOCALE
  return islower(c) ? c - 'a' + 'A' : c;
#else
  return __toupper[c];
#endif
}

と定義することができます。
もちろん、各関数は外部関数としても同様の定義が必要になります。
2006/02/11 02:03|文字種別TB:0CM:0

 

今回は空白類文字の判別関数についてお話します。これで<ctype.h>内で宣言されるis〜系関数は終わりです。

isspace関数の厳密な仕様は、isalnum関数が偽となる処理系定義の文字、または標準空白類文字に対して真となります。標準空白類文字は、空白類文字とは異なり、復帰文字 '\r' が含まれます。isspace関数の実装は、他のis〜関数と同様、

static __inline__ int isspace(int c)
{
  return __ctype[c] & _SPACE;
}

とします。"C"ロケールで真になるのは、空白文字 ' '、水平タブ文字 '\t'、垂直タブ文字 '\v'、書式送り文字 '\f'、改行文字 '\n'、および復帰文字 '\r' です。

さて、C95までであれば、ここまででis〜系関数は終わりなのですが、C99では関数が一つ追加されました。それがisblank関数です。この関数は行内空白類文字を判別するためのものです。"C"ロケールでは、標準行内空白類文字かどうかの判別を行うことになります。標準行ない空白類文字には、空白文字 ' ' と水平タブ '\t' が含まれます。

厳密に言えば、isblank関数もロケールによって動作が変わるので、表引きにすべきなのかもしれませんが、実際問題として、およそ扱う可能性のあるロケールでは、標準行内空白類文字の判別ができれば十分ですので、今回は決めうちにします。

というのも、C95の範囲の関数で、8ビットあるビットパターンはすべてふさがっており、isblank関数を表引きにしようとすると、それだけで表のサイズが倍になってしまうからです。したがって、今回は、

static __inline__ int isblank(int c)
{
  return c == ' ' || c == '\t';
}

のように定義することにします。
2006/02/08 23:59|文字種別TB:0CM:0

 

<ctype.h>内で宣言される関数についての話が続いていますが、今回は制御文字の判別です。"C"ロケールでは、最低限サポートしなければならない制御文字は、逆斜線+英一文字で表現できる文字だけでよいのですが、ASCII、というかISO-646およびその上位互換の文字コードの場合、0x00〜0x1fおよび0x7fを制御文字として扱いますので、今回もそれにあわせることにします。

したがって、

static __inline__ int iscntrl(int c)
{
  return 0x00 <= c && c <= 0x1f || c == 0x7f;
}

のように記述してもよいのですが、とりあえずは表引きを使って、

static __inline__ int iscntrl(int c)
{
  return __ctype[c] & _CNTRL;
}

とすることにします。
2006/02/08 23:44|文字種別TB:0CM:0

 

前回に引き続き、<ctype.h>内で宣言される関数の実装についてのお話です。前回は英数字の判別についてでしたので、今回はそれ以外の表示文字についてです。それ以外の表示文字というのは、具体的には、区切り文字と空白文字があります。早速コードを見てみましょう。

static __inline__ int ispunct(int c)
{
  return __ctype[c] & _PUNCT;
}

上記は、区切り文字の判別を行う関数です。区切り文字と英数字を含めた、表示文字かどうかの判別は次の関数で行います。

static __inline__ int isgraph(int c)
{
  return __ctype[c] & (_LOWER|_UPPER|_DIGIT|_PUNCT);
}

isgraph関数では、空白 ' ' は偽の扱いになります。空白 ' ' まで含めた表示文字の判別は、次の関数で行います。

static __inline__ int isprint(int c)
{
  return __ctype[c] & (_LOWER|_UPPER|_DIGIT|_PUNCT|_BLANK);
}

これらの関数を用いることで、表示文字、あるいは印字可能な文字かどうかの判別を行うことができます。

前回取り上げた関数同様、これらの関数も外部定義が必要になります。今後も、特に触れない限り、ヘッダ内でインライン関数として定義した関数は、別途外部関数を定義する必要が発生します。
2006/02/08 01:03|文字種別TB:0CM:0

 

今回から数回にわたって、<ctype.h>の中で宣言される関数の具体的な実装についてお話します。まずは、英数字の判別を行う関数群です。この中には、isalnum, isalpha, isdigit, islower, isupper, およびisxdigit関数が含まれます。

まず、効率を考えて、これらの関数はヘッダ内でインライン関数として実装することを考えます。このとき、C99ではinlineがサポートされますが、C89等での使用も考慮に入れ、GNU拡張である__inline__キーワードを使用することにします。これは、localeconv関数の実装でも利用した方法です。

static __inline__ int isalnum(int c)
{
  return __ctype[c] & (_LOWER|_UPPER|_DIGIT);
}

このように、前回定義した_LOWER等を用いて、表中の値のビットパターンを調べることで文字種別の判別を行います。is〜系関数は、真のときには非0を返せばよいので、必ずしも1ではない値が返却値になりますが、これに関しては問題ないでしょう。

以下、同様に、

static __inline__ int isalpha(int c)
{
  return __ctype[c] & (_LOWER|_UPPER);
}
static __inline__ int isdigit(int c)
{
  return __ctype[c] & _DIGIT;
}
static __inline__ int islower(int c)
{
  return __ctype[c] & _LOWER;
}
static __inline__ int isupper(int c)
{
  return __ctype[c] & _UPPER;
}
static __inline__ int isxdigit(int c)
{
  return __ctype[c] & _XDIGIT;
}

のように記述することが可能です。

さて、インライン関数に関してはこれでよいのですが、<ctype.h>をインクルードせず、ユーザーが自分で関数原型を記述して、これらの関数を呼び出した場合でも正しく動作する必要があります。これを実現するには、インライン関数と同等の定義を外部関数として行う必要があります。

ライブラリが提供する外部関数は、極力、一関数につき一翻訳単位とする方が望ましいでしょう。アプリケーションであれば、これらの関連性のある関数は、一つのソースコードに列記したくなるところですが、ライブラリでそのようなことをすると、不必要な関数までリンクされる結果となり、空間効率が悪くなります。
2006/02/08 00:54|文字種別TB:0CM:0

ホーム 全記事一覧 次ページ >>

ブログ内検索 

お勧め書籍 

RSSフィード 

リンク 

このブログをリンクに追加する

Powered By FC2ブログ 

Powered By FC2ブログ
ブログやるならFC2ブログ

Copyright(C) 2006 TAKAGI Nobuhisa All rights reserved.
Powered by FC2ブログ. 無料ホームページ アフィリエイト レンタルサーバー FC2ブログ 一戸建て template designed by 遥かなるわらしべ長者への挑戦.