C言語の質問です。


文字列の中に全角スペースで区切った単語が複数あります。
全角スペースを検索することにより、単語ごとに出力したかったのですが上手く動作しませんでした。
出力する単語によっては文字化けが発生します。
文字化けしないようにするにはどうしたらよいでしょうか?
(文字コードはEUCです。)

>||
int main()
{
int i;
char words[100] = "飛行機 電車 train ";
char *pt1, *pt2

pt1 = pt2 = words;

for(i=0; i<3; i++){
pt2 = strstr(pt1, " ")
*pt2 = "\0";

printf("%s\n",pt1);
pt1 = pt2 + 2;
}
return 0;
}
||<

文字列を処理する関数で全角を利用するのに注意が必要であることは調べてわかったのですが、実際にどのように処理すればいいのかが調べきれませんでした。よろしくお願いします。

回答の条件
  • 1人2回まで
  • 登録:
  • 終了:2006/06/02 08:16:24
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

回答8件)

id:mark_hk No.1

回答回数31ベストアンサー獲得回数3

ポイント15pt

strstr関数は半角1文字だけ検索するので予想通りに動作しなかったと思います。

解決方法としては

1.文字列の間を「半角スペースにする」

2.pt2 = strstr(pt1, " ")の部分を

int j;

for (j = 0; j < strlen(pt1); ++j) {

if (0 == strncmp(pt1, " ", 2) {

pt2 = pt1;

break;

}

++pt1;

}

という風に全角文字列で比較するように変更する

のどちらかですかね。構造的には1の方が簡単ですね。

id:ktoshi

文字列の間はわけあって「全角スペース」しか使えないです。

2の方を明日試してみます。ありがとうございました。

2006/05/30 00:36:25
id:kazu1107 No.2

回答回数199ベストアンサー獲得回数14

ポイント5pt

このコードをそのまま動かしたらコンパイルすら出来ないと思いますが・・・。

まぁそこら辺は適当に修正して、コンパイルしてみました。

まず修正するべきは、for文の中にある「*pt2 = "\0";」で、これは「*pt2 = '\0';」で意図する結果がでると思います。

動作&コンパイル環境、その状況などがわからないので文字化けについてはコメントしづらいです。

id:ktoshi

すみません。

家だとコンパイルする環境を作ってないので、適当にコードを書いてしまいました。

知りたいのは文字化けのとこと、文字列を処理する関数での全角の扱い方ですので、よろしくお願いします。

ありがとうございました。

2006/05/30 00:39:42
id:Kumappus No.3

回答回数3784ベストアンサー獲得回数185

ポイント15pt

http://www.ncad.co.jp/~komata/c-kouza21.htm

こちらにstrstrではないですがstrtokを使ってEUCを処理するとハマるパターンについて書かれています。

文字化けするのはEUCのコードの中に0xa1が含まれていて、全角スペース(0xa1a1)と間違ってしまうケースですね。strstrだとたまたま0xa1a1という並びがあると文字の途中でも切れ目だと認識してしまうため文字化けします。

マルチバイト文字列を扱うのは結構大変です。標準的な(UNIX系な)Cであれば

http://always-pg.com/c/runtime_rd/string/mbstowcs.html

あたりにあるようにマルチバイト文字列をワイド文字列(2バイト)に変換して、ワイド文字列を扱うwcsstrなどで検索する必要があります。

http://always-pg.com/c/runtime_rd/string/strstr.html

Windows限定だと_mbsstrが使えそうですが。

http://www.microsoft.com/JAPAN/developer/library/vccore/_crt_str...

id:ktoshi

URLの方は明日、読ませてもらいます。

ありがとうございました。

ちなみに環境はLINUX+REDHATです(バージョンは分かりません)。gccでコンパイルしています。

2006/05/30 00:43:27
id:dungeon-master No.4

回答回数571ベストアンサー獲得回数40

ポイント30pt

1バイト見て文字種別を判断し、それが「2バイト文字の第1バイト」を示す値なら、

続く1バイトを拾って「2バイト文字の第2バイト」として扱う必要があります。

(同様に、3バイト文字領域を示すなら、続く2バイトを拾う)


「飛行機 電車(以下略)」という文字列は、バイナリ16進で1バイトずつ表すと

C8,F4,B9,D4,B5,A1,A1,A1,C5,C5,BC,D6 …と並んでいるはずです。

C8を見て2バイト文字の第1バイトであると判断し、続くF4とあわせて「飛」です。

F4は使ってしまったので、次はB9でこれも2バイト文字の第1バイトになりますから

次のD4と合わせて「行」、同様にB5,A1で「機」、A1,A1で全角スペースです。

問題のプログラムは、単語の区切りとして全角スペース=A1:A1を探していますが、

文字の区切りを考慮せずにstrstr()で探索しているため、先のような文字列では

期待している全角スペースにたどり着く前に「機」の第2バイトと全角スペースの

第1バイトのペアで、A1:A1を検知してしまうため失敗します。


1バイト文字か2バイト文字のみで構成される前提で、こんなかんじかと。

main()
{
 unsigned char words[100] = "飛行機 電車 train ";
 unsigned char *pt1,*pt2
 int i;
 pt1 = pt2 = words;
 for( i=0;i<3;i++ ){
  if( strcmp(pt2," ") ){
    pt2+= ( *pt2>0x80 )? 2:1;
  }else{
    *pt2=0;
    printf("%s\n",pt1);
    pt2+=2;
    pt1=pt2;
  }
 }
}

コンパイルもしていないので、動くかどうかわからんですけど。

id:ktoshi

試してみます。

詳しい説明ありがとうございました。

2006/05/30 08:01:10
id:noboru No.5

回答回数94ベストアンサー獲得回数0

ポイント20pt

文字化けする理由は「飛行機」の「機」の文字の euc-jp でのコードが 0xb5, 0xa1 で、全角スペースが 0xa1, 0xa1 だからです。これが繋がっているので「機」の第2バイトの位置で一致してしまいます。(「機(全角スペース)」は 0xb5, 0xa1, 0xa1, 0xa1 になっているため)。

ということで、正確に扱いたい場合は wchar_t 型に変換してから切り分けた方がいいと思います。

例) gcc の場合

#include <stdio.h>

#include <string.h>

#include <locale.h>

#include <stdlib.h>

#include <wchar.h>

int main()

{

char words[] = "飛行機 電車 train ";

wchar_t wc_words[20], *wp1, *wp2;

int i;

setlocale(LC_ALL, "");

mbstowcs(wc_words, words, 20);

wp1 = wc_words;

for (i = 0; i < 3; i++) {

if ((wp2 = wcschr(wp1, L' ')) != NULL) {

*wp2 = L'\0';

printf("%ls\n", wp1);

wp1 = wp2 + 1;

}

}

return 0;

}

id:ktoshi

wchar_t型というのは知りませんでした。

試してみます。

ありがとうございました。

2006/05/30 08:03:00
id:aki73ix No.6

回答回数5224ベストアンサー獲得回数27

ポイント30pt

参考までにdungeon-master さんのは

1.strcmp(pt2," ")→memcmp(pt2," ")

2.for( i=0;*pt2;i++ )→while(*pt2)

にすれば正常に動作します

・・・がEUCには3バイトコードもあるので、wordsの中に

不規則に文字が入るのであれば以下のほうが宜しいかと

		#include <stdio.h>
		void main()
		{
		 unsigned char *pt1,*pt2,pt3[256];
		 unsigned char words[] = "飛行機 電車 train ";
		 int i;
		 for(pt1=pt2=words;*pt2;){
		    if(*(short int*)pt2== *(short int*)" "  /* 0xa1a1  と同じ */ ){
		//!memcmp(pt2," ",2)の評価と同じ
		     i=pt2-pt1;
		     memcpy(pt3,pt1,i);
		     pt3[i]=0;
		     printf("%s\n",pt3);
		     pt1=pt2+2;
		   }
		   if(*pt2==0x8f)pt2+=3;
		   else if(*pt2>=0xa1||*pt2==0x8e)pt2+=2;
		   else pt2++;
		 }
		}
	

文字列の語尾に必ず全角スペースが入っているとは限らないのであれば

		#include <stdio.h>
		void main()
		{
		 unsigned char *pt1,*pt2,pt3[256];
		 unsigned char words[] = "飛行機 電車 train";
		 int i;
		 for(pt1=pt2=words;*pt2;){
		    if(*(short int*)pt2== *(short int*)" "/* 0xa1a1  と同じ */ ){
		//!memcmp(pt2," ",2)の評価と同じ
		     i=pt2-pt1;
		     memcpy(pt3,pt1,i);
		     pt3[i]=0;
		     printf("%s\n",pt3);
		     pt1=pt2+2;
		   }
		   if(*pt2==0x8f)pt2+=3;
		   else if(*pt2>=0xa1||*pt2==0x8e)pt2+=2;
		   else pt2++;
		 }
		 if(*pt1) 
		     printf("%s\n",pt1);
		}
	

の方が綺麗かもしれません

さらに、全半角対応にするならば

		#include <stdio.h>
		void main()
		{
		 unsigned char *pt1,*pt2,pt3[256];
		 unsigned char words[] = "飛行機 電車 train 船 クルマ 自転車";
		 int i,j;
		 for(pt1=pt2=words;*pt2;pt2+=j){
		   if(*pt2==0x8f)j=3;
		   else if(*pt2>=0xa1||*pt2==0x8e)j=2;
		   else j=1;
		   if(*(short int*)pt2== *(short int*)" " ||*pt2==' ' /* 0xa1a1  と同じ */ ){
		//!memcmp(pt2," ",2)の評価と同じ
		     i=pt2-pt1;
		     memcpy(pt3,pt1,i);
		     pt3[i]=0;
		     printf("%s\n",pt3);
		     pt1=pt2+j;
		   }
		 }
		 if(*pt1) 
		     printf("%s\n",pt1);
		}
	

となります

ソース

参考

http://web.hc.keio.ac.jp/~fujimura/lang/page-8-6.html

id:ktoshi

今日はみなさんの回答を参考に以下のようにしてみました。

>||

int main()

{

unsigned char words[100] = "飛行機 電車 train ";

unsigned char *pt1,*pt2;

int i;

pt1 = pt2 = words;

while(1){

pt2 += ( *pt2>0x80 ) ? 2 : 1;

if( strncmp(pt2, " ", 2)==0){

*pt2 = '\0';

pt2 += 2;

printf("%s\n",pt1);

pt1 = pt2;

}

else if(*pt2 == '\0'){

break;

}

}

return 0;

}

||<

動作させるとまだ怪しげな感じがします。(実際の処理はサイズの大きなCSVファイルを読み込んで1行ずつ処理させています。)

aki73ixさんの回答はいま見たところなので、明日試してみようと思います。

ありがとうございました。

>|| ~ ||< でソースを表示させたいのですが、、、、何故ちゃんと表示されないのだろう。

2006/05/30 21:30:03
id:kumonoyouni No.7

回答回数612ベストアンサー獲得回数131

ポイント5pt

詳しい説明省きますが、

http://ash.jp/ash/src/code/

を参考にされると理解が早いと思います。

id:ktoshi

すみません。

URLを見させて頂きましたが、あまり質問内容とは関係ないかと思います。文字コードの判定について書かれていると思うのですが、使用している文字コードはEUCですので、特に文字コード判定云々については気にしていません。

でも、ありがとうございました。

2006/05/30 21:34:57
id:kumonoyouni No.8

回答回数612ベストアンサー獲得回数131

ポイント10pt

すみません、追記します(ポイント結構です)

文字コードについてリンクおかしいようでしたので、説明書いたページを補足します。


「文字コードについて」という部分の

http://ash.jp/code/index.htm

説明を見ると

>・・・がEUCには3バイトコードもあるので、wordsの中に

という部分もお分かりかと思いますし、ASCII文字も考慮しなければなりませんし、ダンプとあわせてみれば何が悪いのか理解早まると思われます。



"飛行機 電車 train "という文字列は

C8 F4 B9 D4 B5 A1 A1 A1 C5 C5 BC D6 A1 A1 74 72 61 69 6E A1 A1

となっているはずです。


以上、ご参考まで

id:ktoshi

参考になります。

ありがとうございました。

2006/05/31 12:45:15

コメントはまだありません

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません