如何从C中的字符串中获取和计算表达式
如何从C如何从C中的字符串中获取和计算表达式,c,string,C,String,如何从C char *str = "2*8-5+6"; 评估后的结果应为17 自己试试。您可以使用堆栈数据结构来计算这个字符串,这里是要实现的引用(在c++中) 您必须自己做,C没有提供任何方法。C是一种非常低级的语言。最简单的方法是找到一个执行此操作的库,或者如果不存在,则使用lex+yacc创建您自己的解释器 一个快速的谷歌建议如下: 你应该试试。它是一个可以添加到项目中的单个C源代码文件(没有依赖项) 使用它来解决您的问题只是: #include <stdio.h>
char *str = "2*8-5+6";
评估后的结果应为17 自己试试。您可以使用堆栈数据结构来计算这个字符串,这里是要实现的引用(在c++中)
您必须自己做,C没有提供任何方法。C是一种非常低级的语言。最简单的方法是找到一个执行此操作的库,或者如果不存在,则使用lex+yacc创建您自己的解释器 一个快速的谷歌建议如下:
- 你应该试试。它是一个可以添加到项目中的单个C源代码文件(没有依赖项)
使用它来解决您的问题只是:
#include <stdio.h>
#include "tinyexpr.h"
int main()
{
double result = te_interp("2*8-5+6", 0);
printf("Result: %f\n", result);
return 0;
}
#包括
#包括“tinyexpr.h”
int main()
{
双倍结果=te_interp(“2*8-5+6”,0);
printf(“结果:%f\n”,结果);
返回0;
}
将打印出:
Result:17
C没有标准的eval()
函数
有很多库和其他工具可以做到这一点
但是,如果您想自己学习如何编写表达式计算器,那么它可能会非常简单。这不是一个微不足道的问题:这实际上是一个非常深入的理论问题,因为您基本上是在编写一个微型解析器,可能是建立在一个微型词法分析器上的,就像一个真正的编译器一样
编写解析器的一种简单方法涉及一种称为的技术。编写递归下降解析器与解决大问题或困难问题的另一种伟大技术有很多共同之处,即将大问题、困难问题分解为更小、更容易的子问题
让我们看看我们能想出什么。我们将编写一个函数int eval(const char*expr)
,它接受一个包含表达式的字符串,并返回对其求值的int
结果。但是首先让我们编写一个小的主程序来测试它。我们将读取用户使用fgets
键入的一行文本,将其传递给expr()
函数,并打印结果
#include <stdio.h>
int eval(const char *expr);
int main()
{
char line[100];
while(1) {
printf("Expression? ");
if(fgets(line, sizeof line, stdin) == NULL) break;
printf(" -> %d\n", eval(line));
}
}
现在是时候编写evalexpr()
,它将开始做一些实际的工作。它的工作是对表达式进行第一次顶级解析。它将寻找一系列被加或减的“术语”。所以它想要得到一个或多个子表达式,在它们之间有+
或-
操作符。也就是说,它将处理如下表达式
1 + 2
或
或
或者它可以读一个表达式,比如
1
或者,任何被加或减的项都可能是一个更复杂的子表达式,因此它也能够(间接地)处理类似的事情
2*3 + 4*5 - 9/3
但底线是,它希望得到一个表达式,然后可能是一个+
或-
后跟另一个子表达式,然后可能是一个+
或-
后跟另一个子表达式,依此类推,只要它一直看到+
或-
。这是代码。由于它将表达式的加法“术语”相加,因此它通过调用函数evalterm()
来获取子表达式。它还需要查找+
和-
运算符,并通过调用函数gettok()
来完成此操作。有时它会看到一个操作符而不是+
或-
,但这些不是它要处理的任务,因此如果它看到其中一个操作符,它会“取消设置”它,并返回,因为它已经完成了。所有这些函数都将指针传递给pointerp
,因为正如我前面所说的,所有这些函数在解析字符串时都会跟踪它们在字符串中的移动方式
int evalterm(const char **);
int gettok(const char **, int *);
void ungettok(int, const char **);
int evalexpr(const char **p)
{
int r = evalterm(p);
while(1) {
int op = gettok(p, NULL);
switch(op) {
case '+': r += evalterm(p); break;
case '-': r -= evalterm(p); break;
default: ungettok(op, p); return r;
}
}
}
这是一段相当密集的代码,仔细地看着它,让自己相信它正在做我描述的事情。它调用evalterm()
一次,以获取第一个子表达式,并将结果赋给局部变量r
。然后它进入一个潜在的无限循环,因为它可以处理任意数量的加减项。在循环内部,它获取表达式中的下一个运算符,并使用它来决定要执行的操作。(不要担心gettok
的第二个NULL
参数;我们马上就开始讨论这个问题。)
如果它看到一个+
,它将获得另一个子表达式(另一个术语),并将其添加到运行和中。类似地,如果它看到一个-
,它会得到另一个项,并从运行总和中减去它。如果它得到了其他任何东西,这意味着它完成了,因此它“取消”了它不想处理的操作符,并返回了运行的总和——这实际上是它计算的值。(返回语句也打破了“无限”循环。)
在这一点上,你可能会感觉到“好吧,这开始有意义了”和“等一下,你在这里玩得很快很放松,这永远不会起作用,是吗?”但它会起作用,我们会看到的
我们需要的下一个函数是收集evalexpr()要加(减)的“术语”或子表达式的函数。这个函数是evalterm()
,它最终与evalexpr
非常相似。它的工作是收集一系列由*
和/或/
连接的一个或多个子表达式,并对它们进行乘除。此时,我们将这些子表达式称为“primaries”。代码如下:
int evalpri(const char **);
int evalterm(const char **p)
{
int r = evalpri(p);
while(1) {
int op = gettok(p, NULL);
switch(op) {
case '*': r *= evalpri(p); break;
case '/': r /= evalpri(p); break;
default: ungettok(op, p); return r;
}
}
}
这里实际上没有什么要说的了,因为evalterm
的结构最终与evalexpr完全相同,只是它使用*
和
进行操作,并调用evalpri
来获取/计算其子表达式
现在让我们来看一下evalpri
。它的工作是评估exp的三个最低级别但优先级最高的元素
2*3 + 4*5 - 9/3
int evalterm(const char **);
int gettok(const char **, int *);
void ungettok(int, const char **);
int evalexpr(const char **p)
{
int r = evalterm(p);
while(1) {
int op = gettok(p, NULL);
switch(op) {
case '+': r += evalterm(p); break;
case '-': r -= evalterm(p); break;
default: ungettok(op, p); return r;
}
}
}
int evalpri(const char **);
int evalterm(const char **p)
{
int r = evalpri(p);
while(1) {
int op = gettok(p, NULL);
switch(op) {
case '*': r *= evalpri(p); break;
case '/': r /= evalpri(p); break;
default: ungettok(op, p); return r;
}
}
}
int evalpri(const char **p)
{
int v;
int op = gettok(p, &v);
switch(op) {
case '1': return v;
case '-': return -evalpri(p);
case '(':
v = evalexpr(p);
op = gettok(p, NULL);
if(op != ')') {
fprintf(stderr, "missing ')'\n");
ungettok(op, p);
}
return v;
}
}
#include <stdlib.h>
#include <ctype.h>
void skipwhite(const char **);
int gettok(const char **p, int *vp)
{
skipwhite(p);
char c = **p;
if(isdigit(c)) {
char *p2;
int v = strtoul(*p, &p2, 0);
*p = p2;
if(vp) *vp = v;
return '1';
}
(*p)++;
return c;
}
void skipwhite(const char **p)
{
while(isspace(**p))
(*p)++;
}
void ungettok(int op, const char **p)
{
(*p)--;
}