大文字と小文字の相互変換

大文字/小文字に変換

アルファベットの大文字を小文字に変換するにはtolower関数を使用します。
小文字を大文字に変換するにはtoupper関数を使用します。
これらの使用にはコード先頭に#include <ctype.h>の追加が必要です。


#include <stdio.h>
#include <ctype.h>

int main()
{
    printf("%c\n", tolower('a'));
    printf("%c\n", tolower('B'));
    printf("%c\n", tolower('Z'));

    printf("\n");

    printf("%c\n", toupper('a'));
    printf("%c\n", toupper('B'));
    printf("%c\n", toupper('Z'));

    printf("\n");

    printf("%c\n", tolower('1'));
    printf("%c\n", tolower('9'));
    printf("%c\n", tolower('/'));

    getchar();
}
a
b
c

A
B
C

1
9
/
int tolower(
 int c
);
文字cを小文字にして返す。
変換できない文字の場合はcをそのまま返す。
int toupper(
 int c
);
文字cを大文字にして返す。
変換できない文字の場合はcをそのまま返す。

特に難しいことはない関数です。

文字列を一気に大文字/小文字に変換する関数はありませんが、ループ文を使用して全てを置き換えることができます。


char str[] = "AbCDe";
int length = sizeof(str) / sizeof(str[0]);

for (int i = 0; i < length; i++) {
	str[i] = tolower(str[i]);
}

printf("%s\n", str);

for (int i = 0; i < length; i++) {
	str[i] = toupper(str[i]);
}

printf("%s\n", str);
abcde
ABCDE

ただし文字列リテラルは書き換えることはできないので(未定義動作)、char型配列ではなくポインタで文字列を保存している場合は別の配列やメモリ領域に受け取る処理が必要です。


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

//文字列strを全て小文字にして文字列バッファbufにコピーする
//成功した場合はコピーした文字数を返す
//失敗した場合は-1を返す
int ToLower(const char* str, char* buf, size_t bufSize)
{
    if (str == NULL || bufSize == 0)
        return -1;

    size_t length = strlen(str) + 1;
    length = length > bufSize ? bufSize : length;

    for (int i = 0; i < length; i++) {
        buf[i] = tolower(str[i]);
    }

    buf[bufSize - 1] = '\0';
    return length - 1;
}

void Foo()
{
    const char* s = "aBCde";
    size_t length = strlen(s) + 1;
    char* buf = (char*)malloc(sizeof(char) * length);
    if (buf == NULL)
        return NULL;

    int r = ToLower(s, buf, length);
    if (r > 0)
        printf("%s", buf);
    else
        printf("変換失敗");

    free(buf);
}

int main()
{
    Foo();
    getchar();
}
abcde

malloc関数はメモリの操作の項で解説しています。

大文字/小文字の判定

文字が小文字か否かを判定するにはislower関数を使用します。
大文字か否かを判定するにはisuppoer関数を使用します。


#include <stdio.h>
#include <ctype.h>

int main()
{
    printf("%d\n", islower('a'));
    printf("%d\n", islower('B'));
    printf("%d\n", islower('Z'));

    printf("\n");

    printf("%d\n", isupper('a'));
    printf("%d\n", isupper('B'));
    printf("%d\n", isupper('Z'));

    getchar();
}
2
0
0

0
1
1
int islower(
 int c
);
文字cが小文字なら0を返す。
小文字でなければ0以外を返す。
int isupper(
 int c
);
文字cが大文字なら0を返す。
大文字でなければ0以外を返す。

islower/isupper関数に指定可能なのはunsigned char型の範囲の値(0~255)、またはEOF(-1)のみです。
それ以外の値を指定した場合は未定義動作で、何が返ってくるかは分からないので行わないようにしましょう。

厳密には未定義動作はどのような動作をするかはわかりません。
(どのような動作をしても良い)
極端なことを言えばプログラムがクラッシュする可能性もゼロではないため、行うべきではありません。

アルファベットの大文字小文字の違いを無視した比較

strcmp(strncmp)関数は、アルファベットの大文字と小文字を別の文字と判断します。
(→文字列の比較)
例えばWindowsのファイル名は大文字と小文字を区別しないので、ファイル名やパスを比較するとき、大文字でも小文字でも同じ文字列と判断したほうが都合が良い場合があります。

このような比較を一発で行う関数はC言語標準関数には用意されていないので、tolower関数(またはtoupper関数)を利用して実装してみます。


#include <stdio.h>
#include <string.h>
#include <ctype.h>

//s1とs2を比較をする
//ignoreCaseが0以外の場合は
//大文字小文字の違いを無視する
//戻り値: s1のほうが小さい場合は-1以下
//s1のほうが大きい場合は1以上
//一致する場合は0
int MyStrcmp(const char* s1, const char* s2, char ignoreCase)
{
    if (ignoreCase == 0)
    {
        //大文字小文字を無視しない
        return strcmp(s1, s2);
    }

    char c1 = tolower(*s1);
    char c2 = tolower(*s2);
    while (c1 == c2) {
        //NULL文字に到達したら0
       //ここではc1とc2は一致していることが確実なので
       //判定はどちらか一方のみで良い
        if (c1 == '\0') {
            return 0;
        }

        c1 = tolower(*(++s1));
        c2 = tolower(*(++s2));
    }

    //一致しなかった場合ここに到達
    //c1のほうが大きければ1
    //c1のほうが小さければ-1を返す
    return c1 > c2 ? 1 : -1;
}

//s1とs2をcount文字数分の比較をする
//ignoreCaseが0以外の場合は
//大文字小文字の違いを無視する
//戻り値: s1のほうが小さい場合は-1以下
//s1のほうが大きい場合は1以上
//一致する場合は0
int MyStrncmp(const char* s1, const char* s2, size_t count, char ignoreCase)
{
    if (ignoreCase == 0)
    {
        //大文字小文字を無視しない
        return strncmp(s1, s2, count);
    }

    char c1, c2;
    while (count > 0) {
        count--;

        c1 = tolower(*s1);
        c2 = tolower(*s2);

        //c1とc2が一致しなくなったら
        if (c1 != c2) {
            //c1のほうが大きければ1
            //c1のほうが小さければ-1を返す
            return c1 > c2 ? 1 : -1;
        }

        //NULL文字に到達したら0
        //ここではc1とc2は一致していることが確実なので
        //判定はどちらか一方のみで良い
        if (c1 == '\0') {
            return 0;
        }

        s1++;
        s2++;
    }

    //この行に到達したら
    //少なくともcount文字数分は一致している
    return 0;
}

//画面表示用
//countが正数ならばstrncmp関数
//負数ならばstrcmp関数
void Print(const char* s1, const char* s2, int count)
{
    if(count >= 0)
        printf("%sと%sを%d文字比較\n%d\n\n",
		    s1, s2, count, MyStrncmp(s1, s2, count, 1));
    else
        printf("%sと%sを比較\n%d\n\n",
		    s1, s2, MyStrcmp(s1, s2, 1));
}

int main()
{
    char* s1 = "abc";
    char* s2 = "ABC";
    int count = 3;
    Print(s1, s2, -1);
    Print(s1, s2, count);
    printf("- - - - -\n");


    s1 = "ABC";
    s2 = "aBc";
    Print(s1, s2, -1);
    Print(s1, s2, count);
    printf("- - - - -\n");

    s1 = "ABCDE";
    s2 = "abc";
    Print(s1, s2, -1);
    Print(s1, s2, count);
    printf("- - - - -\n");

    s1 = "AbC";
    s2 = "abCDE";
    Print(s1, s2, -1);
    Print(s1, s2, count);

    getchar();
}
abcとABCを比較
0
abcとABCを3文字比較
0

- - - - -
ABCとaBcを比較
0
ABCとaBcを3文字比較
0

- - - - -
ABCDEとabcを比較
1
ABCDEとabcを3文字比較
0

- - - - -
AbCとabCDEを比較
-1
AbCとabCDEを3文字比較
0

コードの解説はコメントに書いてある通りです。
大文字小文字を無視しない検索もできるように、引数でstrcmp/strncmp関数を呼び出せるようにしています。
(もちろん普通にそれぞれの関数を呼んでもかまいません)

今回はtolower関数を使用していますが、toupper関数を使用しても実行結果は同じです。
どちらのほうにメリットがあるとかはありません。

strcmp/strncmp関数は、文字列が一致すれば0を返しますが、それ以外は定まった数値は返しません。
第一引数のほうが大きければ「1以上」、小さければ「-1以下」を返すことが決められているだけです。
なので、一致しなかった場合の処理は例えば以下のようにしてもかまいません。


if (c1 != c2) {
	return c1 - c2;
}

これでも上記の仕様を満たします。
NULL文字は数値の0と同じなので、この場合でも問題ありません。

なお、大文字/小文字に変換で説明したように、文字列を全て大文字/小文字に置き換えてからstrcmp(strncmp)関数を使用することでも判定は可能です。
しかしこれは速度的にもメリットがなく、メモリ使用量も多くなります。