The 3rd part of the Calculator program _ FILE I/O

题目链接:第五次作业(计算器第三步)
github链接:Calculator_1.5.0 第五次作业

第三部分,开篇最想说的是,不知不觉进化到version1.5.0了(hhh~)

言归正传,这篇随笔有需要说明的一点是,由于在修改第四次作业的中途发布了第五次作业,所以这篇也会是上一次博客随笔面向对象程序设计_Task4_Calculator1.1的延续(评论中的下回分解hhh~)

So,在下文的代码前后,会结合第四、第五次作业的感受一起瞎掰。

以上。

Part 1

第五次作业代码方面的要求较之前相比,多了一个对文件读写的支持,且表达式自带等号。第二个问题很好解决,至于第一个,在对文件的读写有大致的了解之后也不是难题。

另外,用图形的方式描述整个项目的框架,即各个类之间的调用关系。

一脸懵逼.jpg

这是要用UML(Unified Modeling Language, 统一建模语言)的意思吗(貌似之前学JAVA过程中听过这个东西,类图用例图对象图各种图orz...)?

管他是不是,反正用了准没错= =,之后懵逼的去百度类图,还是没搞怎么明白,不过也大致画出了一点框架

下面,附上心得体会和代码实现...

Part 2

那么,依次对不同类进行分析,首先是:

  • Scan类

    在Scan类中,唯一的改变是ToStringQueue函数,针对第五次作业在表达式后自带等号的情况,所取得的表达式长度应为实际长度-1

    Scan.cpp

      queue<string> Scan::ToStringQueue(string input)
    {
    queue<string> q; // q即为最后所要return的队列,用于保存input表达式中的数字和符号
    string res = ""; // res为扫描input表达式时用于临时存储的字符串
    string strArr[N]; // strArr为扫描input表达式时用于存储数字和符号的string数组
    int len = input.length() - 1; // 省略最后的等号'='
    int cnt = 0; // 作为strArr的下标记录已完成提取的数字和符号的个数 for (int i = 0; i < len; i++)
    {
    if ((input[i] >= '0' && input[i] <= '9') || input[i] == '.')
    {
    res += input[i]; // 若是数字或 '.' 则添加到临时存储器res里,
    if (i == len - 1) // 若已完成扫描
    {
    strArr[cnt++] = res;
    break;
    }
    continue; // 继续判断下一元素
    }
    else
    {
    if (i == 0 && res.empty()) // 若第一个元素为‘-’
    {
    res += input[i];
    continue;
    }
    if (!res.empty()) // res非空表示此时res中缓存着一个数字 或 第一个元素为 '-'
    {
    strArr[cnt++] = res;
    }
    res = ""; // res置空
    strArr[cnt++] = res + input[i]; // 将不是数字或 "." 的元素(即符号元素)添加到strArr中
    }
    } for (int i =0; i < cnt; i++) // 将strArr中各个元素添加到q队列中
    {
    if (strArr[i].length() > 10)
    {
    setIsExceed10(true); // 存在大于10位数的数字
    }
    q.push(strArr[i]);
    } return q;
    }


接下来则是:

  • Calculation类

    这个类是有颇多的改动,其一是改变了栈中提取出来的操作数的类型,由int转为double,防止小数部分的截断以至于WA;

    其二则是对类成员的封装保护,将几个成员变量定义为private,并为其中一些变量提供了getter和setter方法;

    其三是在重复利用stringstream对象时,除了.clear(),还进行了.str("")操作;

    其四是将多个if-else语句简化为switch-case语句,优化、美观;

    最后,则是digitStack的改动,由stack换成了stack,处理上,简单的还真不是一星半点...orz...减少了stringstream流的使用频率,也缩减了代码量。

    calculation.h

      const int SIZE = 250;     // 表达式的最大长度(即q.size() <= SIZE)
    
      class Calculation
    {
    public:
    Calculation(void);
    ~Calculation(void);
    void setPriorityLevel();
    stack<double>& getDigitStack();
    bool isOperator(string s);
    bool isDigit(string s);
    void toPostfixExpression(queue<string> q);
    void calculatingExpression(queue<string> q, bool is_Exceed10);
    private:
    int priority[128]; // 存储运算符的优先级
    stack<string> cacheStack; // 缓存栈
    stack<string> operatorStack; // 操作符栈,包括"+", "-", "*", "/", "(", ")"
    stack<double> digitStack; // 数字栈,包括处理与运算过程
    };


*Calculation.cpp*

	void Calculation::calculatingExpression(queue<string> q, bool is_Exceed10)
{
if (is_Exceed10) // 若超过10位数的数字存在为真
{
cout << "Input error ! Not exceeding 10 digits expected! " << endl;
return;
} toPostfixExpression(q); // 将中缀表达式转换为后缀表达式 /**********计算部分***********/
string postfixExp[SIZE]; // 由于计算时需对缓存栈从栈底到栈顶的逐一扫描,故用string数组进行遍历操作
int expLen = 0;
while (!cacheStack.empty())
{
postfixExp[expLen++] = cacheStack.top();
cacheStack.pop();
} stringstream str_stream; // 用stringstream流进行string和double的格式转换
double res = 0; // res用于临时存储,push进数字栈
double rightNum = 0; // 右操作数
double leftNum = 0; // 左操作数
for (int i = expLen - 1; i >= 0; i--)
{
if (isDigit(postfixExp[i])) // 若该元素为数字,则直接入数字栈digitStack
{
str_stream << postfixExp[i]; // 格式转换:string->double
str_stream >> res;
digitStack.push(res);
str_stream.clear(); // stringstream流的清空,以便重复利用
str_stream.str("");
}
else if (isOperator(postfixExp[i])) // 若该元素为运算符,则弹出数字栈的两个数进行相应的运算并将结果push进数字栈
{
if (!digitStack.empty())
{
rightNum = digitStack.top();
digitStack.pop();
}
if (!digitStack.empty())
{
leftNum = digitStack.top();
digitStack.pop();
} switch (postfixExp[i][0]) // 对应加法、减法、乘法、除法运算
{
case '+':
digitStack.push(leftNum + rightNum);
break;
case '-':
digitStack.push(leftNum - rightNum);
break;
case '*':
digitStack.push(leftNum * rightNum);
break;
case '/':
digitStack.push(leftNum / rightNum);
break;
default:
break;
}
}
}
}


最后的就是第五次作业的主体:

  • Print类

    作业要求:所有关于输出的代码,都写在Print类里面

    相较之前,这次几乎将所有的输出都放在了Print类,而且这次作业的一个很大的改进是,终于想到在类的构造函数和析构函数里面写点东西了→_→

    而且,在一段纠结是否要重载PrintResult函数的时间之后,还是选用了不同的输出函数处理,更便于理解(理解万岁~)

    这里还有要说到的就是文件的读写,我用的是fstream文件流来操作,(文件流参考博客),用file_read和file_write关联两个文件,循环从输入文件开头读到文件尾,一次读取一个表达式并将表达式的值输出到输出文件内。

    print.h

      class Print
    {
    public:
    Print(void);
    ~Print(void);
    void printError();
    void printResult(string input);
    void printResult_a(string input);
    void printResult_f(string inputAddr, string outputAddr);
    void printQueue(queue<string> q, bool isExceed10);
    private:
    Scan *sc;
    Calculation *ca;
    };


*print.cpp*

	#include "Print.h"
#include <iostream>
#include <fstream> Print::Print(void) // 在构造函数中初始化 *sc 和 *ca
{
sc = new Scan();
ca = new Calculation();
} Print::~Print(void) // 在析构函数中释放
{
delete sc;
sc = NULL;
delete ca;
ca = NULL;
} void Print::printError()
{
cout << "Input error!" << endl;
} /*************************************************
Description: 针对命令行参数为 表达式 的输出
Input: string input: 待计算的表达式
Output: 表达式的值
*************************************************/
void Print::printResult(string input)
{
ca->calculatingExpression(sc->ToStringQueue(input), sc->getIsExceed10());
cout << ca->getDigitStack().top() << endl;
ca->getDigitStack().pop();
} /*************************************************
Description: 针对命令行参数为 "-a"和表达式 的输出
Input: string input: 待计算的表达式
Output: 表达式及表达式的值
*************************************************/
void Print::printResult_a(string input)
{
cout << input << " ";
ca->calculatingExpression(sc->ToStringQueue(input), sc->getIsExceed10());
cout << ca->getDigitStack().top() << endl;
ca->getDigitStack().pop();
} /*************************************************
Description: 针对命令行参数为 "-f"和输入、输出文件地址 的输出
Input: string inputAddr: 即读取的文件地址
string outputAddr: 即写入的文件地址
*************************************************/
void Print::printResult_f(string inputAddr, string outputAddr)
{
string input;
fstream file_read(inputAddr);
fstream file_write(outputAddr, ios::out);
while (!file_read.eof())
{
file_read >> input;
ca->calculatingExpression(sc->ToStringQueue(input), sc->getIsExceed10());
file_write << input << " " << ca->getDigitStack().top() << "\n";
ca->getDigitStack().pop();
}
file_read.close();
file_write.close();
}


Part 3

下面则是main函数的实战部分,由于将输出部分移到了Print类,故main函数只需对传进的命令行参数进行判断即可(依旧用了strcmp...),针对不同输入格式调用Print类的不同方法。

main.cpp

#include "Print.h"
#include <iostream>
#include <cstring> using namespace std; /*************************************************
Description: 接收表达式的输入并求值
Input: int argc: 命令行参数个数
char *argv[] : 实际传入的命令行参数
Return: 若正常结束,则返回0
*************************************************/
int main(int argc, char *argv[])
{
Print *pr = new Print();
if (argc == 2) // 对传入的参数只有表达式的处理,调用Print类对应的PrintResult方法输出
{
pr->printResult(argv[1]);
}
else if (!strcmp(argv[1], "-a") && argc == 3) // 对传入的参数为"-a"的处理,调用Print类对应的PrintResult_a方法输出
{
pr->printResult_a(argv[2]);
}
else if (!strcmp(argv[1], "-f") && argc == 4) // 对传入的参数为"-f"的处理,调用Print类对应的PrintResult_f方法输出
{
pr->printResult_f(argv[2], argv[3]);
}
else
{
pr->printError();
} // system("pause");
return 0;
}


然后附上实战结果= =




Part 4

最后,则是框架图...模仿着加上几个类和清一色的几个箭头----------→→→→欧啦= =

Part 5

胡乱讲了一大堆导致篇幅有点长,感觉自己的代码还是有很多提升的空间的,比如Scan类的ToStringQueue方法可以改进,而且初始化也是一个需要注意的点,再加上对构造、析构函数的运用,函数的重载等等。

期待指点,期待vision 1.6.0的到来(hhh~)

参考资料:

The End

面向对象程序设计_Task5_Calculator1.5.0的更多相关文章

  1. [.net 面向对象程序设计深入](0) 开篇

    [.net 面向对象程序设计深入](0)开篇        [.net 面向对象编程基础]和 [.net 面向对象程序设计进阶]在15年底写完了,群里也加进来不少热爱学习的小伙伴.让我深切感受到在这个 ...

  2. [.net 面向对象程序设计深入](4)MVC 6 —— 谈谈MVC的版本变迁及新版本6.0发展方向

    [.net 面向对象程序设计深入](4)MVC 6 ——谈谈MVC的版本变迁及新版本6.0发展方向 1.关于MVC 在本篇中不再详细介绍MVC的基础概念,这些东西百度要比我写的全面多了,MVC从1.0 ...

  3. [.net 面向对象程序设计深入](6).NET MVC 6 —— 模型、视图、控制器、路由等的基本操作

    [.net 面向对象程序设计深入](6).NET MVC 6 —— 模型.视图.控制器.路由等的基本操作 1. 使用Visual Studio 2015创建Web App (1)文件>新建> ...

  4. [.net 面向对象程序设计深入](5)MVC 6 —— 构建跨平台.NET开发环境(Windows/Mac OS X/Linux)

    [.net 面向对象程序设计深入](5)MVC 6 —— 构建跨平台.NET开发环境(Windows/Mac OS X/Linux) 1.关于跨平台 上篇中介绍了MVC的发展历程,说到ASP.NET ...

  5. [.net 面向对象程序设计深入](1)UML——在Visual Studio 2013/2015中设计UML类图

    [.net 面向对象程序设计深入](1)UML——在Visual Studio 2013/2015中设计UML类图 1.UML简介 Unified Modeling Language (UML)又称统 ...

  6. [.net 面向对象程序设计进阶] (25) 团队开发利器(四)分布式版本控制系统Git——使用GitStack+TortoiseGit 图形界面搭建Git环境

    [.net 面向对象程序设计进阶] (25) 团队开发利器(四)分布式版本控制系统Git——使用GitStack+TortoiseGit 图形界面搭建Git环境 本篇导读: 前面介绍了两款代码管理工具 ...

  7. [.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦

    [.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦 本节导读:上篇文章简单介绍了.NET面向对象中一个重要的技术反射的基本应用,它可以让我们动态的调 ...

  8. [.net 面向对象程序设计进阶] (20) 反射(Reflection)(上)利用反射技术实现动态编程

    [.net 面向对象程序设计进阶] (20) 反射(Reflection)(上)利用反射技术实现动态编程 本节导读:本节主要介绍什么是.NET反射特性,.NET反射能为我们做些什么,最后介绍几种常用的 ...

  9. [.net 面向对象程序设计进阶] (19) 异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序

    [.net 面向对象程序设计进阶] (19) 异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序 本节导读: 本节主要说明使用异步进行程序设计的优缺点及如何通过异步编程. 使用 ...

随机推荐

  1. 求两个Linux文本文件的交集、差集、并集

    一.交集 sort a.txt b.txt | uniq -d 二.并集 sort a.txt b.txt | uniq 三.差集 a.txt-b.txt: sort a.txt b.txt b.tx ...

  2. 阿里云Maven仓库

    <mirror> <id>nexus-aliyun</id> <mirrorOf>*</mirrorOf> <name>Nexu ...

  3. [疑难杂症]解决实际开发中各种问题bug

    我有一个习惯就是遇到问题找到解决方案后收藏网页.后来遇到问题越来越多,收藏就多得有点离谱了.我反思了一下,其实有用的信息就那么点,那我干脆还是做成网页剪报好了. 关于VS的 Problem:未能正确加 ...

  4. Differences between write through and write back

    https://stackoverflow.com/questions/27087912/write-back-vs-write-through

  5. linux 用vi命令的使用以及vi编辑后的后续保存退出等相关命令的使用

    一.首先用vi命令打卡要编辑的文件: 注意:vi命令的使用如下 打开或新建文件,并将光标至于第一行首:[root@centos6 /]# vi /etc/my.cnf 打开文件,并将光标移至最后一行行 ...

  6. Github的readme.md的排版

    排版格式: 1 标题与文字格式 标题 # 这是 H1 <一级标题> ## 这是 H2 <二级标题> ###### 这是 H6 <六级标题> 文字格式 **这是文字粗 ...

  7. Android Viewpager+Fragment实现滑动标签页

    ViewPager 结合Fragment实现一个Activity里包含多个可滑动的标签页,每个标签页可以有独立的布局及响应. 主页布局 <?xml version="1.0" ...

  8. iphone精简教程

    那么教程开始 首先讲一下到底什么是精简 精简,就是把iphone4里面没用的自带软件和一些没用的东西删除 比如说股票,facetime,itunes store这些从来不用的东西,把这些东西删除从而使 ...

  9. CentOS6.5安装vncserver实现图形化访问

    一. 安装gnome图形化桌面 #yum groupinstall -y "X Window System" #yum groupinstall -y "Desktop& ...

  10. 解决升级PHP7.1后,发邮件时提示“fsockopen(): Peer certificate CN=`xxx.xx.com' did not match expected CN=`113.x.xx.98”

    把项目环境升级到PHP7.1后,发现在不使用SSL时可以使用IP发邮件,可设置成SSL时就只能使用hostname发送,PHP提示的错误信息大致意思是说,IP与hostname无法通过SSL验证,修改 ...