空白文字について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら

空白文字について

#1

投稿記事 by » 17年前

int c;
c=fgetc(fp);
if((c != '\n') && (c != ' ') && (c != ' ') ){ //スペースが見つかったとき、行を変える

}
としたとき、全角のスペースだけが無視されてしまいます。
どなたか、ご教授お願いします。

!(isspace(c))ではできませんでした。
以下ソースコード(必要ないと思いますが…
void Load_File::get_file(){
	int c;
	char str[256];
	FILE *fp;
	
	if( (fp =fopen("test.txt", "r")) == NULL ){
		cout << "エラー"<<endl;
	}
	
	int i=0,j=0;
	bool frag=false;
	while( (c = fgetc(fp)) != EOF ){
		memset( &n_v, NULL, sizeof(n_v));
		if((c != '\n') && (c != ' ') && (c != ' ') ){	//スペースが見つかったとき、行を変える
			str[j]=c;
			j++;
		}else
			frag = true;

		if( frag==true ){
			if(j>0){
				str[j]='\0';
				memcpy( n_v.ss , str , sizeof(str) );	//文字列のコピー
				v.push_back(n_v);	//vへの追加
				i++;
			}
			j=0;
			frag=false;
		}
	}
	fclose(fp);
}
<!--12-->

box

Re:空白文字について

#2

投稿記事 by box » 17年前

> if((c != '\n') && (c != ' ') && (c != ' ') ){	//スペースが見つかったとき、行を変える

この判定方法はまずいでしょうね。
例えばシフトJISの場合、全角空白はx8140の2バイトで表わします。
cがx81だったら、次の1文字を読んで、それがx40だったとき、
当該2バイトが全角空白であると判断できると思います。

たかぎ

Re:空白文字について

#3

投稿記事 by たかぎ » 17年前

ロケールを正しく設定した上で、
wint_t c; 
c=fgetwc(fp); 
if((c != L'\n') && (c != L' ') && (c != L' ') ){
とすれば上手くいく可能性があります。
ただし、厳密には環境に依存しますので、まずは環境を明らかにしてください。

Re:空白文字について

#4

投稿記事 by » 17年前

以下のようにすると、できました。
ありがとうございます。
0x81 は全角文字をあらわすため、後ろにスペースなどは来ず、
以下のような処理をしてしまっても良いのでしょうか?
** ソースコードの一部 **
int i=0, j=0, space=0;
bool frag=false;
while( (c = fgetc(fp)) != EOF ){
	if( !(isspace(c)) ){	//スペースなどが見つかったとき、行を変える
		if( !(c == 0x81)){
			str[j]=c;
			j++;
		}else{
			space = c;
			if((c=fgetc(fp))==0x40){
				frag = true;
			}else{
				str[j]=space;
				j++;
				str[j]=c;
			}
		}

box

Re:空白文字について

#5

投稿記事 by box » 17年前

00:09のコードでは変数frag(旗という意味ならflagです)の使い方が
わかりませんので、何ともいえません。

Re:空白文字について

#6

投稿記事 by » 17年前

>たかぎさん
回答ありがとうございます。

>厳密には環境に依存しますので、まずは環境を明らかにしてください。
Windows2000以上では問題なく動作してほしいと思っていますが、それ以外にも大きく影響を受けるのでしょうか?

>ロケールを正しく設定した上で、
どのような設定が必要なのかわからないので、教えてはいただけないでしょうか?

Re:空白文字について

#7

投稿記事 by » 17年前

ご回答ありがとうございます。
frag は flag のつもりで使っていました。
教えていただきありがとうございます。

以下のような方法で、使用環境(例えばパソコンを買い換えるとか)をした場合には問題が生じる可能性があるのでしょうか?
void Load_File::get_file(){
	int c;
	char str[256];
	FILE *fp;
	
	if( (fp =fopen("test.txt", "r")) == NULL ){
		cout << "エラー"<<endl;
	}
	
	int i=0, j=0, space=0;
	bool flag=false;
	while( (c = fgetc(fp)) != EOF ){
		if( !(isspace(c)) ){	//スペースなどが見つかったとき、行を変える
			if( !(c == 0x81)){
				str[j]=c;
				j++;
			}else{
				space = c;
				if((c=fgetc(fp))==0x40){
					flag = true;
				}else{
					str[j]=space;
					j++;
					str[j]=c;
					j++;
				}
			}
		}else
			flag = true;
		
		if( flag==true ){
			if(j>0){
				str[j]='\0';
				memcpy( n_v.ss , str , sizeof(str) );	//文字列のコピー
				v.push_back(n_v);	//vへの追加
				memset( &n_v, NULL, sizeof(n_v));	//コピー後初期化
				i++;
			}
			j=0;
			flag=false;
		}
	}
	fclose(fp);
}

たかぎ

Re:空白文字について

#8

投稿記事 by たかぎ » 17年前

> Windows2000以上では問題なく動作してほしいと思っていますが、それ以外にも大きく影響を受けるのでしょうか?

コンパイラとコンパイルオプションの影響を受けます。

> どのような設定が必要なのかわからないので、教えてはいただけないでしょうか?

setlocaleを使います。

Re:空白文字について

#9

投稿記事 by » 17年前

ご回答ありがとうございます。
コンパイラはVC++2005
コンパイルオプションは設定の変更をしていません。
プロジェクトのプロパティの文字セットをマルチバイトに変更しています。

setlocaleは初めて知ったので、検索してわからないところがあったとき、また質問させてもらってもよろしいですか?

Re:空白文字について

#10

投稿記事 by » 17年前

wint_t c;
c=fgetwc(fp);
if((c != L'\n') && (c != L' ') && (c != L' ') ){

以上の設定をしたら、全角空白は除けたようなのですが、ワイド文字の変換はうまくできなかったので(僕の力不足です)、ワイド文字を使用しないですることはできないでしょうか?

たかぎ

Re:空白文字について

#11

投稿記事 by たかぎ » 17年前

> ワイド文字を使用しないですることはできないでしょうか?

ワイド文字を使わないのであれば、多バイト文字として処理するしかありません。
非常に面倒なので、ワイド文字を使うほうが楽だと思うのですが...

Re:空白文字について

#12

投稿記事 by » 17年前

setlocale(LC_CTYPE, "jpn");
をメインループで宣言しています。

以下のように設定したのですが、MB_CUR_MAXが使用できませんでした。
<stdlib.h>をインクルードすれば良いと書いてあったので↓(多分
http://www.linux.or.jp/JM/html/LDP_man- ... MAX.3.html

#include <iostream>
#include <vector>
#include <stdlib.h> //atof, ワイド文字
#pragma warning(disable : 4996)
using namespace std;

#define str_size 256


がインクルード&宣言してあります。

後、ワイド文字とは関係ないと思うのですが、
void Load_File::disp(){
	for(int i=0; i< (int)v.size(); i++)
		//cout<< "["<< i <<"]:\t"<< v.ss<<endl;
		printf("%s\n",v.ss);
}

この、 // でコメント化しているのを実行すると、表示されません。
この理由も合せて教えていただけないでしょうか?

** 以下ソースコードの一部 **
不明箇所色の変更
void Load_File::get_file(){
	FILE *fp;
	
	if( (fp =fopen("test.txt", "r")) == NULL ){
		cout << "エラー"<<endl;
	}


	wint_t c; 
	char str[str_size],use_wstr[ 5 ]={0}; //MB_CUR_MAXが使用できない
	int j=0, space=0, w;

	//flag : 配列のコピーをするかどうか	: true 行う
	bool flag=false;	
	
	while( (c=fgetwc(fp)) != WEOF ){
		if((c != L'\n') && (c != L' ') && (c != L' ') ){
			w = wctomb(use_wstr,c);
			for(int k=0; k<w; k++){
				str[j]=use_wstr[k];
				j++;
			}
		}else{
			flag = true;
		}
		if( flag==true ){
			if(j>0){
				str[j]='\0';
				memcpy( n_v.ss , str , sizeof(str) );	//文字列のコピー
				v.push_back(n_v);	//vへの追加
				memset( &n_v, '\0', sizeof(n_v));	//コピー後初期化
				memset( str, '\0', sizeof(str));
			}
			j=0;
			flag=false;
		}
	}
	fclose(fp);
}

たかぎ

Re:空白文字について

#13

投稿記事 by たかぎ » 17年前

> setlocale(LC_CTYPE, "jpn");
> をメインループで宣言しています。

Visual C++の場合、"jpn"ではなく"Japanese"だったと思います。OSが日本語設定であれば、""でも大丈夫なはずです。

> 以下のように設定したのですが、MB_CUR_MAXが使用できませんでした。
> char str[str_size],use_wstr[ 5 ]={0}; //MB_CUR_MAXが使用できない

↑これはダメです。MB_CUR_MAX は定数式ではないので、配列の要素に使用することができません。
代わりに、<limits.h> または <climits> で定義される MB_LEN_MAX を使用してください。

ところで、いったん wctomb で多バイト文字に変換していますが、こんなことをしなくても、全部ワイド文字で扱えばよいかと思います。
str を char の配列ではなく wchar_t の配列にするか、C++ であれば std::wstring にするのもよいでしょう。


> //cout<< "["<< i <<"]:\t"<< v.ss<<endl;
> この、 // でコメント化しているのを実行すると、表示されません。

v の型が不明なので正確なことはわかりません。
正確な情報を教えてください。

Re:空白文字について

#14

投稿記事 by » 17年前

ご回答ありがとうございます。

vは構造体のvectorです。
typedef struct vec{
	char ss[str_size];
}VEC;

vector<VEC> v;
setlocale(LC_CTYPE, "jpn");をデバッカで見ると
setlocale が返されました。 0x00a161a4 "Japanese_Japan.932"
と表示されるので、問題ないのではないでしょうか?

>↑これはダメです。MB_CUR_MAX は定数式ではないので、配列の要素に使用することができません。
>代わりに、<limits.h> または <climits> で定義される MB_LEN_MAX を使用してください。
できました。ありがとうございます。

>ところで、いったん wctomb で多バイト文字に変換していますが、こんなことをしなくても、全部ワイド文字で扱えばよいかと思います。
>str を char の配列ではなく wchar_t の配列にするか、C++ であれば std::wstring にするのもよいでしょう。

ワイド文字は良くわからず、charで対応できるならそれで良いか、と考えていました。
質問なのですが、wchar_t と char はどのように違うのでしょうか?同じように使用してしまってもいいのでしょうか?
char は
char ss[10][256]; などとしてよく使用しているのですが、これを wchar_t ss[10][256] と行っても問題はないのでしょうか?
手元にある本では
「C言語入門者が一般のプログラミングでこれらの機能を活用する機会は少ないものです。ここでは知識として紹介します」
新訂 新C言語入門~シニア編~より

と書いてあり、説明も一ページぐらいだったので、使わないほうが良いのでは?と思っているのですが、ワイド文字を利用したほうがいいのでしょうか?
利用したほうが良いのであれば、ワイド文字が分かりやすそうな本を教えていただけませんか?
(他にもプログラマーになるのであれば是非読んでおいた方が良い本もできればお願いします。)

wstring ( つまり、string ) は全く理解していないので使用を避けています。
C++はアクセス制御子がなんとなくわかる程度なので、良さそうな参考書を探している最中です。
今参考にしているのは、猫でもわかる(ネット上)と14歳からはじめるC++ その他ウェブページです。
こちらも、よろしければお勧めを教えていただけないでしょうか?

たかぎ

Re:空白文字について

#15

投稿記事 by たかぎ » 17年前

> setlocale(LC_CTYPE, "jpn");をデバッカで見ると
> setlocale が返されました。 0x00a161a4 "Japanese_Japan.932"
> と表示されるので、問題ないのではないでしょうか?

ここは記憶が不確かなので、問題ないならそれでよいと思います。

> 質問なのですが、wchar_t と char はどのように違うのでしょうか?

Visual C++に限れば、wchar_t は Unicode を扱うための型です。サロゲートを無視すれば、漢字なども1要素として扱うことができるので非常に楽になります。

> 同じように使用してしまってもいいのでしょうか?

より簡単になるという点、そして、ワイド文字(列)の場合はそれ専用の関数を使う必要がある点がことなります。例えば、ワイド文字列のコピーは strcpy ではなく wcscpy を使用します。
std::wstring を使うのであれば、std::string と同じメンバを備えていますので、ほぼ同じに扱えると考えてよいでしょう。

> char ss[10][256]; などとしてよく使用しているのですが、これを wchar_t ss[10][256] と行っても問題はないのでしょうか?

問題ありません。

> 「C言語入門者が一般のプログラミングでこれらの機能を活用する機会は少ないものです。ここでは知識として紹介します」
> 新訂 新C言語入門~シニア編~より

それはその著者の解釈です。
入門者云々ではなく、数年前まではワイド文字(列)用ライブラリのサポート状況が必ずしもよくありませんでした。Visual C++はまだよかったのですが、GCCなどでは使えないことが多々あったのです。しかし、現在では主要な処理系はほぼサポートが行き届くようになりましたので、移植性の観点からも大きな問題はなくなりました。

> 利用したほうが良いのであれば、ワイド文字が分かりやすそうな本を教えていただけませんか?

何も専用の本を読むほどのことではありません。
ライブラリのドキュメントを読めば十分です。

ワイド文字(列)を使うときの注意点としては、

1. リテラルを書くときに L を付ける(L'あ', L"漢字" など)。
2. 専用のライブラリを用いる。
3. 稀にワイド文字列が使えない関数がある(fopen など)。
4. 外部に対する入出力ではロケールを設定する必要がある。
5. テキストストリームとバイナリストリームの区別がない環境でも、ワイド文字(列)入出力では違い出る。

ぐらいです。

Re:空白文字について

#16

投稿記事 by » 17年前

丁寧なお答えありがとうございます。
これから、色々な所をワイド文字に変更していきたいと思います。

とても初歩的な質問なのですが、
>ライブラリのドキュメント
は何を指しているのでしょうか?
デバッカで追っていけるものでいいのでしょうか?
MSDNのHPにある、ライブラリのことなのでしょうか?
今まで、新しい関数は本でたまたま見つけたり、掲示板や知り合いに聞いたり、WEBページからリンクでどんどん見つけていく方法しかとったことがありません。
方法があれば是非教えてください。

>サロゲートを無視すれば
少し調べてみたのですが、まだ理解が及ばなさそうなので無視したいのですが、気づかないうちに問題が生じたりするものなのでしょうか?
このあたりまで理解しないとワイド文字は使用しないほうがいいのでしょうか?

たかぎ

Re:空白文字について

#17

投稿記事 by たかぎ » 17年前

> >ライブラリのドキュメント
> は何を指しているのでしょうか?

これについては、Visual C++に限れば

> MSDNのHPにある、ライブラリのことなのでしょうか?

と考えてよいでしょう。
移植性を考えるなら、言語規格に目を通す必要があります。

> このあたりまで理解しないとワイド文字は使用しないほうがいいのでしょうか?

よほど特殊な文字(古代や中世の文字など)を扱うのでなければ、サロゲートを気にすることはないでしょう。一応、そのようなものがあることだけ、記憶の片隅に置いておくことをお勧めします。

Re:空白文字について

#18

投稿記事 by » 17年前

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

がんばって使用していきたいと思います。

Re:空白文字について

#19

投稿記事 by » 17年前

ファイルのロードをすべてワイド文字で出来るようにしてみようとしたのですが、
wmemcpy( n_v.ss, str, sizeof(n_v));
この位置で
rep movsd ;N - move all of our dwords
この場所に飛んでエラーとなるのですが、データが全部移行できる領域がないと解釈して
wsccpy( n_v.ss , str);
にしたところ、今度は関数終了時に、strが破損した、見たいなエラー(↓)
Stack around the variable 'str' was corrupted
と表示されます。

以上の点について、教えていただけないでしょうか?


/* 以下ソースファイルの一部(問題の関数) */
void Load_File::get_file(){
	FILE *fp;
	
	if( (fp =fopen("test.txt", "r")) == NULL ){
		cout << "エラー"<<endl;
	}

	setlocale(LC_CTYPE, "jpn");

	wint_t c; 
	wchar_t str[str_size]={0},use_wstr[ MB_LEN_MAX ]={0};
	//wstring str;
	int j=0, space=0, w=0;

	//flag : 配列のコピーをするかどうか	: true 行う
	bool flag=false;
	
	while( (c=fgetwc(fp)) != WEOF ){
		if((c != L'\n') && (c != L' ') && (c != L' ') ){
			str[j] = c;
			j++;
		}else{
			flag = true;
		}
		if( flag==true ){
			if(j>0){
				str[j] = L'\0';
				wmemcpy( n_v.ss, str, sizeof(n_v)); 
				v.push_back(n_v);	//vへの追加
				memset( &n_v, '\0', sizeof(n_v));	//コピー後初期化
				wmemset( str, L'\0', sizeof(str) );
			}
			flag=false;
			j=0;
		}
	}
	
	fclose(fp);
}

たかぎ

Re:空白文字について

#20

投稿記事 by たかぎ » 17年前

> wmemcpy( n_v.ss, str, sizeof(n_v));

↑は、意味的には

wcscpy( n_v.ss, str );

ではないのですか?
どうしても wmemcpy を使うのであれば、

wmemcpy( n_v.ss, str, sizeof(n_v.ss)/sizeof(n_v.ss[0]) );

とすべきです。

Re:空白文字について

#21

投稿記事 by » 17年前

ご回答ありがとうございます。

はい。
動作としては、wcscpy();と同じ事をしようとしていました。
>wmemcpy( n_v.ss, str, sizeof(n_v.ss)/sizeof(n_v.ss[0]) );
文字の個数であることをすっかり忘れてしまっていました。
ありがとうございます。

閉鎖

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