ページ 1 / 1
マルチバイト文字の格納や比較について
Posted: 2017年11月08日(水) 19:49
by Toshita
いつもお世話になっております。
現在日本語の自然言語処理について勉強してまして、
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=かかきくけこ//
となります。なぜでしょうか?
Re: マルチバイト文字の格納や比較について
Posted: 2017年11月08日(水) 22:21
by Dixq (管理人)
文字列には最後が終端記号が入ります。一見そのことが分かっているようなコードに見えますが、それが考慮できていません。
(※追記: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);
}
}
}
実行結果
Re: マルチバイト文字の格納や比較について
Posted: 2017年11月08日(水) 22:43
by Dixq (管理人)
文字列管理は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;
}
}
}
Re: マルチバイト文字の格納や比較について
Posted: 2017年11月08日(水) 23:31
by みけCAT
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)ともに再現できませんでした。
► スポイラーを表示
コード:
#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("あ"));
}
未定義動作(終端文字の分のメモリが無いことによる配列の範囲外へのアクセス)の疑いがあります。
Re: マルチバイト文字の格納や比較について
Posted: 2017年11月09日(木) 12:19
by Dixq (管理人)
みけ君の言うとおり私のコードはUTF16やSJIS前提なので、使用環境を教えてもらわないとダメですね。
逆に3バイト文字環境で実行してるなら、
a
は終端記号を含めた4バイトにしないとダメですね。
Re: マルチバイト文字の格納や比較について
Posted: 2017年11月13日(月) 13:59
by Toshita
環境の話を忘れてました。
申し訳ないです。
文字コードはUTF-8です。
上記の修正点を元に修正したら解決しました。
助かりました。