第25回
データ構造(4)~文字列をポインタで扱う

汎用的な関数を作る

ここまでに紹介した処理は、引数の文字列がすべて大文字または小文字であることを前提としていました。今度は、もっと汎用的に使えるよう、大文字・小文字・記号の混在した文字列を扱えるようにしてみましょう。

大文字/小文字を判断する下請け処理

引数の文字列が大文字または小文字だけに限定されている場合、"Hello!"のように大文字・小文字・記号が混じった一般的な文字列には対応できません。大文字の'H'から0x20を減算すると'('になってしまいます。

このような大文字、小文字、記号の混在した文字列を例えば「大文字に変換する」といった場合、以下のような結果が期待されるのが普通です。
小文字はすべて大文字に変換
大文字と記号はそのまま(変換しない)
この処理を実現するには、
ある1文字が変換対象の小文字かどうかを判断する処理
が必要になります。まず、これを作ってみましょう。名前は"islower"とし、「小文字ならTRUE、それ以外ならFALSE」を返す関数とします。リスト9のようになります。

リスト9:引数の1文字が小文字ならTRUEを返す関数“islower”
#define FALSE 0
#define TRUE !FALSE
#define bool int        /* 論理型を定義 */

bool islower(char c)
{
  if ((c >= 'a') && (c <= 'z')) {
    return (TRUE);
  } else {
    return (FALSE);
  }
}

小文字だけを大文字に変換する関数

islowerを使って、引数の文字列の中から小文字だけを大文字に変換し、それ以外の文字は何もしない──という処理を作ってみましょう。リスト10のようになります。

whileループの中で
if (islower(*s))
として文字列中の1文字(*s)が小文字かどうかを調べ、小文字の場合だけ
*s -= _DEF;
として、差分の"_DEF"を減算します。*sの示す1文字が小文字でなかった場合にはなにもせず、続いて
s++;
としてポインタを次の1文字に進めます。

リスト10:文字列の中から小文字だけを大文字に変換する関数“strupper”
#define _DEF 0x20

const char * strupper(char * s)
{
  char * ret;
  ret = s;    /* 引数のアドレスを保存 */
  while (*s != NULL) {    /* 文字列終端まで繰り返す */
    if (islower(*s)) {    /* 小文字のときだけ変換 */
      *s -= _DEF;
    }             
    s++;                  /* ポインタを進める */
  }
  return(ret);
}

大文字だけを小文字に変換する関数

同じ要領で、引数の文字列をすべて大文字に変換する処理を作ってみましょう。今度は、
大文字はすべて小文字に変換
小文字と記号はそのまま(変換しない)
という形になります。

下請け関数として、今度は
ある1文字が変換対象の大文字かどうかを判断する関数
が必要になります。これを"isupper"とし、「大文字ならTRUE、それ以外ならFALSE」を返すようにします。

この下請け関数も含めて、ソースはリスト11のようになります。

リスト11:文字列の中から大文字だけを小文字に変換する関数“strlower”
#define FALSE 0
#define TRUE !FALSE
#define bool int
#define _DEF 0x20

/* --------------------------------------------------
   isupper -- 大文字ならTRUE、それ以外ならFALSEを返す
  --------------------------------------------------- */
bool isupper(char c)
{
  if ((c >= 'A') && (c <= 'Z')) {
    return (TRUE);
  } else {
    return (FALSE);
  }
}

/* --------------------------------------------------
   strlower -- 文字列をすべて小文字に変換する
  --------------------------------------------------- */
const char * strlower(char * s)
{
  char * ret;
  ret = s;    /* 引数のアドレスを保存 */
  while (*s != NULL) {    /* 文字列終端まで繰り返す */
    if (isupper(*s)) {    /* 大文字のときだけ変換 */
      *s += _DEF;
    }
    s++;                  /* ポインタを進める */
  }
  return(ret);
}

あとがき

最後に紹介した大文字/小文字変換関数"strupper"と"strlower"の動作を試すためのプログラムを用意しました。

ソース"ex2501.c"とそれをコンパイルした"ex2501.exe"(DOSコマンドライン版実行形式ファイル)です。実行すると"Hello, world!"という大文字と小文字、記号の混在した文字列を、以下のようにすべて大文字とすべて小文字に変換して表示します。 ※1
HELLO, WORLD!
hello, world!
Windows Vista環境で日本語の入ったソースを実行させる場合、日本語の部分が文字化けする可能性がありますが、Vista環境で再コンパイルすることで回避できます。

文字を数値として扱うCの特徴を利用した文字列の大文字/小文字の変換処理を例に、配列とポインタの違いを紹介しました。ポインタで文字列を扱う処理を書いてみれば、ポインタの扱いがよく分かるようになります。

という訳で、次回はポインタを使った文字列処理関数をいくつか作ってみましょう。