C言語関係掲示板

過去ログ

No843 キー入力から数値、四則演算子、変数名を切り出し表示する。

[戻る] [ホームページ]
No.10799

この課題を教えていただきたいのですが...
投稿者---やじ(2003/11/28 22:58:29)


大学の授業で課題が出されたのですが分からず、困っています。
実力的にはかなりの初心者なので易しい表現で記述してもらえると助かります。

・つくる関数はmainのみ
・キーボードから一行入力し、数値、演算記号(四則演算のみ)、変数名
 を切り出し、それぞれ切り出したトークンを表示する。
 ただしキーワードとして「END」が来れば終了。
 すべて処理したら次の行を入力。

  (例) 1.0 + a - [Return]

NUMBER 1.0
PLUS
IDENT a
MINUS
BN     (*改行)



* a1 -> IDENT
* 1a , 1.0.3 , 「,」 , % , a.1 , -> エラー
* TAB,スペースは無視


みなさん宜しくお願いします。

No.10804

Re:この課題を教えていただきたいのですが...
投稿者---ОВАКАРЭДЭСУ(2003/11/29 01:27:49)


>題名は具体的にお願いします。
>学校の課題はある程度ご自分の解き方をお示しください。



No.10805

トークンの切り出しについて
投稿者---やじ(2003/11/29 02:02:54)


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

void main(void)
{
int i, a=0, j=0, k=0, l=o;
double num;
char str[100], t[100];

printf(`input:`);
scanf(`%s` , &str);

for(i=0; i<100; i++) {
if(isalpha(str[i])){
t[j] = str[i];
j++;
k=1;
}

else if(isdigit(str[i])){
t[j] = str[i];
j++;
}

else if(str[i] == '.'){
t[j] = str[i];
j++;
a++;
}

else if(str[i]=='+' || str[i]=='-' || str[i]=='*' || str==' ' || str[i]=='/'
|| str[i]=='\n') {
t[j] = '\n';


分からないので途中ですが
自分で考えたのはこのような感じです。
宜しくお願いします。


No.10822

Re:トークンの切り出しについて
投稿者---すがりん(2003/11/30 00:42:28)


>分からないので途中ですが
>自分で考えたのはこのような感じです。
>宜しくお願いします。

はじめから全部の機能を実現しようとするのではなく、
より簡単に書けるものを書いて、それが動いたら拡張していくというやり方の方がいいと思います。

例えばこんな感じ
  • 1行入力したものをそのまま表示する
  • 1行入力した文字列を演算子と演算子以外で分けて表示する
  • 「演算子以外」が、数字か変数かそれ以外かを判定する機能を付ける
  • 課題通りに動くものにする

別にもっと細かく分けても、逆にどれかの段階をすっ飛ばしても構いませんが。

No.10836

Re:トークンの切り出しについて
投稿者---やじ(2003/11/30 22:47:28)



>はじめから全部の機能を実現しようとするのではなく、
>より簡単に書けるものを書いて、それが動いたら拡張していくというやり方の方がいいと思います。
>
>例えばこんな感じ
  • 1行入力したものをそのまま表示する
  • 1行入力した文字列を演算子と演算子以外で分けて表示する
  • 「演算子以外」が、数字か変数かそれ以外かを判定する機能を付ける
  • 課題通りに動くものにする

>別にもっと細かく分けても、逆にどれかの段階をすっ飛ばしても構いませんが。


すがりんさん、ありがとうございます。
教えていただいたとおりに考えた方が分かりやすいですね。
以後そのように作っていきたいと思います。
しかし、今回の課題が火曜の朝が締め切りのため
私の実力では間に合いそうにないので
同じようなものを作ったことがある方
もしくは簡単に作れるという方がいましたら
今回だけ教えていただけないでしょうか。
宜しくお願いいたします。

No.10837

Re:トークンの切り出しについて
投稿者---あかま(2003/11/30 23:19:18)


入力されたものはトークンごとに半角スペースで区切られてるの?
区切られてるなら結構簡単そうだけど。

No.10839

Re:トークンの切り出しについて
投稿者---すがりん(2003/12/01 21:32:55)


「かなりの初心者」に出す問題ではないような。。。
まあでも、
>・つくる関数はmainのみ

ということは初心者向きのつもりなのかな。

#何か邪悪な意図があるのかもしれないけど
#実際は関数に分けた方が簡単だし。

というわけで関数に分けると↓になりました。
と言っても最初全部main()に書いてたのをバラしただけなので、
再びmain()に入れ直すのはそんなに難しくないと思います。
gotoだらけになるかもしれませんが(ヒントだよ)、それはこんな問題を出す方が悪いのです。

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

/*
 * スペースやタブを詰める
 */
void delete_space(char *dest, const char *src)
{
    int	i, j;

    for (i = j = 0; i < strlen(src) + 1; i++, j++) {
	while (isspace(src[i])) {
	    i++;
	}
	dest[j] = src[i];
    }
}

/*
 * 変数かどうか調べる
 */
int isidentifier(char *token)
{
    int	i;

    if (!isalpha(token[0]))
	return 0;

    for (i = 1; i < strlen(token); i++) {
	if (!isalnum(token[i]))
	    return 0;
    }
    return 1;
}

/*
 * 数字かどうか調べる
 */
int isnumberstr(char *token)
{
    int	i, pointflg = 0, len = strlen(token);

    if (!isdigit(token[0]) || !isdigit(token[len-1]))
	return 0;

    for (i = 1; i < len - 1; i++) {
	if (!isdigit(token[i])) {
	    if (!pointflg && token[i] == '.') {
		pointflg = 1;
	    } else {
		return 0;
	    }
	}
    }
    return 1;
}

/*
 * 最初の演算子までの長さを求める
 */
int get_token_len(char *str, char *separator)
{
    if (strpbrk(str, separator) != NULL) {
	return strpbrk(str, separator) - str;
    } else {
	return strlen(str);
    }
}

int main(void)
{
    char buf[1024];

    while (fgets(buf, sizeof(buf), stdin) != NULL) {
	char	buf_clone[1024] = "";
	char	*token_head = buf_clone, *flg_end_key;
	int	operator_idx;

	if ((flg_end_key = strstr(buf, "END")) != NULL) {
	    *flg_end_key = '\0';
	}

	delete_space(buf_clone, buf);

	printf("\n");
	for (token_head = buf_clone ; token_head[0] != '\0'; token_head
		 += operator_idx + 1) {
	    char	token[100] = "", operator[] = "+-*/";
	    int		ch;

	    operator_idx = get_token_len(token_head, operator);

	    /* (演算子以外の)トークンを切り出す */
	    strncpy(token, token_head, operator_idx);

	    /* 演算子以外のトークンを処理 */
	    if (strlen(token) > 0) {
		if (isnumberstr(token)) {
		    printf("NUMBER");
		} else if (isidentifier(token)) {
		    printf("IDENT");
		} else {
		    break;
		}
		printf(" %s\n", token);
	    }

	    /* 演算子の名前を表示する */
	    ch = token_head[operator_idx];
	    printf(ch == '+' ? "PLUS\n" :
		   ch == '-' ? "MINUS\n" :
		   ch == '*' ? "ASTERISK\n" :
		   ch == '/' ? "SLASH\n" : "");
	}
	if (token_head[0] != '\0') {
	    fprintf(stderr, "\nerror\n\n");
	    continue;
	}
	printf("%s\n\n", flg_end_key == NULL ? "BN" : "END");
    }

    return 0;
}


>ただしキーワードとして「END」が来れば終了。
>すべて処理したら次の行を入力。
>* TAB,スペースは無視

は、解釈に困りましたが、
1.空白がある場合は、空白がない−ほかの部分は同じ−文字列の場合と全く同じ結果になる
2.ENDが来た時点で処理を終えて(ENDと出力して)入力待ち

としています。

あと、
(例) 1.0 + a - [Return]
はエラーにならないようですので、
エラーになるのは、一つのトークンでエラーが見つかったときだけにしました。だから、
////**
のような入力はエラーになりません。
#strtok()を使っていないのはこういうことがやりにくいから

間違ってたらその辺もなおしてください

No.10840

Re:トークンの切り出しについて
投稿者---かずま(2003/12/01 21:50:57)


> * 1a , 1.0.3 , 「,」 , % , a.1 , -> エラー

次のプログラムは、1a がエラーにならないので、課題の解答ではありませんが、
こんなやりかたもあるという参考にはなるかもしれません。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

int main(void)
{
    char buf[1024], *p, *q;

    while (printf(">>> "), fgets(buf, sizeof buf, stdin))
        for (p = buf; *p != '\0'; p++)
            if (*p == ' ' || *p == '\t') continue;
            else if (*p == '\n') { puts(" BN"); break; }
            else if (*p == '+') puts(" PLUS");
            else if (*p == '-') puts(" MINUS");
            else if (*p == '*') puts(" MULTIPLY");
            else if (*p == '/') puts(" DIVIDE");
            else if (isdigit(*p)) {
                strtod(p, &q);
                printf(" NUMBER %.*s\n", q-p, p);
                p = q-1;
            }
            else if (isalpha(*p)) {
                if (memcmp(p, "END", 3) == 0) return 0;
                for (q = p; isalnum(*q); q++) ;
                printf(" IDENT  %.*s\n", q-p, p);
                p = q-1;
            }
            else { puts(" ERROR"); break; }
    return 0;
}


No.10846

Re:トークンの切り出しについて
投稿者---やじ(2003/12/02 17:15:03)


すがりんさん、かずまさん。
書き込みありがとうございました。
書き込みを見させていただく前に
すがりんさんのアドバイスどおりにやってみて
なんとかつくることができました。
とても参考になります。
勉強のためにおふたりのプログラムを軸に
もう一度作成してみようと思います。
本当にありがとうございました。