ページ 11

位分け

Posted: 2007年6月24日(日) 22:22
by さっかん
早速ですが、再び質問させていただきます;;

位分けってありますよね?例1000000000⇒1,000,000,000(位分けというのかわかりませんが;)
十桁までの数字を入れれば位分けをしてくれる関数を作りたいのですが、自分なりに作ってみたのですが途中でわからないところがあるのでご教授下さい。

数字を受け取る(例:1852078004)

4つに分ける。(1と852と78と4に分ける)
1000000000未満、1000000以上なら3つにわける。
1000000未満、1000以上なら2つにわける。
1000未満ならそのまま(負数は考えません)。

間に「,」を加えて数値は数字として文字配列に格納する。(a[0]:'1' a[1]:',' a[2]:852 a[3]:','・・・・・a[7]:'4')

0をつけて格納する(1⇒1、852⇒852、78⇒078、4⇒004)
【↑この方法がいまいち分かりません;ご教授お願いします!】

文字を返す

以上です。もしも回りくどいことをしているならもっと簡単な方法を教えて欲しいのですが、基本的なことしかわからないため、あまり高度な技は理解しにくいかもしれませんので、よろしくお願いします。

Re:位分け

Posted: 2007年6月24日(日) 22:50
by box
> 数字を受け取る(例:1852078004)

入力を数値で受け取ると、1つ問題点があります。

10桁までに対応するということは、0~9999999999
(99億9999万9999まで)に対応したいのですよね。
ところが、コンピュータの(32ビットの)世界では、
unsigned long型の最大値でも43億に満たない値です。

入力を文字列で受け取るようにすれば、少なくとも
この問題点からは解放されます。

入力する文字列は、通常、'0'~'9'の数字からなる長さが1~10の文字列です。
このルールに反する場合の対応も考えておく方がよいでしょう。

正しい仕様の文字列を受け取り、それを3桁ずつカンマで区切るには、
文字列の長さが
 ・1~3の場合
 ・4~6の場合
 ・7~9の場合
 ・10の場合
となるケースについて考えるか、または、文字列の長さを3で割ったあまりが
 ・0の場合
 ・1の場合
 ・2の場合
となるケースについて考えるか、あるいは両者を組み合わせるか、でありましょう。

Re:位分け

Posted: 2007年6月25日(月) 00:26
by 管理人
もし、コンマを表示したいだけなら下のように考えました。

数字で受りたいのならLONGLONG型を使ってみてはいかがでしょう。
windows.hをインクルードする必要がありますが。

また、文字列に格納してから必要なときにコンマをうちながら表示すれば簡単ではないでしょうか?
サンプル作ってみました。
きっともっといい方法があるのでしょうけど、パッと考えてこんな感じはどうかなと思ったので書きました。
#include <stdio.h>
#include <windows.h>

void conma(LONGLONG n){
	int i=0,j,s;
	char st[20];

	while(1){
		st[i++]='0'+n%10;	//一桁ずつ格納
		if(n<10)			
			break;
		n/=10;
	}
	i--;
	for(j=i,s=0;j>=0;j--,s++){
		printf("%c",st[j]);
		if(i%3==s%3 && j!=0)//iは桁数。3桁ずつコンマをうつ、かつ最後の行ではコンマをうたない
			printf(",");
	}
}

int main(){
	int i=0;
	LONGLONG n;
	n=10000000000;
	conma(n);
	return 0;
}
 
用意した配列の数より多くなりそうなら処理をやめたり、他必要なエラー処理は必要に応じて行ってください。

質問内容でよくわからないとおっしゃっている部分もサンプルにでてくる

st[i++]='0'+n%10; //一桁ずつ格納

この辺見ていただければ解決するのではないでしょうか。

Re:位分け

Posted: 2007年6月25日(月) 01:35
by さっかん
返信が遅くなってすいません><;
boxさんの言うとおり一回文字列に直して格納したほうが安全ですね!ルールに反した場合のエラー処理は一応elseの時という感じで用意してます。
管理人さんのプログラムをそのまま貼り付けてコンパイルしてみたら
warning:integer constant is too large for "long" type
という警告が表示されたんですが、これはLONGLONG型がlong型と読みとられたから格納しきれなかったのですか??
あと警告だけだったので実行できると思って実行したら、ほんの一瞬だけ10,000,000,000と出てすぐ消えてしまいました。この前にもこんなことがあって、その時は¥nで改行を入れたらちゃんと消えずに表示されたので、今回もforを出た後にprintf("\n");で改行を入れたらちゃんと表示されました。これはどういうことなんでしょうか??

Re:位分け

Posted: 2007年6月25日(月) 03:23
by 管理人
う~ん、、、そういう経験は無いのですが、、、
コンパイラは何ですか?
OS等環境を教えてもらえたら何か調べようがあるかもしれません。

Re:位分け

Posted: 2007年6月25日(月) 14:34
by さっかん
windowsでsygwinというものを使っています。
あと何度もすいませんが
warning:integer constant is too large for "long" type
という警告は上に書いたような意味としてとらえて良いのでしょうか?

Re:位分け

Posted: 2007年6月25日(月) 14:45
by バグ
試しに、LONGLONGの部分を__int64に書き換えてみて下さい。
テストなんで、うまく動くかどうか保証はありませんけど…(^_^;)

Re:位分け

Posted: 2007年6月25日(月) 14:46
by バグ
ひょっとしたら、64bit整数型がサポートされていない環境なのかもしれません…

Re:位分け

Posted: 2007年6月25日(月) 15:14
by Justy
 その警告は多分 gcc(cygwinでしょうかね)をつかっているからで、
n=10000000000の代入のところで、数字の部分を 10000000000LLとかにすれば
直るかもしれません。

Re:位分け

Posted: 2007年6月25日(月) 15:22
by 管理人
なるほど、LLをつける必要があるんですか^^;
しかし何故改行すると正常に表示されるんでしょうね・・。

Re:位分け

Posted: 2007年6月25日(月) 15:39
by Justy
>何故改行すると正常に表示されるんでしょうね・

 printfとかの出力ストリームって呼んだらすぐに表示されることは保証していません。
 
 大抵行単位でバッファリングしているので、その場合改行コードがあるか、
入力ストリームからの読み込みがあるとかしないと出力されません。

 なので、強制的に出力する fflush()があります。

Re:位分け

Posted: 2007年6月25日(月) 16:04
by さっかん
それ不思議なんですよ~^^;
一回LLつけてやってみますね!
あと次々と質問攻めで失礼ではあると思うのですが・・・

#include<stdio.h>
#include<string.h>
char place(int number,char buf[7][10]);
int main()
{
int x;
char y[7][10];
x=123456;
place(x,y);
printf("%s\n",y[0]);
return 0;
}
char place(int number,char buf[7][10]){
int a,b;
char num[7][10];
a=number*1000;
b=number-a*1000;
sprintf(num[0],"%d",a);
strcpy(num[1],",");
sprintf(num[2],"%d",b);
}

このプログラムを実行したら何も表示されないんですが、何故でしょうか?
あと今printfにy[0]を出力させているんですが、ひとつの%sに一気にy[0]とy[1]とy[2]を出力させることはできますか?
やはりfor文を使ったり、"%s%s%s",y[0],y[1],y[2]と書くしかないのでしょうか?
一気に質問してしまいましたが、よろしくお願いします!!

Re:位分け

Posted: 2007年6月25日(月) 16:08
by さっかん
>justyさん
残念ながら難しくて理解しにくいのですが、頭に叩き込んでおきます!
ご説明ありがとうございます!!

Re:位分け

Posted: 2007年6月25日(月) 16:35
by バグ
place関数内でオーバーフローしてますね。

123456 * 1000 * 1000 = 123456000000

この値はint型に収められる範囲を大きく超えてしまっています。
それで起こっている現象だと思われます。

Re:位分け

Posted: 2007年6月25日(月) 16:53
by box
> このプログラムを実行したら何も表示されないんですが、何故でしょうか?

place関数で定義しているnum[/url][/url]は、同関数の中だけで有効です。
sprintf関数やstrcpy関数を使ってセットした内容は、
place関数から抜けた時点で雲散霧消しています。

Re:位分け

Posted: 2007年6月25日(月) 17:00
by バグ
>>boxさん
あ、本当ですね…オーバーフローよりも、もっと根本的な部分に気付いてなかったとは…(^_^;)

Re:位分け

Posted: 2007年6月25日(月) 17:26
by さっかん
あ!numではなくてbufを使わなきゃいけませんよね!
初歩的なミスでした;すいません;;
えと、main内のprintfでyに入った物を全部表示させたいのですが、y[0]から一個ずつ%sで出していくしかないんですよね?

Re:位分け

Posted: 2007年6月25日(月) 17:49
by バグ
>>えと、main内のprintfでyに入った物を全部表示させたいのですが、y[0]から一個ずつ%sで出していくしかないんですよね?

うーん、2次元配列を1次元配列に変更すればいいかと思いますよ。どうしても2次元配列でないといけないのであれば、別に1次元のバッファを用意して、strcat関数等で文字列を連結させるとか…どうでしょう?

下記サンプルは前者の方法を採用したものです。
#include	<windows.h>
#include	<stdio.h>
#include	<string.h>

void place(LONGLONG number, char buf[4096]);

int main()
{
	LONGLONG x;
	char y[4096];

	x = 123456;
	place(x, y);
	printf("%s\n", y);

	return 0;
}

void place(LONGLONG number, char buf[4096])
{
	LONGLONG a, b;

	a = number * 1000;
	b = number - a * 1000;

	sprintf(buf,"%d, %d",a, b);
}

Re:位分け

Posted: 2007年6月25日(月) 17:57
by バグ
もう1つのサンプルです(^-^)
#include<stdio.h>
#include<string.h>

void place(unsigned long number,char buf[7][10]);

int main()
{
	unsigned long x;
	char y[7][10], z[70];

	x=123456;
	place(x,y);
	sprintf(z, "%s%s%s", y[0], y[1], y[2]);
	printf("%s\n", z);

	return 0;
}

void place(unsigned long number,char buf[7][10])
{
	unsigned long a, b;

	a = number * 1000;
	b = number - a * 1000;

	sprintf(buf[0], "%d", a);
	strcpy(buf[1], ",");
	sprintf(buf[2], "%d", b);
}

Re:位分け

Posted: 2007年6月25日(月) 18:24
by Justy
>sprintf(buf[0], "%d", a);
 aや bは unsigned longなので、"%d"ではなく "%lu"になります。

Re:位分け

Posted: 2007年6月25日(月) 19:05
by バグ
え?そうなんですか?(汗)

整数型は全部"%d"でいけるのかと思ってました(;^_^A

実際、今回のも普通に表示されていますし、これまでも不具合とか無かったので…(^^ゞ

Re:位分け

Posted: 2007年6月25日(月) 21:58
by さっかん
入力する数字が一億になると、yの配列が7つ(コンマを入れるため)必要になって配列を使う量が変わってくるので何個の時にも対応してprintfを使えるように、前者のサンプルを参考にしたいと思います。その前者のサンプルで
sprintf(buf,"%d, %d",a, b);
とありますが、%dと%dの間の,は数字の位分けするためのコンマを表しているんですよね?
あとbが4だった時に0を二つつけて004として格納しなければならないのですが、このように一次元配列の場合でも可能ですか?

Re:位分け

Posted: 2007年6月25日(月) 23:20
by box
書式文字列を決める際の参考になるでしょうか。

#include <stdio.h>

int main(void)
{
	int i, j, k;
	
	for (i = 0; i <= 1000; i += 1000) {
		for (j = 0; j <= 99; j++) {
			k = i + j;
			if (k < 1000)
				printf("%3d ", k);
			else
				printf("%d,%03d ", k / 1000, k % 1000);
			if (k % 10 == 9)
				putchar('\n');
		}
		putchar('\n');
	}
	return 0;
}

Re:位分け

Posted: 2007年6月26日(火) 01:13
by Justy
>実際、今回のも普通に表示されていますし、これまでも不具合とか無かったので…(^^ゞ
 まぁ、実際のところ sizeof(int) == sizeof(unsigned long)なら「大抵」は大丈夫ですが、
そうとも符号の有無も違うので内部の挙動が若干変わりますし、
long型が 8バイトな環境も多く存在しますので出来るだけフォーマット指定は
実際の型に合わせておいた方がいいかと思います。


http://www.itmedia.co.jp/enterprise/art ... 003_2.html

Re:位分け

Posted: 2007年6月26日(火) 21:12
by さっかん
返信が大分遅れてしまってすいません;;
皆さんアドバイス有難う御座います!少し難しくて分からないところもありますので、自分なりに調べて参考にさせていただきます。一応今夜頑張ってみます。また分からないところが出てきたらその時はよろしくお願いします。

Re:位分け

Posted: 2007年6月26日(火) 22:29
by 初心者A
こんなの作ってみました。
char	buff[256];
int		len;
int		i;

for(;;)
{
	fgets(buff, sizeof(buff), stdin);
	/* 後ろの余計な制御文字を除去 */
	for(i = (int)strlen(buff) - 1; i >= 0 && buff < ' '; i--)
	{
		buff = '\0';
	}
	/* 終了条件は改行のみ入力 */
	if(*buff == '\0')
	{
		break;
	}
	/* 頑張って表示して見る */
	len = (int)strlen(buff);
	for(i = 0; i < len; i++)
	{
		if((len - i) % 3 == 0 && i > 0)
		{
			printf(",");
		}
		printf("%c", buff);
	}
	printf("\n\n");
}

Re:位分け

Posted: 2007年6月27日(水) 00:25
by YuO
ほぼ与太話として見ていただければよいです。

えーっと,国際化対応のために<locale.h>というものがありまして,
その中に,localeconvという関数があります。

で,これで得られるstruct lconvには
・decimal_point
・thousands_sep
・grouping
という金額以外の値を整形出力するためのメンバが存在します。
# setlocaleされたLC_NUMERICで変更されます。

わかりやすいのはdecimal_pointで,アメリカや日本なら.ですが,フランスやドイツなどでは,だったりします。
# これは,printfでも確認できます。

ところが,これを実際に使うと死ぬほど面倒なことになります。
# groupingの取り扱いが面倒……。
で,便利な方法は……標準ライブラリには用意されていません。

時間があれば,挑戦してみるのも面白いと思いますよ。
ちなみに,別の掲示板の過去ログに,私が昔格闘した結果 (通貨なのでもっとややこしい) があったりします。
まぁ,退屈しのぎにでもなれば。
http://f4.aaa.livedoor.jp/~pointc/log679.html

Re:位分け

Posted: 2007年6月27日(水) 00:40
by さっかん
おかげさまでプログラムは完成しました!(がむしゃらに作ったので287行になりましたが;)
助けていただいた皆さんに心から感謝いたします。
また分からないところがあったらよろしくお願いします!

Re:位分け

Posted: 2007年6月27日(水) 05:19
by box
いったん投稿いたしましたが、
解決した後の投稿としては内容が不適切であるため、
お見せするのを控えます。
失礼いたしました。

Re:位分け

Posted: 2007年6月27日(水) 07:39
by さっかん
いえいえ、どうかお気になさらないで下さい。
boxさんも色々と有難う御座いました!