ページ 1 / 1
全角文字列取得
Posted: 2008年8月12日(火) 15:08
by いけやん
初めまして、いけやんと申します。
さっそく質問します。
開発環境
Windows XP sp2
Visual C++6.0
MFC ダイアログベース
----+--------+--------------------
2000|02-00-00|《ああああああ》
2000|02-01-00| いい掛/うう
現在上記の様な、テキストデータを読み込んで、CTreeCtrlに表示させているのですが
今strtok関数を使ってテキストの中の必要のない物をどけて、文字列を摘出しています。
しかしstrtok関数は、半角の物を意識して作ってあるらしく
全角文字列を読み込ませるのは、よろしくないみたいです。
実際に今のプログラムでは、
実行結果
<<ああああああ>>
いい
/うう
「掛」この文字だけなくなり、
こういった表示になります。
原因は、「全角1文字を半角2文字として扱うため、
場合によっては全角文字の一部が区切り文字と誤認識され、ぶった切られます。」
なので、wcstok関数を使えば全角文字列も読み込み
できるみたいなのでwcstok関数を使う事にしました。
リファレンスより
wchar_t *wcstok(
wchar_t *strToken,
const wchar_t *strDelimit
);
wcstokの第一引数をUnsigned shortに変換に変換できません
というエラーがでます。
http://hw001.gate01.com/eggplant/tcf/cp ... _trap.html
この関係なんですかね?
無理やりですが、キャストしたりしてみてもエラーがでます。
使い方に困っています。
なので、wcstok関数の使い方がわかる方
すいませんが、お願いたします。
またwcstok関数以外でも結構ですので
よろしくお願いします。
Re:全角文字列取得
Posted: 2008年8月12日(火) 15:10
by いけやん
これがソースです。
CStdioFile fin;//ファイル変数
CString str;//1行文読込用
int i = 0,count;
if ( !fin.Open("部材.txt", CFile::modeRead, NULL ) ) {return -1;}
while ( fin.ReadString(str) != NULL ) {
int i = 0;
reinterpret_cast<unsigned short *>(str);
for (const char *token = wcstok(str.GetBuffer(str.GetLength()),"+-|0123456789 ");
token != NULL;
token = wcstok(0,"-+|0123456789 ")){
m_HndTree[0] = m_tree.InsertItem(token,TVI_ROOT);
m_HndTree[1] = m_tree.InsertItem(token,m_HndTree[0]);
省略
}
Re:全角文字列取得
Posted: 2008年8月12日(火) 15:30
by box
第1引数は、ポインターでなければならないはずです。
私はwcstok関数を使ったことがないのでナニですが、
> token = wcstok(0,"-+|0123456789 ")){
ここの第1引数は NULL あたりにしなければならないのではないでしょうか。
Re:全角文字列取得
Posted: 2008年8月12日(火) 15:42
by toyo
部材.txtはシフトJISで書かれているのですよね
掛の0x7Cが'|'とかぶりますね
wcstok( )を使うにはUTF-16にコンバートしないといけません
ファイル自体を変えるか読み込んでから
MultiByteToWideChar( )
で変更するか
でもファイル自体の文字コードを変更できるならEUC-JPにしたら区切り文字とかぶることはなくなりますよ
Re:全角文字列取得
Posted: 2008年8月12日(火) 16:48
by いけやん
すいません返信遅れました。
>token = wcstok(0,"-+|0123456789 ")){
直しました。ありがとうございます。
すいません。文字コードの前に
文字列をキャストしているところエラーが出てました。
reinterpret_cast<unsigned short *>(str);
for (const char *token = wcstok(str.GetBuffer(str.GetLength()),"+-|0123456789 ");//エラー
token != NULL;
token = wcstok(NULL,"+-|0123456789 ") ) {//エラー
class CString' から 'unsigned short *' に変換することはできません。
すいませんが、CStringからunsgined shortへの変換の仕方
教えて頂けないでしょうか。お願いします。
Re:全角文字列取得
Posted: 2008年8月12日(火) 17:21
by toyo
単純なキャストだとASCII文字が2文字で1文字として扱われてしまうのでだめですね
wchar_t型にするなら文字コードもユニコードにするのが本来の使い方だと思います
wchar_t buf[256];
MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED, str, 256, buf, 256);
でどうでしょう
あとwcstok関数は第2引数もwchar_t型なので
wcstok(NULL, L"+-|0123456789 ")
とLを付けないといけませんね
個人的な意見としてはマルチバイト文字をユニコード用の関数で扱うのはあまりいいやり方ではないような気がします。
自分ならstr.Mid( )で1バイトずつチェックして0x80以上なら2バイト文字なので読み飛ばすようにします。
Re:全角文字列取得
Posted: 2008年8月12日(火) 19:31
by いけやん
う~ん、MultiByteToWideCharを使ってみたのですが
やっぱりchar * から unsigned short *変換できません
エラーになりますね。
if ( !fin.Open("部材.txt", CFile::modeRead, NULL ) ) {return -1;}
while ( fin.ReadString(str) != NULL ) {
int i = 0;
MultiByteToWideChar(CP_THREAD_ACP,MB_PRECOMPOSED,str,256,buf,256);
//const char *str2 = new char[str.GetLength()+1] = strcpy(str2,str);
//reinterpret_cast<unsigned short *>(str);
for (const char *token = wcstok(str.GetBuffer(str.GetLength()),L"+-|0123456789 ");
token != NULL;
token = wcstok(NULL,L"+-|0123456789 ") ) {
>個人的な意見としてはマルチバイト文字をユニコード用の関数で扱うのは
>あまりいいやり方ではないような気がします。
これは、下記の様な無理?に変換して使うのは、
あまりよろしくないという事ですね?
MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED, str, 256, buf, 256);
>str.Mid( )で1バイトずつチェックして0x80以上なら2バイト文字なので読み飛ばすようにします。
toyoさんありがとうございます。
上記の件試したいと思います。
あまりC初心者なので、エラー出しながらなので、返信遅くなると思います。
Re:全角文字列取得
Posted: 2008年8月12日(火) 19:58
by 御津凪
Visual C++6.0 で可能かどうか分かりませんが、以下の方法はどうでしょうか。
wchar_t buf[256];
swprintf(buf,L"%S",str);
もし実行時に日本語部分が出なければ、以下の処理を事前に実行しておくと出ると思います。
// ↓をインクルードする
// #include <locale.h>
set_locale(LC_ALL,"jpn");
MultiByteToWideChar でも上記処理は有効だと思います。
Re:全角文字列取得
Posted: 2008年8月13日(水) 14:19
by いけやん
返信、遅くなってすいません。
>Visual C++6.0 で可能かどうか分かりませんが、以下の方法はどうでしょうか。
>もし実行時に日本語部分が出なければ、以下の処理を事前に実行しておくと出ると思います。
御津凪さん参考にさして頂きます。
>自分ならstr.Mid( )で1バイトずつチェックして0x80以上なら2バイト文字なので読み飛ばすようにします。
リファレンスを見ながらやってみたのが
下記の警告がでます。
argument 'const int' から 'char' へ切り詰めます
引数で怒られているみたいなのですが、
使い方をご教授頂けないでしょうか?
#define String str2
while ( fin.ReadString(str) != NULL ) {
//MultiByteToWideChar(CP_THREAD_ACP,MB_PRECOMPOSED,str,256,buf,256);
if(str.Mid(2) >= 0x80 ){
str2 = str.Mid(1);
m_HndTree[0] = m_tree.InsertItem(String,TVI_ROOT);
m_HndTree[1] = m_tree.InsertItem(String,m_HndTree[0]);
Re:全角文字列取得
Posted: 2008年8月14日(木) 07:25
by toyo
test用のプログラムを作ってみました
参考になりますでしょうか
// 対象文字列
CString str = "2000|02-01-00| いい掛/うう";
// 抜き出し用文字列
CString str2;
// 対象文字列の長さ(バイト数で文字数ではない)だけ繰り返す
for (int i = 0; i < str.GetLength(); i++) {
// 区切り文字でない場所を探す
if (str == '+' || str == '-' || str == '|' ||
(str >= '0' && str <= '9') || str == ' ') {
continue;
}
// 文字列抜き出しの開始
str2.Empty();
while (i < str.GetLength()) {
// 区切り文字があればそこで抜き出し終了
if (str == '+' || str == '-' || str == '|' ||
(str >= '0' && str[i] <= '9') || str[i] == ' ') {
break;
}
// 抜き出し用の文字列に1バイト追加
str2 += str[i];
// 2バイト文字の場合は無条件でもう1バイト追加
// 0xA1-0xDFは半角カナで1バイトなので除外
if ((UCHAR)str[i] >= 0x80 && ((UCHAR)str[i] < 0xA1 || (UCHAR)str[i] > 0xDF)) {
i++;
str2 += str[i];
}
i++;
}
// 確認用のメッセージボックス表示
AfxMessageBox(str2, MB_OK, 0);
}
Re:全角文字列取得
Posted: 2008年8月14日(木) 07:36
by toyo
ちなみに2バイト文字判定は超手抜きですので
Re:全角文字列取得
Posted: 2008年8月14日(木) 12:20
by lbfuvab
2バイト文字を弾いてコピーする関数を作っては駄目ですか?
void Copy1ByteOnly(unsigned char *src,unsigned char *dest){ //オーバフロー対策はしてません。
while(*src){
if((*src>=0x81 && *src<=0x9F) || (*src>=0xE0 && *src<=0xFC)) //2byteなら
src++; //取り敢えず1byte進める
else
*dest++=*src;
src++;
}
*dest='\0';
}
Re:全角文字列取得
Posted: 2008年8月14日(木) 13:55
by いけやん
toyoさん、lbfuvabさん
サンプルありがとうございます。
すいませんが、上記のサンプルで解らない部分があり
自分的には、解らない所を解らないまま書くのは
良くないと思っているので、教えて頂けないでしょう?
①str2.Empty();
リファレンスを見ても「空の文字列を表します。このフィールドは読み取り専用です。 」
としか書いてありませんでした。
これはEmpty()関数で、str2を初期化する又は、空にするという解釈で合っていますか?
お願いします。
②0x80が2バイトなら・・・、0xA1-0xDFは半角カナで1バイト、等と書いてあるのですが
どこで、そういった事が解るのですか?
「文字コード」で検索してもありませんでした。
基本的な事なら、すいません。
教えて頂けないでしょうか?
「○○○」で調べて、出直して来いでも結構です。お願いします。
Re:全角文字列取得
Posted: 2008年8月14日(木) 16:32
by toyo
str2.Empty(); に関してはstr2の初期化(文字列を空にする)であっています。
str2 = "";
でも同じです。
文字コードの件ですが
「shift jis コード表」あたりで検索すると出てくるかもしれません
実際には1バイト目はlbfuvabさんのように
(*src>=0x81 && *src<=0x9F) || (*src>=0xE0 && *src<=0xFC)
のほうがより正確です。
厳密に言うと2バイト目も0x40~0x7E, 0x80~0xFCになるのをチェックしないといけないのでしょうが
今回の例ではASCII文字と半角カナ以外で始まる文字をすべて2バイト文字として扱いました。
Re:全角文字列取得
Posted: 2008年8月14日(木) 17:50
by いけやん
toyoさんありがとうございます。
shift jis コード表で調べてみます。
後、本当に勝手なのですが、
これから、ちょっと連休に入るのでサンプル等の返事が
来週の水曜なると思いますので
ご了承下さい。
Re:全角文字列取得
Posted: 2008年8月20日(水) 11:47
by いけやん
返信遅くなりました。文字列表示できました。
下記で解決しました。
if ( !fin.Open("部材.txt", CFile::modeRead, NULL ) ) {return -1;}
while ( fin.ReadString(str) != NULL ) {
toyoさんのサンプル
m_HndTree[0] = m_tree.InsertItem(String,TVI_ROOT);
}
toyoさんのサンプルを殆ど使わして頂きました。
shift jisの件,
調べてみるとあるページに下記の事が書いてありました。
制御コード
0x00~0x1F、0x7F
ASCII文字
0x20~0x7E
半角カタカナ
0xA1~0xDF
漢字
0x8140~0x9FFC、0xE040~0xFCFC
(第1バイト: 0x81~0x9F、0xE0~0xFC 第2バイト: 0x40~0x7E、0x80~0xFC)
すいません、もう少し聞きたいのですが
>実際には1バイト目はlbfuvabさんのように
>(*src>=0x81 && *src<=0x9F) || (*src>=0xE0 && *src<=0xFC)
>厳密に言うと2バイト目も0x40~0x7E, 0x80~0xFCになるのをチェックしないといけないのでしょうが
上記の事を自分なりに解釈すると、
もう少し厳密な、2byte漢字処理が必要という事ですか?
またそれは、何のために必要なんでしょうか?
お手数ですが、お願いします。
<!--1
Flying Fly
Posted: 2008年8月29日(金) 09:47
by lbfuvab
Bugさんのつくっていた「Flying Fly」と言うゲームを
友人に見せようと思ったのですがどこにおいてあるのでしょうか?
「その他のソフト紹介」の中にも見当たりません。<!--23-->
Re:Flying Fly
Posted: 2008年8月30日(土) 17:52
by 管理人
バグさんに、ここで紹介してもいいか聞いてみます。
もし許可が出たらダウンロードページに追加しようと思います。
Re:Flying Fly
Posted: 2008年9月01日(月) 11:53
by バグ
管理人さんへ返信しておきました。
プロジェクトも付けておきましたんで、悪用しなければ(どうやって?(笑))好きに使ってくださって結構ですよ(^-^)
Re:Flying Fly
Posted: 2008年9月01日(月) 12:58
by バグ
Re:Flying Fly
Posted: 2008年9月01日(月) 13:52
by 管理人
バグさん、ありがとうございます^^
その他のソフト紹介欄にFlyingFly追加させてもらいましたm(_ _)m