マルチバイト文字の格納や比較について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Toshita
記事: 23
登録日時: 2年前

マルチバイト文字の格納や比較について

#1

投稿記事 by Toshita » 7ヶ月前

いつもお世話になっております。
現在日本語の自然言語処理について勉強してまして、
C言語でマルチバイト文字列の一文字目のみを抜き出し、
代入、比較するという方法がわからず質問いたしました。

コード:

char a[3];//日本語1文字目のみ代入
mor[256][256];//「あいう」「あいうえお」「かきく」が入っているとする
/*略*/

if( memcmp(a,mor,sizeof(char)*3) ){ //aの中身とmorの文字列の一文字目の比較 同じ場合偽、違う場合真
	strncpy(a,mor[count],sizeof(char)*3);//もし文字が違う場合、新しい文字列の一文字目を代入
	printf("//a=[%s]//\n",a);
}
出て欲しい出力
//a=あ//
//a=か//

実際の出力
//a=ああいう//
//a=ああいうえお//
//a=かかきくけこ//

となります。なぜでしょうか?

アバター
Dixq (管理人)
管理人
記事: 1613
登録日時: 7年前
住所: 北海道札幌市
連絡を取る:

Re: マルチバイト文字の格納や比較について

#2

投稿記事 by Dixq (管理人) » 7ヶ月前

文字列には最後が終端記号が入ります。一見そのことが分かっているようなコードに見えますが、それが考慮できていません。
(※追記:sjisやutf16において)
まず、全角文字は2バイトですのでmemcmpで比較すべきは2バイトです。
char a[3]="あ";
なら、aの3バイト目には終端記号が入ります。
しかし"あいう"や"あいうえお"や"かきく"との文字列と比較しているのであれば3バイト目は必ず文字データが入るので必ず偽になります。

strncpyするときも2バイトでよいはずです。
aの3バイト目には必ず終端記号が入らなければなりません。

また、if文のmorはmor[count]でなくていいのでしょうか?

やりたいであろうことを想像しながらコードを修正してみました。

コード:

#define _CRT_SECURE_NO_WARNINGS

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

int main() {
    char a[3]="さ";
    char mor[3][256] = { "あいう","あいうえお","かきく" };
    for (int count = 0; count < 3; count++) {
        if (memcmp(a, mor[count], 2)) {
            strncpy(a, mor[count], 2);//もし文字が違う場合、新しい文字列の一文字目を代入
            printf("//a=[%s]//\n", a);
        }
    }
}
実行結果

コード:

//a=あ//
//a=か//

アバター
Dixq (管理人)
管理人
記事: 1613
登録日時: 7年前
住所: 北海道札幌市
連絡を取る:

Re: マルチバイト文字の格納や比較について

#3

投稿記事 by Dixq (管理人) » 7ヶ月前

文字列管理はC++の方がやりやすいと思いますのでC++版も作りました。
よければ参考にしてください。

コード:

#include <iostream>
#include <string>
#include <array>

using namespace std;

bool isSameFirstCharacter(const string str1, const string str2) {
    string s1 = str1.substr(0, 2);
    string s2 = str2.substr(0, 2);
    if (s1 == s2) {
        return true;
    }
    else {
        return false;
    }
}

int main() {
    string target("さ");
    array<string, 3> strings = { "あいう", "あいうえお", "かきく" };
    for (const string& str : strings) {
        if (!isSameFirstCharacter(target, str)) {
            target = str.substr(0, 2);
            cout << target << endl;
        }
    }
}

アバター
みけCAT
記事: 6013
登録日時: 7年前
住所: 千葉県
連絡を取る:

Re: マルチバイト文字の格納や比較について

#4

投稿記事 by みけCAT » 7ヶ月前

Dixq (管理人) さんが書きました: まず、全角文字は2バイトですのでmemcmpで比較すべきは2バイトです。
char a[3]="あ";
なら、aの3バイト目には終端記号が入ります。
しかし"あいう"や"あいうえお"や"かきく"との文字列と比較しているのであれば3バイト目は必ず文字データが入るので必ず偽になります。

strncpyするときも2バイトでよいはずです。
aの3バイト目には必ず終端記号が入らなければなりません。
全角文字であっても、2バイトとは限りません。
実行環境が書かれておらず、UTF-8ではひらがなは通常3バイトで表されます。
Toshita さんが書きました:

コード:

char a[3];//日本語1文字目のみ代入
mor[256][256];//「あいう」「あいうえお」「かきく」が入っているとする
/*略*/

if( memcmp(a,mor,sizeof(char)*3) ){ //aの中身とmorの文字列の一文字目の比較 同じ場合偽、違う場合真
	strncpy(a,mor[count],sizeof(char)*3);//もし文字が違う場合、新しい文字列の一文字目を代入
	printf("//a=[%s]//\n",a);
}
出て欲しい出力
//a=あ//
//a=か//

実際の出力
//a=ああいう//
//a=ああいうえお//
//a=かかきくけこ//

となります。
提示された「コード」はそのままではコンパイルが通りません。
以下のようにコードを補ってみましたが、strlen("あ")が2の環境(ローカルWIndows7)、3の環境(Wandbox)ともに再現できませんでした。
► スポイラーを表示
未定義動作(終端文字の分のメモリが無いことによる配列の範囲外へのアクセス)の疑いがあります。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
Dixq (管理人)
管理人
記事: 1613
登録日時: 7年前
住所: 北海道札幌市
連絡を取る:

Re: マルチバイト文字の格納や比較について

#5

投稿記事 by Dixq (管理人) » 7ヶ月前

みけ君の言うとおり私のコードはUTF16やSJIS前提なので、使用環境を教えてもらわないとダメですね。

逆に3バイト文字環境で実行してるなら、
a
は終端記号を含めた4バイトにしないとダメですね。

Toshita
記事: 23
登録日時: 2年前

Re: マルチバイト文字の格納や比較について

#6

投稿記事 by Toshita » 7ヶ月前

環境の話を忘れてました。
申し訳ないです。

文字コードはUTF-8です。

上記の修正点を元に修正したら解決しました。
助かりました。

返信

“C言語何でも質問掲示板” へ戻る