我想创建一个数组,我可以在其中存储运行时提供的输入,例如- + 2 * 3 4 / 12 6
(这里共9个元素)。假设输入实例中没有一个在数组中需要超过50个索引。我正在考虑为此目的的整数数组,但我没有得到任何格式说明符,可以在scanf()
中用于获取输入(在return / Enter keypress处终止),以便我可以在程序中区分后者特定索引是char还是int,它的值是什么。
如果我使用%c
或甚至getchar()
函数,我将面临处理整数多个数字的麻烦。
如果我使用%d
字符,如* + - /未被存储。等等。
如果可行,请提供一些方法。
答案 0 :(得分:1)
当我学习编程(很久以前......)时,我的老师说“在明确你想通过正确的分析实现什么之前,永远不会开始编码”。如果我正确理解,你正在构建一个计算器,只有五个运算符(+ - / *%)遵循以下语法:
expr : number
| operator expr expr
with the following lexical tokens :
operator: single character among +-*/%
number: consecutive sequence of decimal digits ([0-9]*)
not printing characters (space, tab, \r, \n) are used as delimiters or otherwise ignored
any other character causes an error.
好的:这是您当前的规格,如果您以后想要使用十进制数字,您只需更改数字定义以允许一个可选的小数点。
这样写,使用lex和yacc会很容易,但对于这么简单的语法来说,它肯定是矫枉过正的。
即使我们将空格定义为分隔符,也不可能使用scanf
来获取令牌,因为它会默默地吃+
个符号:+12与12相同。
因此,您必须使用返回标记的getc
构建一个简单的词法分析器,然后使用递归计算表达式的解析器。无需在数组中存储任何内容:
typedef struct _token {
enum {OPERATOR, INT, END, ERROR } type;
union {
int ival;
char op;
} value;
} TOKEN;
TOKEN getToken(FILE *fdin) {
static const char valid_op[] = "+-*/%";
static const char spaces[] = " \t\r\n";
int c;
TOKEN tok;
int val = 0;
int isval = 0;
while ((c = getc(fdin)) != EOF) {
if ((c >= '0') && (c <= '9')) {
val = 10 * val + (c - '0');
isval = 1;
}
else if (isval != 0) {
tok.type = INT;
tok.value.ival = val;
ungetc(c, fdin);
return tok;
}
else if (strchr(valid_op, c)) {
tok.type = OPERATOR;
tok.value.op = c;
return tok;
}
else if (! strchr(spaces, c)) {
tok.type = ERROR;
return tok;
}
}
tok.type = END;
return tok;
}
int parse(FILE *fdin, int *typ) {
int i, j;
*typ = INT;
for(;;) {
TOKEN tok = getToken(fdin);
if (tok.type == INT) {
return tok.value.ival;
}
else if (tok.type == OPERATOR) {
i = parse(fdin, typ);
if (*typ != INT) {
*typ = ERROR;
return 0;
}
j = parse(fdin, typ);
if (*typ != INT) {
*typ = ERROR;
return 0;
}
switch(tok.value.op) {
case '+': return i+j;
case '-': return i-j;
case '*': return i*j;
case '/': return i/j;
case '%': return i * j / 100;
}
}
else {
*typ = tok.type;
return 0;
}
}
}
答案 1 :(得分:1)
您需要一种可以容纳各种类型数据的数据类型:运算符和整数,甚至可能是浮点数或名称(变量或函数)。
C中的一种常见方法是使用union
,它可以在同一空间中保存多种类型。您一次只能使用其中一种类型,因此您需要一种方法来指示哪些类型处于活动状态,这可以使用enum
来完成。然后将enum
和union
包裹在struct
中,让它们彼此并排整齐。
下面是auch数据类型的示例实现。它不进行任何操作,它只解析一个字符串并打印标记。
在您的示例中,所有标记必须用空格分隔,以便strtok
可以找到它们。如果你想将5/2
识别为三个标记,你可以像Serge Ballesta在他非常系统的答案中建议的那样建立一个词法分析器。下面的实现不会识别负数,例如-1
。错误处理也很基本。
此代码可能仍然是您作为解决方案的起点:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
enum Type { /* enumeration of possible types */
Operator,
Integer,
Float,
Name,
Illegal
};
struct Token {
enum Type type; /* token type */
union { /* mutually exclusive data fields */
long long int l; /* ... for Integer */
double x; /* ... for Float */
char name[20]; /* ... for Name and Operator */
} data;
};
struct Token illegal(const char *str)
{
struct Token tk = {Illegal};
snprintf(tk.data.name, 20, "%s", str);
return tk;
}
struct Token parse(const char *str)
{
struct Token tk = {Illegal};
if (strchr("+-*/%", *str)) {
if (str[1]) return illegal("Overlong operator");
tk.type = Operator;
strcpy(tk.data.name, str);
return tk;
}
if (isdigit(*str)) {
double x;
long long l;
char *end;
l = strtoll(str, &end, 0);
if (end != str && *end == '\0') {
tk.type = Integer;
tk.data.l = l;
return tk;
}
x = strtod(str, &end);
if (end != str && *end == '\0') {
tk.type = Float;
tk.data.x = x;
return tk;
}
return illegal("Illegal number");
}
if (isalpha(*str)) {
const char *p = str;
while (*p) {
if (!isalnum(*p++)) return illegal("Illegal name");
}
tk.type = Name;
snprintf(tk.data.name, 20, "%s", str);
return tk;
}
return illegal("Illegal character");
}
int split(struct Token tk[], int max, char *str)
{
int n = 0;
char *p;
p = strtok(str, " \t\n");
while (p) {
struct Token curr = parse(p);
if (curr.type == Illegal) {
fprintf(stderr, "Parse error: %s.\n", curr.data.name);
return -1;
}
if (n < max) tk[n] = curr;
n++;
p = strtok(NULL, " \t\n");
}
return n;
}
void print(struct Token tk)
{
switch (tk.type) {
case Operator: printf("operator %c\n", tk.data.name[0]);
break;
case Integer: printf("integer %lld\n", tk.data.l);
break;
case Float: printf("float %g\n", tk.data.x);
break;
case Name: printf("name \"%s\"\n", tk.data.name);
break;
default: printf("illegal token\n");
}
}
int main()
{
char line[] = "- + 2 * alpha beta / 12.0 6";
struct Token tk[20];
int i, n;
n = split(tk, 20, line);
for (i = 0; i < n; i++) {
print(tk[i]);
}
return 0;
}
答案 2 :(得分:0)
使用
fgets()
阅读直至行尾。strtok()
将该行拆分为令牌,并将空格作为分隔符。strtol()
将令牌转换为整数,这有助于您找到0
和character
之间的差异