アプリ版:「スタンプのみでお礼する」機能のリリースについて

こんばんは、夜分遅くに失礼します。
タイトルの通り、C言語の桁数指定について質問です。

※数値の桁数が %5d などによって指定できる事は把握しています。

私が今書いているコードで、
「入力された数値の桁数によって、出力する際に見やすくなるよう桁数を指定する」
という事を試行しているのですが、
if(max>99){ 処理1
if(max>999){ 処理2
if(max>9999){ 処理3
if(max>99999){ 処理4

などのように場合分けして書くのは煩わしいと感じたので
define NUM 5 → %NUM5
などで代用できるか…?と思い試したところ案の定出来ませんでした。

ここでお聞きしたいのですが、場合分けせずに一つの処理で簡潔に書ける方法はありますでしょうか?
又、もし無い場合私の方法とは別で、何かいい方法はありますでしょうか?

何卒お力添えお願いします m(_ _)m

(文が読み辛かったり、誤字脱字等があれば大変申し訳ないのですが
ご指摘いただけるとありがたいです)

質問者からの補足コメント

  • 追記:アドバイスに沿ってソースコードを貼り付けます。
    OS:Windows10、 ソフト:VScode
    途中で穴に気づいた為修正しております。

    「入力された最小値、最大値、個数に基づいて乱数を出力」

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    int random(int max, int min){
    return (rand()%(max-min))+min;
    }
    int main(void){
    int min; /*乱数の最小値*/
    int max; /*乱数の最大値*/
    int number; /*乱数の個数*/
    int x=0; /*カウント用変数*/
    printf("min > "); scanf("%d",&min);
    ↓文字数オーバーの為次に続きます…

      補足日時:2022/05/27 00:19
  • printf("max > "); scanf("%d",&max);
    printf("number > "); scanf("%d",&number);

    if(min>=max){
    print("error\n");
    }

    srand((int)time(NULL)); /*乱数リセット*/

    for(int i=0;i<number;i++){
    if(x==10) printf("\n"); /*10個ごとに改行*/
    if(max<10) printf("%2d",random(max,min));
    if(max>9 && max<100) printf("%3d",random(max,min));
    ↓続きます…2

      補足日時:2022/05/27 00:23
  • if(max>99 && max<1000) printf("%4d",random(max,min));
    if(max>999 && max<10000) printf("%5d",random(max,min));
    if(max>9999 && max<100000) printf("%6d",random(max,min));
    if(max>99999 && max<100000) printf("%7d",random(max,min));
    if(max>999999) printf("%d ",random(max,min));
    x++;
    }
    return 0;
    }

    読みづらいコードですみません…

      補足日時:2022/05/27 00:23
  • つらい・・・

    すみません、if(){ printf("error")}のところは exit(0) が抜けてます…
    改行していたのですが、それも無くなってしまってます…(なんでだろ)

    駄文ですみません…

      補足日時:2022/05/27 00:30

A 回答 (8件)

printf のフィールド幅は '*' を使えば引数からも指定できます


例)
printf( "%*d", フィールド幅, 値 );

対数と丸めを活用すればフィールド幅を計算式で表せます
例)
フィールド幅 = 2 + (int)floor(log10(最大値));
最大値 = 9 → フィールド幅 = 2
最大値 = 10 → フィールド幅 = 3
最大値 = 99 → フィールド幅 = 3
最大値 = 100 → フィールド幅 = 4

参考)
https://linuxjm.osdn.jp/html/LDP_man-pages/man3/ …
https://linuxjm.osdn.jp/html/LDP_man-pages/man3/ …
https://linuxjm.osdn.jp/html/LDP_man-pages/man3/ …
    • good
    • 0
この回答へのお礼

No.8 さん(ベストアンサー)
%*dで指定…なるほど、そんなことができるんですね(;゚Д゚)
まさに私が求めていた方法です。ありがとうございます!

No.6の方も同じ事を書いてくださっていましたが、No.8さんが
コード書き方など詳しく書いて下さったのでこちらを
ベストアンサーとさせてください(。-人-。)

まだまだ分からない関数等沢山ありますが、少しずつ頑張ります。
皆様御回答ありがとうございました!

お礼日時:2022/05/27 21:09

以下のようにしてはいかがでしょうか。


maxの桁数をstrlenで取得し、(maxが3桁なら)formへ"%4d"
の文字列を作成し、それを乱数印字時の書式として使います。
------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h> //追加
int random(int max, int min)
{
return (rand() % (max - min)) + min;
}
int main(void)
{
int min; /*乱数の最小値 */
int max; /*乱数の最大値 */
int number; /*乱数の個数 */
int x = 0; /*カウント用変数 */
char form[16]; //追加
char digit[16]; //追加
printf("min > ");
scanf("%d", &min);
printf("max > ");
scanf("%d", &max);
sprintf(digit,"%d",max); //追加
sprintf(form,"%%%dd",strlen(digit)+1); //追加
printf("number > ");
scanf("%d", &number);

if (min >= max) {
printf("error\n");
exit(0);
}

srand((int) time(NULL)); /*乱数リセット */

for (int i = 0; i < number; i++) {
if (x == 10)
printf("\n"); /*10個ごとに改行 */
printf(form, random(max, min)); //修正
x++;
}
return 0;
}
    • good
    • 0
この回答へのお礼

No.7 さん
strlen()とsprintf()の使用、なるほど…
sprintf()をあまり使用しないので考えつきませんでした(´・ω・`)
使い方等を熟知した上で考えさせてもらいます…!

お礼日時:2022/05/27 21:08

#include <math.h>



printf("%*d",(int)log10l(max-1)+1,random(max,min));

でしょうか。桁数を求めるのに対数関数を使っており、誤差が出るかもしれないので、境界の数値でテストしてみてください。たぶん大丈夫だと思いますが。
    • good
    • 1
この回答へのお礼

No.6 さん
対数で桁数を取得、なるほど…
言われてみればそれなら確かにコードが少なくて済みますね
参考にさせていただきます(*´▽`*)

お礼日時:2022/05/27 21:08

あ、ごめん、手が滑ってヘンな事書いちゃったや。


#4 の

strtod(buf, NULL);

って書いてるトコは全部

strtol(buf, NULL, 10);

です。
何やってんだろ。

あと、オリジナルの

> if(x==10) printf("\n"); /*10個ごとに改行*/

ですが、forループの中にあるiを利用した方がいいです。
基本的iを10で割った余りが0になる時、10個表示し終えた、って事なんで、そこで改行させます。
    • good
    • 0

うん、O.K.



まずね、もっとモダンな言語だと、恐らくハッシュテーブルとか使って処理するような事になると思う。
それで条件分岐を避けてprintfの文字列にハメるのね。

ただ、C言語はハッシュテーブルみたいなイケてるデータ型を持ってないんで、例えばこんな風に実装してみる。

/* ここから */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
#include <string.h>

#define N 6

/* テーブル要素 */
typedef struct elm_ {
 int v;
 char* s;
} Elm_t;

/* データ変換テーブル */
Elm_t table[] = {{1, "%2d"}, {2, "%3d"}, {3, "%4d"},
       {4, "%5d"}, {5, "%6d"}, {6, "%7d"}};

/* 検索関数 */
Elm_t assoc(int key, Elm_t* table, size_t size) {
 for (size_t i = 0; i < size; i++) {
  if (key == table[i].v) {
   return table[i];
  }
 }
 Elm_t false_result= {false, "%d"};
 return false_result;
}

/* random と言う名前は衝突するので使えない(stdlib.h) */
int rndm(int max, int min) {
 return rand() % (max - min) + 1;
}

int main(void) {
 int min; /* 乱数の最小値 */
 int max; /* 乱数の最大値 */
 int number; /* 乱数の個数 */
 int x;
 char buf[10]; /* バッファ */
 printf("min > "); scanf("%9s%*[^\n]", buf);
 getchar(); min = strtod(buf, NULL);
 printf("max > "); scanf("%9s%*[^\n]", buf);
 getchar(); max = strtod(buf, NULL);
 printf("number > "); scanf("%9s%*[^\n]", buf);
 getchar(); number = strtod(buf, NULL);

 if (min >= max) {
  puts("error");
  return EXIT_FAILURE;
 }

 srand((int)time(NULL));

 for (int i = 0; i < number; i++) {
  if ((i != 0) && (i % 10 == 0)) {
   puts("");
  }
  x = rndm(max, min);
  snprintf(buf, 10, "%d", x);
  printf(assoc(strlen(buf), table, N).s, x);
 }
 puts("");
 return EXIT_SUCCESS;
}

/* ここまで */

まず構造体でデータ要素を作る。数値と文字列を持ったデータ型だな。
それでデータテーブルを作る。1桁と"%2d"、2桁と"%3d"、・・・と言うカンジでペアのデータをセットしていく。
キーは数値で、例えば1桁の「1」が来ると{1, "%2d"}と言う要素(構造体)を返すような検索関数assocを作る。見つからなかった場合には{false, "%d"}と言う構造体で作ったペアが買える。

あとは、桁数判定のティップス。桁数数えるのが面倒なんで、一旦数値を文字列に直しちゃう。その文字列の「長さ」が桁数に一致する。
それを利用して、検索関数からハメたい文字列を持ってきて、ってやれば条件分岐は避けられます。
    • good
    • 0
この回答へのお礼

No.4 さん
構造体を利用、なるほど…
・for文のiを利用して改行…確かにそちらの方がスマートな感じですね!
・数値を一旦文字列に直したりすると色々便利になるんですね。
 今回の場合それで条件分岐を避けられる、と。
・関数名の衝突は確かに避けなければいけませんね、失念しておりました。
 次回以降気を付けます( ..)φメモメモ

ご丁寧な説明ありがとうございます!
じっくり読んで今後の参考にさせていただきます…!

お礼日時:2022/05/27 21:08

そういう関数を作ればよいのではないでしょうか



/////////////////////////////////////////////////////////////

#include <stdio.h>
#include <math.h>

void printdigit(int value, int max){
for(int i = pow(10, max-1); i != 0; i = (i/10)){
if(value > i){
int digit = (value / i) % 10;
printf("%d", digit);
}else{
printf(" ");
}
}
}

int getdigit(int value){
for(int i = 1; ; i++){
value = (value/10);
if(value == 0) { return i; }
}
}

int main(void){
int input = 123456;
int value = 123;

printdigit(value, getdigit(input));
}
    • good
    • 0
この回答へのお礼

No.3 さん
pow()と除算を利用、なるほど…
この手法は自分ではとても思いつきませんでした。
参考にさせていただきます(*´▽`*)

お礼日時:2022/05/27 21:08

書式文字列を変数で指定する.

    • good
    • 1

ネタ的には面白そうなんだけど、この場合は書いたコードが全部あった方がいいかも。



あと、多分その分岐処理じゃ上手く行かないんじゃないかしらん。
例えば6桁の数値があった時点で、最初の条件節が処理されちゃって上手く行かないんじゃない?
条件を逆に99999から初めて小さくしていくべきなんじゃないか、って思う。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!