MSVCでShiftJISの全角文字と小文字a-zが混在する文字列をa-zの小文字のみ大文字に変更するサンプルコード

Visual C/C++

この記事では、Visual Cを使用して、ShiftJISの全角文字と小文字a-zが混在する文字列から、小文字のa-zのみを大文字に変更するサンプルコードを紹介します。サンプルコードでは、指定された入力ファイルから文字列を読み込み、変換後の文字列を標準出力に表示します。

ソースコード

以下のソースコードは、プログラムに引数としてファイル名を指定し、そのファイルから文字列を読み込んで処理を行います。引数が指定されていない場合は、使用方法を表示します。

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

void convert_to_uppercase(char* str) {
    while (*str) {
        if ( ( (0x81 <= (unsigned char)*str && (unsigned char)*str <= 0x9F) ||
               (0xE0 <= (unsigned char)*str && (unsigned char)*str <= 0xEF)) &&
             ( (0x40 <= (unsigned char)*(str + 1) && (unsigned char)*(str + 1) <= 0x7E) ||
               (0x80 <= (unsigned char)*(str + 1) && (unsigned char)*(str + 1) <= 0xFC)
            ) ) {
            // ShiftJISの全角文字をスキップ
            str += 2;
        } else if ('a' <= *str && *str <= 'z') {
            *str = toupper((unsigned char)*str);
            str++;
        } else {
            str++;
        }
    }
}

int main(int argc, char* argv[]) {
    if (argc != 2) {
        printf("Usage: %s <filename>\n", argv[0]);
        return 1;
    }

    FILE* file;
    if (fopen_s(&file, argv[1], "r") != 0) {
        perror("Could not open file");
        return 1;
    }

    fseek(file, 0, SEEK_END);
    long file_size = ftell(file);
    fseek(file, 0, SEEK_SET);

    if (file_size < 0) {
        perror("Invalid file size");
        fclose(file);
        return 1;
    }

    char* buffer = (char*)malloc((size_t)file_size + 1);
    if (!buffer) {
        perror("Could not allocate memory");
        fclose(file);
        return 1;
    }

    size_t read_size = fread(buffer, 1, (size_t)file_size, file);
    buffer[read_size] = '\0';

    fclose(file);

    printf("Original: %s\n", buffer);
    convert_to_uppercase(buffer);
    printf("Converted: %s\n", buffer);

    free(buffer);
    return 0;
}

ShiftJISの全角文字と小文字a-zが混在する文字列をif (‘a’ <= *str && *str <= ‘z’)の判定のみで対応すると全角文字の2バイトのうち2バイト目に(‘a’ <= *str && *str <= ‘z’)範囲のデータが変更される危険性があります。

テスト用入力ファイル

以下はテスト用入力ファイル(test.txt)の内容です。

[電子データの#小文字abcを試しに&大文字にhen*kouする__sample__データ.]

test.exe test.txtを実行しましとtest.txtの内容が問題なく標準出力として出力されます。

[電子データの#小文字ABCを試しに&大文字にHEN*KOUする__SAMPLE__データ.]

注意事項

変数 *strを char ではなく unsigned char にする必要があります。char 型が符号付き、即ち*strに例えとして0x93 の値が負の値としてセットされている場合、具体的には、0x93 は2の補数表現で -109 となります。これにより、比較が意図した通りに動作しない可能性があります。char が符号付きの場合、特に日本語のShiftJISコード範囲(0x81~0x9F、0xE0~0xEF)を扱うときに問題が発生するためです。

unsigned charではなくcharで判定するようソースを修正した場合、

 if ( (  (0x81 <= *str && *str <= 0x9F) ||
         (0xE0 <= *str && *str <= 0xEF)) &&
      (  (0x40 <= *(str + 1) && *(str + 1) <= 0x7E) ||
         (0x80 <= *(str + 1) && *(str + 1) <= 0xFC) ) )

入力データ

[電子データの#小文字abcを試しに&大文字にhen*kouする__sample__データ.]

出力結果(NG)

以下のよう文字化けする箇所が発生します。

[参ェータの#小文字ABCを試しに&大文字にHEN*KOUする__SAMPLE__ェータ.]

まとめ

Visual Cを使用して、ShiftJISの全角文字と小文字a-zが混在する文字列から、a-zの小文字のみを大文字に変更するサンプルコードを紹介しました。プログラムは、入力ファイルから文字列を読み込み、変換して標準出力に表示します。char 型が符号付きの場合の問題点にも注意が必要です。この記事を参考に、同様の文字列操作を行うプログラムを作成してみてください。

タイトルとURLをコピーしました