diff --git a/CMakeLists.txt b/CMakeLists.txt index cc4fc46..d489252 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,5 +13,5 @@ add_library( src/core/manager/CalculationManagement.cpp include/CalculationManagement.h src/core/calculation/PrefixExpressionOperation.cpp src/exceptional/MExceptional.cpp include/MExceptional.h - src/utils/NumberUtils.cpp src/utils/StrUtils.cpp src/dataContainer/MEStack.cpp include/MEStack.h include/PrefixExpressionOperation.h src/core/calculation/NumberCalculation.cpp include/NumberCalculation.h src/core/calculation/Calculation.cpp include/Calculation.h include/mathematical_expression.h src/core/calculation/BracketsCalculation.cpp include/BracketsCalculation.h) + src/utils/NumberUtils.cpp src/utils/StrUtils.cpp src/dataContainer/MEStack.cpp include/MEStack.h include/PrefixExpressionOperation.h src/core/calculation/NumberCalculation.cpp include/NumberCalculation.h src/core/calculation/Calculation.cpp include/Calculation.h include/mathematical_expression.h src/core/calculation/BracketsCalculation.cpp include/BracketsCalculation.h src/core/calculation/BracketsCalculationTwo.cpp include/BracketsCalculationTwo.h src/core/mathematical_expression.cpp) diff --git a/README-Chinese.md b/README-Chinese.md index b1f301f..e105ce2 100644 --- a/README-Chinese.md +++ b/README-Chinese.md @@ -5,3 +5,186 @@ ## 介绍 本框架是一种针对数学公式解析的有效工具,能够通过C++的API解析包含嵌套函数,包含函数,数列步长累加等数学公式,返回值是一个数值的结果对象,同时也可以进行比较运算的操作,再进行比较的时候,返回值是一个布尔值结果对象。 +如果您是一位 Java 或 python爱好者,可以专门前往 [JavaAPI](https://github.com/BeardedManZhao/mathematical-expression.git) +或 [PythonAPI](https://github.com/BeardedManZhao/mathematical-expression-Py) 中进行相关资料的查阅。 + +### 如何使用库? + +在项目中有一个 include 文件目录,其中存储的就是库需要的所有头文件,您可以将其中的库文件导入到您的项目中,然后再集成本框架编译好的dll,下面是cmake的配置文件实例。 + +```cmake +cmake_minimum_required(VERSION 3.23) +project(MyCpp) + +set(CMAKE_CXX_STANDARD 14) + +# 设置头文件目录(可以自定义) +include_directories(${PROJECT_SOURCE_DIR}/head) +add_executable(MyCpp main.cpp) +# 与项目进行链接(将库链接到编译之后的目标中) +target_link_libraries(${PROJECT_NAME} D:\\liming\\Project\\Clion\\MyCpp\\cmake-build-debug\\mathematical_expression_cpp.dll) +``` + +集成操作完毕之后,您可以尝试输入以下代码来判断库的功能是否正常,下面是该库的一个测试代码,如果其运行之后的程序main函数返回值为0 +代表程序正常退出,意味着库装载完毕。 + +```c++ +#include "mathematical_expression.h" + +int main(){ + system("chcp 65001"); + // 打印 mathematical_expression 的版本信息 + cout << mathematical_expression::getVERSION() << endl; +} +``` + +### 通过 mathematical-expression 库直接获取到计算组件并进行计算 + +```c++ +#include "mathematical_expression.h" + +int main(){ + system("chcp 65001"); + // 构建需要计算的两种表达式 + string s1 = "1 + 20 - 2 + 4", s2 = "1 + 20 - (2 + 4)"; + // 获取到 me 门户类 + mathematical_expression me; + // 获取到 无括号表达式计算组件 + ME::PrefixExpressionOperation prefixExpressionOperation = me.getPrefixExpressionOperation(); + // 获取到 有括号表达式计算组件 + ME::BracketsCalculationTwo bracketsCalculationTwo = me.getBracketsCalculation2(); + // 开始检查表达式 + prefixExpressionOperation.check(s1); + bracketsCalculationTwo.check(s2); + // 开始计算两个表达式 可以使用左移运算符将表达式送给计算组件 获取到结果对象 + ME::CalculationNumberResults r1 = prefixExpressionOperation << s1; + ME::CalculationNumberResults r2 = bracketsCalculationTwo << s2; + // 开始进行结果查看 + cout << "计算层数:" << r1.getResultLayers() << "\t计算结果:" << r1 << "\t计算来源:" << r1.getCalculationSourceName() << endl; + cout << "计算层数:" << r1.getResultLayers() << "\t计算结果:" << r2 << "\t计算来源:" << r1.getCalculationSourceName() << endl; +} +``` + +- 运行结果 + +``` +Active code page: 65001 +计算层数:1 计算结果:23 计算来源:PrefixExpressionOperation +计算层数:1 计算结果:15 计算来源:PrefixExpressionOperation + +进程已结束,退出代码0 +``` + +## 框架架构 + +### 门户类 + +我们可以通过指定的门户类对象获取到相关的各个计算组件,这里与 JavaAPI 和 PythonAPI 的实现有些不同,这里是使用的get函数获取到指定计算组件对象,而非使用 +Hash 缓冲池。 + +```c++ +#include "mathematical_expression.h" + +int main(){ + system("chcp 65001"); + // 获取到 me 门户类 + mathematical_expression me; + // 获取到 无括号表达式计算组件 + ME::PrefixExpressionOperation prefixExpressionOperation = me.getPrefixExpressionOperation(); + // 获取到 有括号表达式计算组件 + ME::BracketsCalculationTwo bracketsCalculationTwo = me.getBracketsCalculation2(); +} +``` + +## 计算组件 + +### 无括号表达式 + +- 类组件:core.calculation.number.PrefixExpressionOperation +- 介绍 + + 针对一个没有括号,但是有加减乘除以及取余等运算操作的数学表达式而设计的组件,该组件可以实现带有优先级计算的功能,其中通过前缀表达式解析计算,将操作数与操作符一同存储到栈,在存储的同时配有计算优先级比较,如果当下的优先级较小,就先将上一个操作数与操作符与当前操作数进行运算,形成一个新的数值,然后再入栈。 +- API使用示例 + + 该组件支持的运算符有: a+b a-b a*b a/b a%b + +```c++ +#include "mathematical_expression.h" + +int main(){ + system("chcp 65001"); + // 获取到 me 门户类 + mathematical_expression me; + // 获取到 无括号表达式计算组件 + ME::PrefixExpressionOperation prefixExpressionOperation = me.getPrefixExpressionOperation(); + // 构建一个表达式 + string s = "1 + 2 + 4 * 10 - 3"; + // 开始检查表达式 + prefixExpressionOperation.check(s); + // 开始计算两个表达式 可以使用左移运算符将表达式送给计算组件 获取到结果对象 + ME::CalculationNumberResults r1 = prefixExpressionOperation << s; + // 开始进行结果查看 + cout << "计算层数:" << r1.getResultLayers() << "\t计算结果:" << r1 << "\t计算来源:" << r1.getCalculationSourceName() << endl; +} +``` + +- 运行结果 + + 在API调用中,对函数的运行结果进行了打印,可以看到,组件计算的返回值是一个结果集对象,在该对象中存储的就是很多有关计算结果相关的信息。 + +``` +Active code page: 65001 +计算层数:1 计算结果:40 计算来源:PrefixExpressionOperation + +进程已结束,退出代码0 +``` + +### 嵌套括号表达式 + +- 类组件:core.calculation.number.BracketsCalculation2 +- 介绍: + + 嵌套括号表达式解析组件,能够针对带有多个括号的数学表达式进行解析与结果计算,针对嵌套括号进行优先级的解析与计算,该组件依赖于“core.calculation.number.PrefixExpressionOperation”,在该组件中采用递归进行括号的解析,然后将最内层面的表达式提供给“core.calculation.number.PrefixExpressionOperation”进行计算。 +- API使用示例 + + 该组件支持的运算符有: a+b a-b a*b a/b a%b ( ) + +```c++ +#include "mathematical_expression.h" + +int main(){ + system("chcp 65001"); + // 获取到 me 门户类 + mathematical_expression me; + // 获取到 无括号表达式计算组件 + ME::BracketsCalculationTwo bracketsCalculationTwo = me.getBracketsCalculation2(); + // 构建一个表达式 + string s = "1 + 2 + 4 * (10 - 3)"; + // 开始检查表达式 + bracketsCalculationTwo.check(s); + // 开始计算两个表达式 可以使用左移运算符将表达式送给计算组件 获取到结果对象 + ME::CalculationNumberResults r1 = bracketsCalculationTwo << s; + // 开始进行结果查看 + cout << "计算层数:" << r1.getResultLayers() << "\t计算结果:" << r1 << "\t计算来源:" << r1.getCalculationSourceName() << endl; +} +``` + +- 运行结果 + + 在API调用中,对表达式的计算结果进行了打印,可以看到,组件计算的返回值是一个数值结果对象,在该对象中存储的就是很多有关计算结果相关的信息。 + +``` +Active code page: 65001 +计算层数:2 计算结果:31 计算来源:BracketsCalculation + +进程已结束,退出代码0 +``` + +
+ * Compute a mathematical expression and store the calculation details and results in the numerical result set. * - * @param BinaryFormula 公式 例如 1 + 2 - * @return 计算结果数值 + * @param Formula 被计算的表达式,要求返回值是一个数值。 + *
+ * The returned value of the evaluated expression is required to be a numeric value. + * @param formatRequired 是否需要被格式化,用于确保公式格式正确。 + *
+ * Whether it needs to be formatted to ensure that the formula format is correct. + * @return 数值结果集对象,其中保存着每一步的操作数据,以及最终结果数值 + *
+ * Numerical result set object, which stores the operation data of each step and the final result value */ - static double calculation2(std::string Formula); + CalculationNumberResults calculation(std::string Formula, bool formatRequired) = 0; }; } diff --git a/include/BracketsCalculationTwo.h b/include/BracketsCalculationTwo.h new file mode 100644 index 0000000..2e265cb --- /dev/null +++ b/include/BracketsCalculationTwo.h @@ -0,0 +1,30 @@ +// +// Created by Liming on 2023/6/20. +// + +#ifndef MATHEMATICAL_EXPRESSION_CPP_BRACKETSCALCULATIONTWO_H +#define MATHEMATICAL_EXPRESSION_CPP_BRACKETSCALCULATIONTWO_H + +#include "regex" +#include "BracketsCalculation.h" + +namespace ME { + class BracketsCalculationTwo : public BracketsCalculation { + + public: + string formatStr(std::string string) override; + + void check(std::string string) override; + + ME::CalculationNumberResults operator>>(std::string &format); + + ME::CalculationNumberResults operator<<(std::string &format); + + string getName() override; + + CalculationNumberResults calculation(std::string Formula, bool formatRequired) override; + }; +} + + +#endif //MATHEMATICAL_EXPRESSION_CPP_BRACKETSCALCULATIONTWO_H diff --git a/include/CalculationResults.h b/include/CalculationResults.h index 51f4b29..ed8ce80 100644 --- a/include/CalculationResults.h +++ b/include/CalculationResults.h @@ -202,10 +202,6 @@ namespace ME { size_t operator()(const CalculationBooleanResults &person) const; }; - ostream &operator<<(ostream &out, const CalculationBooleanResults *rhs); - - ostream &operator<<(ostream &out, const CalculationBooleanResults &rhs); - CalculationNumberResults operator+(CalculationNumberResults v1, CalculationNumberResults v2); CalculationNumberResults operator-(CalculationNumberResults v1, CalculationNumberResults v2); @@ -217,10 +213,14 @@ namespace ME { CalculationNumberResults operator<<(CalculationNumberResults v1, CalculationNumberResults v2); CalculationNumberResults operator>>(CalculationNumberResults v1, CalculationNumberResults v2); +} - ostream &operator<<(ostream &out, const CalculationNumberResults *rhs); +ostream &operator<<(ostream &out, const ME::CalculationBooleanResults *rhs); - ostream &operator<<(ostream &out, const CalculationNumberResults &rhs); -} +ostream &operator<<(ostream &out, const ME::CalculationBooleanResults &rhs); + +ostream &operator<<(ostream &out, const ME::CalculationNumberResults *rhs); + +ostream &operator<<(ostream &out, const ME::CalculationNumberResults &rhs); #endif //MATHEMATICAL_EXPRESSION_CPP_CALCULATIONRESULTS_H diff --git a/include/NumberCalculation.h b/include/NumberCalculation.h index 3cedef3..a4110cd 100644 --- a/include/NumberCalculation.h +++ b/include/NumberCalculation.h @@ -26,6 +26,8 @@ class NumberCalculation : public Calculation { */ virtual ME::CalculationNumberResults calculation(std::string Formula, bool formatRequired) = 0; +private: + virtual /** * 计算一个数学表达式,并将计算细节与计算结果存储到数值结果集中。 *
diff --git a/include/mathematical_expression.h b/include/mathematical_expression.h
index 4c2300f..eb2c954 100644
--- a/include/mathematical_expression.h
+++ b/include/mathematical_expression.h
@@ -6,11 +6,47 @@
#define MATHEMATICAL_EXPRESSION_CPP_MATHEMATICAL_EXPRESSION_H
#include "PrefixExpressionOperation.h"
+#include "BracketsCalculationTwo.h"
-namespace ME {
- PrefixExpressionOperation prefixExpressionOperation;
+#define VERSION "1.0.0-mathematical_expression-C++";
-}
+/**
+ * 数学表达式解析计算库中的门户类,由该类获取到数学表达式计算库中的相关数据对象。
+ *
+ * The portal class in the mathematical expression analysis and calculation library, which obtains the relevant data objects in the mathematical expression calculation library.
+ */
+class mathematical_expression {
+protected:
+ ME::PrefixExpressionOperation prefixExpressionOperation;
+ ME::BracketsCalculationTwo bracketsCalculation2;
+public:
+
+ /**
+ *
+ * @return 此库的版本信息。
+ *
+ * The version information of this library.
+ */
+ static string getVERSION() {
+ return VERSION
+ }
+
+ /**
+ *
+ * @return 无括号表达式计算组件对象。
+ *
+ * An expression without parentheses evaluates a component object.
+ */
+ ME::PrefixExpressionOperation getPrefixExpressionOperation();
+
+ /**
+ *
+ * @return 数学表达式计算组件,该组件能够解析并计算一个带有括号的表达式。
+ *
+ * Mathematical expression calculation component, which can parse and evaluate an expression with parentheses.
+ */
+ ME::BracketsCalculationTwo getBracketsCalculation2();
+};
#endif //MATHEMATICAL_EXPRESSION_CPP_MATHEMATICAL_EXPRESSION_H
diff --git a/src/core/calculation/BracketsCalculation.cpp b/src/core/calculation/BracketsCalculation.cpp
index 60dedb0..9661327 100644
--- a/src/core/calculation/BracketsCalculation.cpp
+++ b/src/core/calculation/BracketsCalculation.cpp
@@ -13,31 +13,70 @@ namespace ME {
return std::regex_replace(string, ME::ALL_INVISIBLE_CHARACTERS_PATTERN, NO_CHAR);
}
- double BracketsCalculation::calculation2(std::string Formula) {
- std::string a;
- std::string b;
- bool isOk = false;
- char Operator = 0;
- for (const auto &c: Formula) {
- if (!isOk) {
- if (StrUtils::IsAnOperator(c)) {
- Operator = c;
- isOk = true;
+ string BracketsCalculation::getName() {
+ return "BracketsCalculation";
+ }
+
+ void BracketsCalculation::check(std::string string) {
+ if (string.empty()) {
+ throw ME::WrongFormat("您传入的表达式为null 无法进行计算。");
+ }
+ unsigned int lastIndex = string.length() - 1;
+ // 左括号出现数量
+ int LeftCount = 0;
+ // 右括号出现数量
+ int RightCount = 0;
+ {
+ char lastChar = string[lastIndex];
+ while (lastChar == EMPTY) {
+ lastChar = string[--lastIndex];
+ }
+ if (!StrUtils::IsANumber(lastChar)) {
+ if (lastChar != RIGHT_BRACKET) {
+ throw ME::WrongFormat(
+ &"您传入的表达式格式有误,最后一个字符不是一个数值!!!\nThe format of the expression you passed in is incorrect. The last character is not a numeric value!!!\nERROR => "[lastChar]
+ );
+ }
+ }
+ }
+ // 上一个字符是否是运算符
+ bool is = false;
+ int index = 0;
+ for (const auto &c: string) {
+ if (c == LEFT_BRACKET) {
+ ++LeftCount;
+ } else if (c == RIGHT_BRACKET) {
+ ++RightCount;
+ } else if (!(StrUtils::IsANumber(c) || c == DECIMAL_POINT || c == EMPTY)) {
+ if (!StrUtils::IsAnOperator(c)) {
+ std::string data = "解析表达式的时候出现了未知符号!!!\nUnknown symbol appears when parsing expression!!!\nWrong format => [";
+ data += c;
+ throw ME::WrongFormat(data.append("] from " + string));
} else {
- b += c;
+ if (is && c != MINUS_SIGN && c != PLUS_SIGN)
+ throw ME::WrongFormat(
+ "您的数学表达式不正确,缺失了一个运算数值或多出了一个运算符。ERROR => " + string);
+ is = true;
+ continue;
}
- } else {
- a += c;
}
+ if (++index > lastIndex) {
+ break;
+ }
+ is = false;
}
- if (Operator != 0) {
- return NumberUtils::calculation(
- Operator, StrUtils::stringToDouble(a), StrUtils::stringToDouble(b)
- );
- } else {
- throw ME::AbnormalOperation("您的公式格式错误,未解析成功,请您检查您的格式哦!\n" +
- "The format of your formula is wrong, and it was not parsed successfully. Please check your format!\n" +
- "error format => " + Formula);
+ if (LeftCount != RightCount) {
+ int absV = abs(LeftCount - RightCount);
+ std::string data =
+ "您的格式不正确,出现了数学表达式中不正确的括号对数,请您检查是否缺少或者多出了[" +
+ to_string(absV);
+ data
+ .append(data + "]个括号。\n")
+ .append("Your format is incorrect. There are incorrect parenthesis logarithms in the mathematical expression. Please check whether [")
+ .append(to_string(absV))
+ .append("] parentheses are missing or extra.\n")
+ .append("Wrong from [" + string + "]");
+ throw ME::WrongFormat(data);
}
}
}
diff --git a/src/core/calculation/BracketsCalculationTwo.cpp b/src/core/calculation/BracketsCalculationTwo.cpp
new file mode 100644
index 0000000..34a9cf7
--- /dev/null
+++ b/src/core/calculation/BracketsCalculationTwo.cpp
@@ -0,0 +1,70 @@
+//
+// Created by Liming on 2023/6/20.
+//
+
+#include "BracketsCalculationTwo.h"
+#include "ConstantRegion.h"
+
+static ME::PrefixExpressionOperation operation;
+
+ME::CalculationNumberResults ME::BracketsCalculationTwo::calculation(std::string Formula, bool formatRequired) {
+ unsigned int length = Formula.length();
+ // 公式存储区
+ std::string stringBuilder;
+ // 括号内数据的起始索引
+ int start = 0;
+ bool setok = false;
+ int layer = 0;
+ // 括号内的括号均衡数量,为了确定是一对括号
+ int count = 0;
+ for (int i = 0; i < length; i++) {
+ char aChar = Formula[i];
+ if (aChar == LEFT_BRACKET) {
+ // 如果当前字符是一个左括号,那么说明括号开始了,这个时候需要将起始点记录
+ if (!setok) {
+ setok = true;
+ start = i + 1;
+ }
+ ++count;
+ } else if (aChar == RIGHT_BRACKET && --count == 0) {
+ setok = false;
+ // 如果当前字符是一个右括号,那么就将括号中的字符进行递归计算,计算之后将该参数作为公式的一部分
+ CalculationNumberResults calculation = operation.calculation(
+ Formula.substr(start, i - start + 1), formatRequired
+ );
+ stringBuilder.append(to_string(calculation.getResult()));
+ ++layer;
+ } else if (!setok && aChar != EMPTY) {
+ // 如果不是一个括号就将字符提供给字符串缓冲区
+ stringBuilder += aChar;
+ }
+ }
+ // 将此字符串的结果计算出来
+ CalculationNumberResults calculationNumberResults = operation.calculation(stringBuilder,
+ formatRequired);
+ return {
+ calculationNumberResults.getResultLayers() + layer,
+ calculationNumberResults.getResult(),
+ this->getName()
+ };
+}
+
+string ME::BracketsCalculationTwo::formatStr(std::string string) {
+ return BracketsCalculation::formatStr(string);
+}
+
+void ME::BracketsCalculationTwo::check(std::string string) {
+ BracketsCalculation::check(string);
+}
+
+string ME::BracketsCalculationTwo::getName() {
+ return BracketsCalculation::getName();
+}
+
+ME::CalculationNumberResults ME::BracketsCalculationTwo::operator>>(string &format) {
+ return this->calculation(format, true);
+}
+
+ME::CalculationNumberResults ME::BracketsCalculationTwo::operator<<(string &format) {
+ return this->calculation(format, true);
+}
diff --git a/src/core/calculation/NumberCalculation.cpp b/src/core/calculation/NumberCalculation.cpp
index 9dc9e6d..d4ab3ce 100644
--- a/src/core/calculation/NumberCalculation.cpp
+++ b/src/core/calculation/NumberCalculation.cpp
@@ -2,3 +2,5 @@
// Created by Liming on 2023/6/19.
//
+#include