合計 昨日 今日

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

フォーラムルール
フォーラムルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Name: Toshita
[URL]
初心者(6,859 ポイント)
Date: 2017年11月08日(水) 19:49
No: 1
(OFFLINE)

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

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

コード[C++]: 全て選択
1
2
3
4
5
6
7
8
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=かかきくけこ//

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

Name: Dixq (管理人)
(管理人)
[URL]
ウィザード(1,488,004 ポイント)
Date: 2017年11月08日(水) 22:21
No: 2
(OFFLINE)

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

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

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

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

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

コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#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);
        }
    }
}

実行結果
コード[C++]: 全て選択
1
2
//a=あ//
//a=か//

Name: Dixq (管理人)
(管理人)
[URL]
ウィザード(1,488,004 ポイント)
Date: 2017年11月08日(水) 22:43
No: 3
(OFFLINE)

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

文字列管理はC++の方がやりやすいと思いますのでC++版も作りました。
よければ参考にしてください。
コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#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;
        }
    }
}

Name: みけCAT
[URL]
伝説なるハッカー(681,038 ポイント)
Date: 2017年11月08日(水) 23:31
No: 4
(ONLINE)

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

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

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

全角文字であっても、2バイトとは限りません。
実行環境が書かれておらず、UTF-8ではひらがなは通常3バイトで表されます。

Toshita さんが書きました:
コード[C++]: 全て選択
1
2
3
4
5
6
7
8
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)ともに再現できませんでした。
コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <string.h>
int main(){
    char a[3]="あ";//日本語1文字目のみ代入
    char mor[256][256]={"あいう","あいうえお","かきく"};//「あいう」「あいうえお」「かきく」が入っているとする
    /*略*/
    for(int count=0;count<=2;count++)
    if( memcmp(a,mor[count],sizeof(char)*3) ){ //aの中身とmorの文字列の一文字目の比較 同じ場合偽、違う場合真
        strncpy(a,mor[count],sizeof(char)*3);//もし文字が違う場合、新しい文字列の一文字目を代入
        printf("//a=[%s]//\n",a);
    }
    printf("%d\n",(int)strlen("あ"));
}

未定義動作(終端文字の分のメモリが無いことによる配列の範囲外へのアクセス)の疑いがあります。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

Name: Dixq (管理人)
(管理人)
[URL]
ウィザード(1,488,004 ポイント)
Date: 2017年11月09日(木) 12:19
No: 5
(OFFLINE)

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

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

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

Name: Toshita
[URL]
初心者(6,859 ポイント)
Date: 2017年11月13日(月) 13:59
No: 6
(OFFLINE)

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

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

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

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


Return to C言語何でも質問掲示板

オンラインデータ

このフォーラムを閲覧中のユーザー: なし & ゲスト[16人]