質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.32%

C言語で指定されたデータ型に合わせて入力を受けるには

解決済

回答 6

投稿

  • 評価
  • クリップ 1
  • VIEW 481

wanwannyaan

score 19

データ型 データ
int 6
char k
float 3.11685


上記のような入力を想定します。
この入力で指定されたデータ型で変数を宣言し、データを格納する。また、その内容をprintfで出力する。という動作を実現したいときに,それらを最も簡潔に記述できる方法が知りたいです。

補足
自分は次のようなアルゴリズムを思いついたのですが、receive_typeと各変数型名を格納した配列との比較を全パターン記述するのはとても時間がかかるなと思い、もっといい表現はないものかと気になりました。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void){
    char seisuu[4] = "int";
    char daburu[6] = "double";
    char buff[1024];
    char receive_type[10];
    char received_char_tmp[1024];

    scanf("%s", buff);
    sscanf(buff, "%s %s", receive_type, received_char_tmp);
    if((strcmp(daburu,receive_type))==0){
        double d_tmp;
        strtod(received_char_tmp, &d_tmp);
        printf("%lf\n", d_tmp);
    }else if((strcmp(seisuu,receive_type))==0){
        int i_tmp;
        i_tmp = atoi(received_char_tmp);
        printf("%d\n", i_tmp);
    }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 6

+2

最も簡潔に

ということなので、いちばん楽な方法を答えるとfgetsで読み込んで空白以後をprintfで出力する。

    scanf("%s", buff);
    sscanf(buff, "%s %s", receive_type, received_char_tmp);

scanf("%s", buff);では空白までしか読み取らないので、これは想定通りに動かないでしょう。


真面目にやると

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


typedef struct {
    char name[16];
    unsigned int size;
    char input_format[8];
} TYPE;

int input(char* str, void** data){
    static TYPE types[] ={
        {"int", sizeof(int), "%d"},
        {"char", 1, "%c"},
        {"float", sizeof(float), "%f"},
        {"double", sizeof(double), "%lf"},
        {"\0",0,"\0"}
    };

    char* t = strtok(str, " ");
    char* v = strtok(NULL, "\n");
    for(int i = 0; types[i].size != 0; i++){
        if(!strcmp(t, types[i].name)){
            *data = malloc(types[i].size);
            sscanf(v, types[i].input_format, *data);
            return i;
        }
    }
    return -1;
}

int main(void)
{
    char buf[1024];
    void* data;
    fgets(buf, sizeof(buf), stdin);
    int t = input(buf, &data);
    switch(t){
        case 0: printf("%d", *(int*)data); break;
        case 1: printf("%c", *(char*)data); break;
        case 2: printf("%f", *(float*)data); break;
        case 3: printf("%f", *(double*)data); break;
    }
    if(t>=0) free(data);
    return EXIT_SUCCESS;
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+2

こんにちは。

C言語のマクロは、貧弱なのですが意外に強力です。
received_char_tmpが表に見えてどんくさいですが、次のような記述も可能です。
else PATTERN(...)を定義していけば、容易に増やせます。

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

#define PATTERN(type, conv, format) \
    if ((strcmp(#type, receive_type)) == 0) \
    { \
        type tmp; \
        tmp = conv; \
        printf(format "\n", tmp); \
    }

int main(void)
{
    char buff[1024];
    char receive_type[10];
    char received_char_tmp[1024];

    fgets(buff, sizeof(buff), stdin);
    sscanf(buff, "%s %s", receive_type, received_char_tmp);

    PATTERN(double, strtod(received_char_tmp, NULL), "%lf")
    else PATTERN(int, atoi(received_char_tmp), "%d")
}


wandbox

もう少し頑張ればreceived_char_tmpを隠したり、convの自由度を上げたりすることも可能だろうと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

checkベストアンサー

+1

izmktr さんの回答から「配列にしてforループ」, takasima20 さんの回答から「型ごとの処理は関数化」を組み合わせ, 以下の様では如何でしょうか.

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

void int_func(char *s) {
  int i = atoi(s);
  printf("i %d\n", i);
}
void double_func(char *s) {
  double d = atof(s);
  printf("d %lf\n", d);
}
void float_func(char *s) {
  float f = atof(s);
  printf("f %f\n", f);
}
void char_func(char *s) {
  char c = s[0];
  printf("c %c\n", c);
}

struct type_table {
  char *type;
  void (*func)(char*);
} table[] = {
  { "int",    int_func    },
  { "double", double_func },
  { "float",  float_func  },
  { "char",   char_func   },
  { NULL, NULL } //EOD
};

#define BUFFSIZE 1024

int main(void){
  char buff[BUFFSIZE], *type, *value;

  fgets(buff, BUFFSIZE, stdin);
  type = strtok(buff, " \n");
  value = strtok(NULL, " \n");

  for(int i=0; table[i].type != NULL; i++) {
    if(strcmp(table[i].type, type) == 0) {
      table[i].func(value);
      break;
    }
  }
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/21 07:45

    おー、きれいなコードですね。

    キャンセル

  • 2019/06/21 10:03

    ついでに、 int i=0; を止めて、table_type *p = table;

    キャンセル

+1

そのアルゴリズムでほぼ正解です
二分探索を使おうにも数が少ないのでべた書きでOKです

そのへんのメタ情報を配列にしてforループで回すのもありですが、
割と自己満足領域なので、べた書きでも問題ないと思います

そういうふうなものをどういう風に実装すればいいのかを知りたいのなら、
Luaという言語のソースがC言語で書かれているので参考になるでしょう
https://www.lua.org/

char daburu[6] = "double";
細かい話だけど、ここは[7]ですね、間違えやすいので[]を使うのがいいです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

全パターン記述するのはとても時間がかかるなと思い、もっといい表現はないものかと気になりました。

それが必要なら、そうするしかないと思います。
見た目を整理したいなら、型ごとの処理は関数化するとか。

あと、型を文字列で入力させるのはめんどうなので
数字をふったメニューを表示して、数字を入力させるのはどうでしょう。
それを switch で判断して各関数にとばすみたいな感じで。
二度手間にはなりますが、当初の入力方式もサポートするなら
すればいいですし。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

読み取りまでは何とか出来ます。

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

int main(void)
{
    char line[1024];
    char type[100];

    char *types[] = {"int","float","double","char"};
    char *ifmts[]  = {"%d", "%f","%lf"," %c"};
    union {
        int xi;
        float xf;
        double xd;
        char xc;
    } work;

    int i;

    while(fgets(line,sizeof line,stdin)){
        if(sscanf(line, "%s", type)!=1) exit(1);
        for(i=0; i<sizeof types/sizeof types[0]; i++){
            if(!strcmp(type,types[i])) break;
        }
        if(i==sizeof types/sizeof types[0]) exit(2);
        if(sscanf(line+strlen(type), ifmts[i], (void*)&work)!=1) exit(3);
        printf("int\t%d\n",work.xi);
        printf("float\t%g\n",work.xf);
        printf("double\t%lg\n",work.xd);
        printf(isgraph(work.xc)?"char\t'%c'\n":"char\t0x%02X\n",work.xc);
    }
}


printfも同じやり方で出来るかと思ったのですが、少なくともgccの場合は、引数の型によって異なるレジスタで引数を渡すので、無理でした。
学生が実習で作ったようなCコンパイラなら可能かも。

普通のコンパイラの場合は、テーブルを引くなら出力するための関数を型それぞれ個別で定義しないと駄目ですね。
ifの連鎖をマクロを使って書く案が出ていますが、そういう方向がいいと思います。

追記

やはり処理系依存ですが、bcc32 (Borland C)だと出来ました。
va_list型が、ポインタになっているタイプだと多分大丈夫。
ただし、float型の出力指定子は無いので、doubleにする必要があります。この特別扱いはどうしようも無いですね。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>

int main(void)
{
    char line[1024];
    char type[100];

    char *types[] = {"int","float","double","char"};
    char *ifmts[]  = {"%d", "%f","%lf"," %c"};
    char *ofmts[]  = {"int=%d\n", "float=%f\n","double=%f\n","char='%c'\n"};
    union {
        int xi;
        float xf;
        double xd;
        char xc;
    } work;

    int i;

    while(fgets(line,sizeof line,stdin)){
        if(sscanf(line, "%s", type)!=1) exit(1);
        for(i=0; i<sizeof types/sizeof types[0]; i++){
            if(!strcmp(type,types[i])) break;
        }
        if(i==sizeof types/sizeof types[0]) exit(2);
        if(sscanf(line+strlen(type), ifmts[i], (void*)&work)!=1) exit(3);
        if(i==1) work.xd = work.xf;
        vprintf(ofmts[i],&work);
    }
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.32%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る