ワイド文字 解答ページ | Programming Place Plus C言語編 第47章

トップページC言語編第47章

問題① 🔗

問題① マルチバイト文字とワイド文字の違いを説明してください。


1文字を表現するために必要なバイト数が、可変であるものがマルチバイト文字です。これは、char型を使って表現します。

ワイド文字は、現在のロケールに存在するすべての文字が表現できる文字表現です。これは、wchar_t型を使って表現します。wchar_t型の大きさは環境下では固定なので、ある意味で、固定長の大きさを持った文字ではありますが、現実には UTF-16 のように、1文字が 16ビットであったり 32ビットであったりすることがあるので、ワイド文字=固定長であるとは限りません。

問題② 🔗

問題② L”abcde” の大きさを調べたところ 24バイトでした。L’\0’ は何バイトですか?


L”abcde” の型は、wchar_t[6] です。全体で 24バイトだったのだから、wchar_t型の大きさは、24 / 6 で 4バイトであると分かります。

L’\0’ も wchar_t型であることに変わりはないので、4バイトが答えです。

問題③ 🔗

問題③ 2つのワイド文字列を用意し、互いの内容が一致しているかどうかを調べるプログラムを作成してください。wcscmp関数を使うものと、使わずに自力で行うものを両方作成してください。


wcscmp関数を使うと、次のようになります。

#include <stdio.h>
#include <wchar.h>

int main(void)
{
    const wchar_t* str1 = L"あいうえお";
    const wchar_t* str2 = L"あいうえお";

    printf("%d\n", wcscmp(str1, str2));
}

実行結果:

0

次に、自力で実装してみます。 第33章の練習問題で、strcmp関数を自作しましたが、基本的にそれと同じです。

#include <stdio.h>
#include <wchar.h>
#include <assert.h>

int my_wcscmp(const wchar_t* s1, const wchar_t* s2);

int main(void)
{
    const wchar_t* str1 = L"あいうえお";
    const wchar_t* str2 = L"あいうえお";

    printf("%d\n", my_wcscmp(str1, str2));
}

/*
    自作 wcscmp関数
    引数:
        s1: 比較する文字列。
        s2: 比較する文字列。
    戻り値:
        辞書順で s1 の方が小さければ負数、s2 の方が小さければ 0より大きい値。
        同じであれば 0 が返される。
*/
int my_wcscmp(const wchar_t* s1, const wchar_t* s2)
{
    assert(s1 != NULL);
    assert(s2 != NULL);

    while (*s1 == *s2) {
        if (*s1 == L'\0') {
            return 0;
        }

        s1++;
        s2++;
    }

    return (*s1 < *s2) ? -1 : 1;
}

実行結果:

0

問題④ 🔗

問題④ 1つのワイド文字列と、1つのマルチバイト文字列を用意し、互いの内容が一致しているかどうかを調べるプログラムを作成してください。


いったん、いずれかのタイプの文字列に変換してしまえば良いでしょう。 ここでは、ワイド文字列の方に合わせます。

#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <locale.h>

int mbslen(const char* mbs);

int main(void)
{
    const char mbs[] = "あいうえお";
    const wchar_t wcs[] = L"あいうえお";

    if (setlocale(LC_CTYPE, "") == NULL) {
        fputs("ロケールの設定に失敗しました。\n", stderr);
        return EXIT_FAILURE;
    }

    // マルチバイト文字列の文字数を取得
    int len = mbslen(mbs);

    // ワイド文字列に変換したとき、必要になる大きさ分の領域を確保
    wchar_t* wcs2 = malloc(sizeof(wchar_t) * (len + 1));
    if (wcs2 == NULL) {
        fputs("動的メモリ割り当てに失敗しました。\n", stderr);
        return EXIT_FAILURE;
    }

    // マルチ文字列からワイド文字列へ
    if (mbstowcs(wcs2, mbs, len + 1) == -1) {
        fputs("ワイド文字列への変換に失敗しました。\n", stderr);
        return EXIT_FAILURE;
    }

    // ワイド文字列として比較
    printf("%d\n", wcscmp(wcs, wcs2));

    free(wcs2);
}

/*
    マルチバイト文字列の文字数を数える。
    引数:
        mbs:    対象のマルチバイト文字列。
    戻り値:
        文字数
*/
int mbslen(const char* mbs)
{
    int i = 0;
    int char_count = 0;

    while (mbs[i] != '\0') {
        int res = mblen(&mbs[i], MB_CUR_MAX);
        if (res < 0) {
            fputs("不正な文字を含んでいます。\n", stderr);
            exit(EXIT_FAILURE);
        }

        i += res;
        char_count++;
    }

    return char_count;
}

実行結果:

0

本編では、変換結果を受け取る配列の要素数は固定にしていましたが、今回は実際の長さに合わせて動的に確保するようにしてみました。そのためには、ワイド文字列に変換したら何バイトになるのかを計算する必要があります。さらにそのためには、マルチバイト文字列の文字数が必要です。

マルチバイト文字列の文字数は、第46章で見たように、mblen関数を駆使して調べられます。今回はせっかくなので関数化しました。

マルチバイト文字列でもワイド文字列でも、文字数は変わらないので、これで変換後の文字数も分かったことになります。あとは、ヌル文字の分を忘れずに加算して、sizeof(wchar_t) を掛け合わせれば、ワイド文字列の全体の大きさが分かります。この大きさを malloc関数に確保してもらえばよいです。



参考リンク 🔗


更新履歴 🔗

 第48章の練習問題④、⑥を移動してきて、練習問題①、②とした。既存の①~②は2つずつずらした。

 全面的に文章を見直し、修正を行った。

 setlocale関数の LC_CTYPE の指定を修正。

 新規作成。



第47章のメインページへ

C言語編のトップページへ

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る