あなたは、あなたが最後の文字をungetc(3)
する必要があり、おそらく、次の文字が新しいトークンの一部にすることはできませんまで有効な文字を食べる貪欲スキャナを構築する必要があります。
以下は、整数(符号付きまたは符号なし)を解析し、スペース(または符号)を正しく扱うスキャナです(私は思っています:)。自由に使用したり、必要に応じて変更してください。数字の前にプラス記号またはマイナス記号が付いていることに気をつけてください(スペースを挿入することで解き放つことができます)
もっと複雑なもの(浮動小数点数など)を解析する必要がある場合は、スキャナジェネレータlex(1)
/flex(1)
を読んで使用してください。
#include <stdio.h>
#include <ctype.h>
#define MAX_ID 64 /* must be at least two. Routine assumes that */
/* single chars include values 0..255 */
#define TOK_EOF 256
#define TOK_ERR 257
#define TOK_INT 258
union tokenval {
int num; /* in case we return an integer */
/* in case you scan other tokens with specific values,
* e.g. floating point numbers, you can add to this union. */
};
/* lexical scanner with one character of lookahead.
* It recognises:
* integers (regexp: [+-]?[0-9][0-9]*)
* symbols (regexp: .)
* comments (regexp: #.*\n) (these are ignored)
*/
int scan(FILE *in, union tokenval *val)
{
int c;
int result = TOK_ERR;
while ((c = fgetc(in)) != EOF) {
if (isspace(c)) /* skip spaces */
continue;
if (c == '#') { /* comment */
while ((c = fgetc(in)) != EOF && (c != '\n')) continue;
/* c == EOF || c == '\n' */
if (c == EOF) return TOK_EOF;
/* c == '\n' */
continue;
}
if (isalpha(c)) { /* identifier */
char *p = val->id;
size_t l = 1;
*p++ = c; /* add read char */
while (isalnum(c = fgetc(in))) {
if (l < MAX_ID-1) { /* add only if we have space */
*p++ = c; l++;
}
}
*p = '\0'; /* terminate the identifier properly */
/* !isalnum(c) */
ungetc(c, in);
return TOK_ID;
}
/* possible signed number */
switch(c) {
case '+': /* possible signed number */
case '-':
result = c; /* save the read char in result until we know
if we have a trailing number. */
c = fgetc(in);
}
/* result in {TOK_ERR, '+', '-'} */
if (isdigit(c)) { /* integer */
val->num = c - '0';
while (isdigit(c = fgetc(in))) {
val->num *= 10;
val->num += c - '0';
}
/* !isdigit(c) */
ungetc(c, in);
if (result == '-') val->num = -val->num;
return TOK_INT;
}
return result == TOK_ERR ? c : result;
} /* while */
/* c == EOF */
return TOK_EOF;
} /* scan */
int main() {
int tok;
union tokenval val;
#define EOL() puts("")
#define P(tok) printf("[%s-%d]", #tok, (tok))
#define PS(tok) printf("['%c'-%d]\n", (tok), (tok))
do {
tok = scan(stdin, &val);
switch(tok) {
case TOK_ERR: P(TOK_ERR); EOL(); break;
case TOK_INT: P(TOK_INT); printf(":%d\n", val.num); break;
case TOK_EOF: P(TOK_EOF); EOL(); break;
default: PS(tok); break;
} /* switch */
} while (tok != TOK_EOF);
} /* main */
浮動小数点数でe-1
末尾サフィックスを扱っていない、示したスキャナとNOTE
問題(私はそれが含まれていない理由は)、これが可能なスキャナを必要とすることです有効な浮動小数点数(例えば、2.71
---を読み、その後にe-
を読み込んだ場合は、浮動小数点数を拡大するかどうかを決定するために別の文字を読み込む必要があります。 -
符号の後に数字を入力すると、既に読み取った-
とe
文字(その順序で)次のトークンが読み込まれたときにインクルードされます。 lex(1)
/flex(1)
でこの問題を解決するのは非常に簡単であるため、浮動小数点数の読み取りまたは読み取りが可能なスキャナは含まれておらず、コードが複雑になります。
注2
あなたはlex(1)
/flex(1)
を使用する場合は、上記スキャナ用のスキャナ仕様ができ:あなたが唯一のこれまでの1桁の数を入力するかどうかを決定する必要があり
%{
#include <stdio.h>
#include <stdlib.h>
/* single chars include values 0..255 */
#define TOK_EOF 0
#define TOK_ERR 256
#define TOK_INT 257
#define TOK_DBL 258
#define TOK_ID 259
%}
%%
[+-]?[0-9][0-9]* { return TOK_INT; /* integer */ }
[+-]?([0-9]*\.[0-9]+|[0-9]+\.[0-9]*)([eE][+-]?[0-9]+)? { return TOK_DBL; /* double */ }
#.*\n ; /* comment, ignored */
[a-zA-Z][a-zA-Z0-9]* { return TOK_ID; /* ident */ }
[ \t\n] ; /* space, ignored */
. { return yytext[0]; /* the operator char */ }
%%
int main()
{
int tok;
do {
tok = yylex();
switch(tok) {
#define C(ptok) case TOK_##ptok: do{printf("[TOK_"#ptok"-%d]: %s\n", TOK_##ptok, yytext);}while(0)
C(EOF); break;
C(ERR); return 1;
C(INT); break;
C(DBL); break;
C(ID); break;
default: printf("['%c'-%d]\n", tok, tok); break;
}
} while(tok != TOK_EOF);
return 0;
} /* main */
int yywrap() /* so we don't need to link with -ll */
{
return 1;
}
、またはあなたができるかどうか複数桁の番号を入力してください。一桁の数字だけを許可している場合は、数字を数字として読み取ることができます( 'strtod() 'を使用しないでください)。自動的に' 56 * 'から' 5'、 '6'、' * 'を取得します。複数桁の数値を許可すると、 '56 *'はエラーになります。 '*'演算子が使用するのに十分な数のスタックがありません。あなたはどちらかを選ぶでしょう - それは大したことではありません。入力に1桁の数字のみを許可することは、かなり制限的です。ユーザーが空白で数字を区切っていることを主張することをお勧めします。 –
ありがとう@JonathanLeffler。空白の問題を解決するために入力を1桁で保持し、同時に負の整数としてeの表記を受け入れることにした場合、コードをどのように変更すればよいですか? –
現在 'strtod()'と呼ばれているコードをやり直す必要があります。しかし、それは率直に言って1桁の数字で指数表記をすることはできません。サインはより簡単に処理できます。 –