C言語基礎|大文字・小文字の変換

英字だけを、きれいに統一! toupper / tolower で文字列の見た目を整えよう

大文字・小文字をそろえると、文字列が読みやすくなる

ユーザー入力やログ、ID、ファイル名みたいな文字列って、大文字と小文字が混ざりがちです。
でも「大文字に統一」「小文字に統一」できると、検索もしやすいし、比較もラクになります。

ここでは、文字列を先頭から順に走査して、英字だけを変換する方法を学びます。
ポイントは、英字以外(数字や記号)はそのままにできること。C言語標準ライブラリの toupper と tolower が、その仕事をうまくやってくれます。

変換の全体像:1文字ずつ見て、必要なら置き換える

文字列走査と変換(イメージ)

図の説明

  • 文字列は配列(char の並び)なので、先頭から s[i] を順に見ていけます
  • toupper / tolower の返り値を、そのまま s[i] に代入すると「その場で更新」できます
  • 数字や記号は変換されずに残ります

サンプルプログラム

入力は空白を含めない想定で scanf を使います。

プロジェクト名:chap9-10-1 ソースファイル名:chap9-10-1.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

// 文字列内の英字を大文字/小文字に変換
#include <ctype.h>
#include <stdio.h>

// 文字列内の英字を大文字に変換
void str_toupper(char s[])
{
    int i = 0;
    while (s[i]) {
        s[i] = toupper((unsigned char)s[i]);
        i++;
    }
}

// 文字列内の英字を小文字に変換
void str_tolower(char s[])
{
    int i = 0;
    while (s[i]) {
        s[i] = tolower((unsigned char)s[i]);
        i++;
    }
}

int main(void)
{
    char str[128];

    printf("英字まじりの文字列を入力してください:");
    scanf("%127s", str);

    str_toupper(str);
    printf("大文字にそろえる:%s\n", str);

    str_tolower(str);
    printf("小文字にそろえる:%s\n", str);

    return 0;
}

実行例

英字まじりの文字列を入力してください:KaTaKaNa-Box_19
大文字にそろえる:KATAKANA-BOX_19
小文字にそろえる:katakana-box_19

toupper と tolower が「安全」な理由

toupper / tolower は、英字以外を変換しません。
なので、数字や記号を壊さずに「英字だけ」を統一できます。

変換の挙動(例)

入力文字toupper の結果tolower の結果ひとこと
'a''A''a'小文字→大文字
'Z''Z''z'大文字→小文字
'7''7''7'数字はそのまま
'-''-''-'記号もそのまま

表の説明

  • 変換対象は基本的に半角の英字です。
  • それ以外は「触らずに返す」ので、文字列の構造を保てます。

命令の書式と「何をする命令か」

while の書式と役割

  • 書式
    while (条件式) 文;
  • 何をする?
    条件式が真のあいだ文を繰り返します。
    今回は s[i] が 0(終端のナル文字)になるまで、文字を走査します。

代入の書式と役割

  • 書式
    左辺 = 右辺;
  • 何をする?
    右辺の値を左辺に入れます。
    今回は s[i] = toupper(...) の形で、変換結果を同じ場所へ上書きしています。

toupper の書式と役割

  • 書式
    int toupper(int c);
  • 何をする?
    c が英小文字なら対応する英大文字に変換して返します。
    英小文字でなければ、そのまま返します。

tolower の書式と役割

  • 書式
    int tolower(int c);
  • 何をする?
    c が英大文字なら対応する英小文字に変換して返します。
    英大文字でなければ、そのまま返します。

なぜ (unsigned char) を付けているの?

toupper / tolower は「引数に EOF か、unsigned char として表現できる値」を渡す前提で使うのが安全です。
char が負の値になり得る環境もあるので、付けておくと安心です。

変換関数に渡すときの考え方

渡し方安全性理由
toupper(s[i])環境によって注意char が負になる場合がある
toupper((unsigned char)s[i])安全寄り値域が確実に正になる

表の説明

  • 日本語などマルチバイト文字は、この変換では対象外です
  • 英字処理に限定して使うのが基本です

変換関数を2つ書くのが面倒なら、共通化もできる

「大文字化」と「小文字化」は構造が同じなので、関数ポインタで共通化もできます。
ただ、最初は2つに分けた方が読みやすいので、教材としては分ける形が分かりやすいです。

2つの関数が同じ形(イメージ)

str_toupper: s[i] = toupper(...)
str_tolower: s[i] = tolower(...)
どちらも「走査して上書き」

演習問題

演習9-10:文字列から数字文字を全部取り除く

文字列 s 内のすべての数字文字を除去する関数を作成せよ。
void del_digit(char s[]);

たとえば AB1C9 を受け取ったら、ABC に更新する。

解答例

プロジェクト名:chap9-10-2 ソースファイル名:chap9-10-2.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

#include <stdio.h>

void del_digit(char s[])
{
    int r = 0;  // 読み取り位置
    int w = 0;  // 書き込み位置

    while (s[r]) {
        if (!(s[r] >= '0' && s[r] <= '9')) {
            s[w] = s[r];
            w++;
        }
        r++;
    }
    s[w] = 0;   // 終端を付け直す
}

int main(void)
{
    char s[128];

    printf("文字列:");
    scanf("%127s", s);

    del_digit(s);

    printf("数字を除去:%s\n", s);

    return 0;
}

解説

  • 文字列を「読む位置 r」と「書く位置 w」で分けると、削除が簡単になります。
  • 数字以外だけを前に詰めてコピーして、最後に 0(ナル文字)を付け直します。
  • 文字列はナル文字で終わるルールなので、終端処理がとても大事です。