分类

课内:
不限
类型:
不限 毕业设计 课程设计 小学期 大作业
汇编语言 C语言 C++ JAVA C# JSP PYTHON PHP
数据结构与算法 操作系统 编译原理 数据库 计算机网络 软件工程 VC++程序设计
游戏 PC程序 APP 网站 其他
评分:
不限 10 9 8 7 6 5 4 3 2 1
年份:
不限 2018 2019

资源列表

  • 基于C++实现的二叉排序树

    一、使用说明1.1 项目简介依次输入关键字并建立二叉排序树,实现二叉排序树的插入和查找功能。
    1.2 项目功能要求二叉排序树就是指将原来已有的数据根据大小构成一棵二叉树,二叉树中的所有结点数据满足一定的大小关系,所有的左子树中的结点均比根结点小,所有的右子树的结点均比根结点大。
    二叉排序树查找是指按照二叉排序树中结点的关系进行查找,查找关键字首先同根结点进行比较,如果相等则查找成功;如果比根节点小,则在左子树中查找;如果比根结点大,则在右子树中进行查找。这种查找方法可以快速缩小查找范围,大大减少查找关键的比较次数,从而提高查找的效率。
    1.3 操作手册运行程序后,进入欢迎界面,首先要输入表达式。
    第一步,选择操作:

    选择操作1后,输入二叉树的所有数据,当输入完所有数据后,输入0即可。系统会自动显示重复的数据,并且从小到大展示二叉树的数据。

    选择操作2后,首先输入要插入的数据。之后会自动显示数据插入后的二叉树。

    如果数据已存在在二叉树中也会有提示,同时会展示二叉树。

    选择操作3后,首先输入要查询的数据,之后会显示是否查询成功。

    1.4 注意事项
    用户需先建立二叉树,才可以进行操作2和3,否则系统会报错
    用户进行操作1输入数据建立二叉树时,数据元素不得为0,0是作为输入数据的结束标志,且必须输入数字,不能输入其他字符
    同样,在进行操作2和操作3时,输入的必须是数字,不得是其他字符

    二、概述2.1 基本思路二叉排序树节点的插入只需要从根节点开始,不断地判定插入值和节点值的大小,小则进入左子树,大则进入右子树,在这过程中,如果不存在子树了,就插入在该处即可。查找的算法也是一样的。
    2.2 文件目录
    test.cpp (主文件)
    tree.h (定义Node结构体和Tree类)
    tree.cpp (Tree类相关函数的具体实现方法)
    test.exe (程序可执行文件)
    设计报告.doc (项目文档)

    三、具体实现3.1 结构体Nodeint data; // 存储数据Node *left; // 二叉树节点左指针Node *right; // 二叉树节点右指针
    3.2 方法实现具体说明
    生成二叉排序树首先利用一个vector来存储用户输入的数据,将数据存入到vector中后,建立一个根节点,为每个节点分配内存并存入vector中对应的数据。其中在建立每个节点时用了递归函数,如果将要存入的数据比节点数据大,则节点作为右子树节点,如果数据比节点数据小,则节点作为左子树节点。存完数据后打印二叉树的数据,按从小到大的顺序,先遍历左子树再遍历右子树。
    插入元素首先获得用户要插入的数据,之后为该数据建立新节点,与生成二叉排序树时为每个数据建立节点的方法相同。
    查询元素同样利用递归的方式寻找数据,如果寻找的数据小于节点数据,从左子树开始遍历,遍历完左子树后对右子树遍历,直到找到该数据。
    1 评论 6 下载 2018-11-04 18:20:26 下载需要8点积分
  • 基于C++的电网建设造价模拟系统

    一、使用说明1.1 项目简介假设一个城市有n个小区,要实现n个小区之间的电网都能够相互接通,构造这个城市n个小区之间的电网,使总工程造价最低。请设计一个能够满足要求的造价方案。
    1.2 项目功能要求在每个小区之间都可以设置一条电网线路,都要付出相应的经济代价。n个小区之间最多可以有n(n-1)/2条线路,选择其中的n-1条使总的耗费最少。
    1.3 操作手册运行程序后,首先要选择操作。
    第一步,选择操作A创建电网顶点,并且输入顶点个数及顶点的名称。

    第二步,选择操作B添加电网的边,输入对应的两个顶点及其边的权。

    第三步,选择操作C构造最小生成树,并且输入起始顶点。

    第四步,选择操作D显示最小生成树。

    1.4 注意事项
    对电网模拟造价系统操作时按照A、B、C、D的操作顺序进行,不可跨步骤进行操作
    在进行操作A创建电网顶点时输入的顶点名称个数不得超过顶点个数
    只有在输入大写的A、B、C、D、E时电网模拟系统才会进行操作
    在进行操作B时输入顶点名称一定是已有的顶点名称,且先输入顶点名称在输入权。且必须按照A操作的名称顺序输入。如在A操作中输入的顺序为a b c d,则对应B操作中需要按照顺序输入a b, b c, c d等,不得出现b a,d b,c a等反顺序
    在进行操作C时输入的起始点必须是已有点

    二、概述2.1 基本思路采用Prim算法,构造MST类,本题中心思想是逐个将顶点连通来构造最小生成树,从连通网络中的某一顶点出发,将起点加入生成树的集合中,选择与它关联的权最小的边,将其终点加入到生成树的集合中,之后每一步从一个顶点在生成树的集合中,和另一个不在生成树的集合中的点集中找到权值最小的边加入生成树的边集中,直到所有顶点加入生成树集合中。
    2.2 文件目录
    test.cpp (主文件)
    test.exe (程序可执行文件)
    设计报告.doc (项目文档)
    MST.h(头文件)
    MSTfunction.cpp(MST相关方法)

    三、具体实现3.1 MST类


    名称
    数据类型
    用途




    pNumber
    int
    电网节点的个数


    pName
    Vector<string>
    存储所有节点的名称


    iCount
    int
    电网节点个数计数器


    iStart
    int
    起始点位置对应pName中的位置


    iWeight
    int
    存在比temp数值更小的边的权


    map
    Int **
    用于存储各边之间的权


    start
    string
    起始点位置的名称


    store
    Vector<pair<int,int>>
    store中的起点存储的每条边的终点和对应的权值




    3.2 创建电网顶点该操作涉及到函数createPoint(),获得顶点个数以及将各顶点的名称存储到pName中,同时各个顶点有相应的顺序序号。
    3.3 添加电网的边该操作涉及到的函数为addEdge(),首先为map开辟相应的顶点数大小。然后搜索输入两个顶点对应的位置,并将权赋值到对应的map中。
    3.4 构造最小生成树首先计数器每次操作前置0,对store进行清空,以方便重新进行操作C。函数先定义的三个int值分别为iPos对应每次搜索的起点,temp对应起点对应的边的权,iName对应搜索后的终点。开始搜索后,首先找到起始点对应pName中的位置,分别讨论iPos为0时可能出现的不同状况,对权赋不同值之后与起点周围的边进行比较,寻找权最小的边,找到后进行判断,store内是否存在顶点对应比temp更小的边,反复重复操作直至所有顶点个数都在store内。其中利用judgePoint和judgeEdge两个方法进行判断是否有重复顶点在store内和是否有权比temp更小的边。
    3.5 显示最小生成树从store内不断取值,首先从store内取出起点和其终点和对应的权,之后进入for循环开始不断判断最小生成树是否有分支,如果有分支,则依据store内的终点和对应的边寻找其起点,首先在map中找到对应的边,依据该边在map中的位置能够推测出起点和终点,之后进行判断哪个为起点,便可输出。
    1 评论 31 下载 2018-11-04 18:16:46 下载需要7点积分
  • 基于C++的表达式计算求值

    一、使用说明1.1 项目简介表达式求值是程序设计语言编译中的一个最基本的问题,就是将一个表达式转化为逆波兰表达式并求值。具体要求是以字符序列的形式从终端输入语法正确的、不含变量的整数表达式,并利用给定的优先关系实现对算术四则混合表达式的求值,并演示在求值过程中运算符栈,操作数栈,输入字符和主要操作变化过程。
    要把一个表达式翻译成正确求值的一个机器指令序列,或者直接对表达式求值,首先要能正确解释表达式。任何一个表达式都是由操作符,运算符和界限符组成,我们称它们为单词。一般来说,操作数既可以是常数,又可以是被说明为变量或常量的标识符;运算符可以分成算术运算符,关系运算符和逻辑运算符3类;基本界限符有左右括号和表达式结束符等。为了叙述的简洁,我们仅仅讨论简单算术表达式的求值问题,这种表达式只包括加,减,乘,除4种运算符。
    人们在书写表达式时通常采用的是“中缀”表达形式,也就是将运算符放在两个操作数中间,用这种“中缀”形式表示的表达式称为中缀表达式。但是,这种表达式表示形式对计算机处理来说是不大合适的。对于表达式的表示还有另一种形式,称之为“后缀表达式”,也就是将运算符紧跟在两个操作数的后面。这种表达式比较合适计算机的处理方式,因此要用计算机来处理,计算表达式的问题,首先要将中缀表达式转化成后缀表达式,又称为逆波兰表达式。
    1.2 项目功能要求为了实现表达式求值,本项目要求首先读入表达式(包括括号)并创建对应二叉树,其次对二叉树进行前序遍历,中序遍历,后序遍历,输出对应的波兰表达式,中缀表达式和逆波兰表达式。
    1.3 操作手册运行程序后,进入欢迎界面,首先要输入表达式。
    第一步,输入中缀表达式:

    在输入相应表达式后,会自动显示中缀、前缀、后缀表达式。



    1.4 注意事项
    进行操作时,只能输入1或0进行操作
    操作的表达式形式为中缀表达式,可以进行括号、加减乘除转化
    不能进行表达式的计算,不能输入等号或其他字符。只能输入数字和运算符

    二、概述2.1 基本思路在得到用户输入的中缀表达式后,先利用栈将其转换为后缀表达式,同样再利用栈将其转化为二叉树,之后对二叉树进行中序遍历得到中缀表达式,前序遍历得到前缀表达式,后序遍历得到后缀表达式。
    2.2 文件目录
    Test.cpp (主文件)
    Expression.h (定义TreeNode结构体)
    Expression.cpp (Brother_list类相关函数的具体实现方法)
    test.exe (程序可执行文件)
    设计报告.doc (项目文档)

    三、具体实现3.1 结构体TreeNodeChar data; // 存储数据BinaryNode *left; // 二叉树节点左指针BinaryNode *right; // 二叉树节点右指针
    3.2 方法实现具体说明
    中缀表达式转后缀表达式在得到用户输入的中缀表达式后,首先要将中缀表达式转换为后缀表达式。这里利用了两个栈,operate为运算符栈,number为数字栈,之后对中缀表达式进行遍历,遇到数字则直接输出 ,遇到’(‘压栈 ,遇到’)’持续出栈,如果出栈的符号不是’(‘则输出,否则终止出栈。 遇到符号则判断该符号与栈顶符号的运算优先级,如果栈顶符号的运算优先级高,则出栈并输出,直到优先级相等或栈为空;如果栈顶符号的运算优先级低于或等于当前符号的运算优先级,则将当前符号压栈。 处理完字符串后将栈中剩余的符号全部输出,最后得到后缀表达式suffix_exp。关于判断运算符的优先级,利用了二维数组判断优先级更加方便。
    后缀表达式转换为二叉树遍历后缀表达式每个字符,数字直接入栈,运算符创建二叉树节点,创建二叉树节点后,为其创建左右子节点,将栈顶的两个数据弹出作为左右子节点,再将运算符入栈。
    前序、中序、后序遍历三种遍历方式分别得到前缀、中缀、后缀表达式,此三种遍历方式的方法大致相同,都是使用递归遍历节点,只是顺序不同。前序遍历,前序遍历先将根节点找出,再不断搜寻左子树的节点,最后将右子树的节点找出可得到前缀表达式。中序遍历,中序遍历是先找出左子树的节点,再找出根节点,最后找出右子树的所有节点从而转为中缀表达式。后序遍历,后序遍历是先找到所有左子树节点,再将右子树节点找出,最后找出根节点。
    1 评论 19 下载 2018-11-04 18:13:03 下载需要2点积分
  • 基于C++的关键字检索系统

    一、使用说明1.1 项目简介建立一个文本文件,文件名由用户用键盘输入,输入一个不含空格的关键字,统计输出关键字在文本中的出现次数。
    1.2 项目功能要求本项目的设计要求可以分成两个部分实现:首先建立一个文本文件,文件名由用户用键盘输入;然后输入一个不含空格的关键字,统计输出该单词在文本中的出现次数。
    1.3 操作手册运行程序后,首先要输入文件名。
    第一步,输入文件名。

    之后输入一段文章:

    文章的结尾通过输入”^”进行判断

    接下来要输入检索的关键字,会先显示源文件内容,之后再显示关键词出现的次数:

    1.4 注意事项
    文章输入完毕后需要输入“^”符号表示文章结束。但该符号并不有影响关键字统计
    按数字0结束系统工作

    二、概述2.1 基本思路利用Vector数组来存储文章内容,article用来存储文章内容。在屏幕输入文章后保存到文本文件,同时存入article容器中。在文章的结尾用”^”来标记,为了防止该标记对关键字统计造成影响便将该标记去除。
    2.2 文件目录
    test.cpp (主文件)
    Keyword.cpp(相关函数具体实现)
    Keyword_search.h(头文件)
    test.exe (程序可执行文件)
    设计报告.doc (项目文档)

    三、具体实现3.1 朴素的模式匹配(Find(string main_string, string keyword))朴素的模式匹配,即B-F算法,方法是让模式串与目标串进行逐个字符的比较,若目标串中存在与模式串相同的部分,则函数返回true,否则返回false或从下一个位置开始继续进行逐个字符比较。
    具体的函数操作为,先进行逐趟比较,模式串与目标串从0的位置开始进行比较,如果相同则证明iJ与模式串长度相同直接返回true,如果不同,iX+1,从下一个位置开始继续进行逐趟比较,直到比较完所有字符仍不相同则返回False。
    3.2 KMP算法本题可以用KMP算法去实现,但是过程比较复杂。因为B-F算法速度慢,因为存在回溯的问题,而KMP算法则避免了多余的回溯。KMP算法的核心思想是利用上一次的比较结果,KMP算法首先需要确定子串key中的每一个字符的前缀和后缀的共有元素的最长长度,得到next数组。比如key[6]的共有元素有A和ABCA两个,最长长度为4。假设key[0]~key[5]都与主串相等,但是key[6]不相等,我们不再直接从key[0]开始比较,而是根据其前一位的next值决定。由于key[6]的前一位key[5]的前缀和后缀共有元素为ABC,那么根据(需要向前移动位数=已匹配位数-共有元素最长长度),需要移动6-3=3位,也就是从直接从key[3]开始比较。从上可知,共有元素最长长度是多少,key就跳到以长度为下标处。若是仍不相等的话,就看key[3]的前一位key[2]的前后缀共有元素为0,长度为0,则跳到key[0]来比对,依此类推。
    3.3 其他函数(display_article(vector<string>article),Search(string fileName, vector<string>article))Display_article函数直接展示文章内容,Search函数用来进行关键字检索并给关键字计数。
    四、体会通过这道题,我对朴素模式的匹配方法已经完全掌握,KMP算法的核心思想也已经掌握,但是由于时间问题暂时未能写出KMP算法的具体函数,对于该文字检索系统可以做进一步的改进,可以考虑通过对每个单词判断,将标点符号去除缩短运行的时间。而去除标点符号的方式则只需判断单词的最后一个字符是否为标点,是的话去除即可。
    1 评论 13 下载 2018-11-04 18:04:15 下载需要3点积分
  • 基于C++的勇闯迷宫游戏

    一、使用说明1.1 项目简介迷宫只有两个门,一个门叫入口,另一个门叫出口。一个骑士骑马从入口进入迷宫,迷宫设置很多障碍,骑士需要在迷宫中寻找通路以到达出口。
    1.2 项目功能要求提示:

    可以采用二维数组,回溯和递归或非递归加栈实现
    也可以用BFS算法(即图的广度优先搜索算法,又叫宽度优先搜索算法)
    如果用数组的方法,则如果有多条出路,只需要显示一条出路即可,且不一定需要选出最短出路
    如果用BFS的方法,则需要显示所有的出路

    迷宫问题的求解过程可以采用回溯法,即在一定的约束条件下试探地搜索前进,若前进中受阻,则及时回头纠正错误另择通路继续搜索的方法。从入口出发,按某一方向向前探索,若能走通,即某处可达,则到达新点,否则探索下一个方向;若所有的方向均没有通路,则沿原路返回前一点,换下一个方向再继续试探,直到所有可能的道路都探索到,或找到一条通路,或无路可走又返回入口点。在求解过程中,为了保证在达到某一个点后不能向前继续行走时,能正确返回前一个以便从下一个方向向前试探,则需要在试探过程中保存所能够达到的每个点的下标以及该点前进的方向,当找到出口时试探过程就结束了。
    1.3 项目要求
    迷宫的行数,列数和起点坐标由用户输入(行数和列数可以不相等),不能由程序定死
    迷宫地图需由用户输入,可以用1代表障碍,0代表通路
    规定第一行,最后一行,第一列,最后一列是墙壁。如果迷宫有出路,则必须从墙壁进,最后从墙壁出,但起点和终点不能是同一个点

    1.4 操作手册运行程序后,首先选择要进行的操作。

    第一步,选择操作1自定义地图模式后,输入迷宫的行数和列数以及起始点所在行和所在列。

    第二步,依次输入每个坐标,0表示通路,1表示墙。

    第三步,系统自动显示迷宫地图和路径图以及迷宫路径的坐标。

    第四步,如果迷宫无解则系统显示迷宫无解。

    之后课继续选择操作1重复迷宫游戏,选择操作0退出游戏。
    1.5 注意事项
    输入的迷宫地图必须用1代表墙,0代表通路,不可输入其他数字
    输入迷宫地图时需要每输入一个坐标的值后便回车,利用二维数组存储需要逐个输入,不可整行输入
    输入的迷宫地图和定义的行列数必须保持一致

    二、概述2.1 基本思路基本思想为利用二维数组,通过回溯和递归实现,由上、下、左、右的顺序进行搜索,对任一可行节点进行递归搜索,每一步保存路径。在递归失败之后层层退栈,继续尝试下一可行节点,直到找到出口。然后将迷宫栈逐步出栈即可找到可行路径。在对迷宫栈进行操作的同时保存通路栈。
    2.2 文件目录
    test.cpp (主文件)
    test.exe (程序可执行文件)
    设计报告.doc (项目文档)
    maze.h(头文件)
    maze.cpp(迷宫相关函数实现)

    三、具体实现3.1 几个全局变量
    int row:迷宫行数
    int colum:迷宫列数
    int start_row:起始点坐标所在行
    int start_col:起始点坐标所在列
    int **map:迷宫地图
    Stack *Maze:迷宫栈
    path *Path:通径栈

    3.2 迷宫栈(struck stack)迷宫栈中的x和y表示坐标点(x,y)所在行和所在列,迷宫栈存放着勇闯迷宫游戏中每步前进所在的坐标。
    3.3 通径栈(struck path)通径栈中的x和y表示坐标点(x,y)所在行和所在列,inPath表示该点是否在通径中,因为迷宫栈中存在不在路径中的坐标点,这种点的inPath为false,通径栈主要是为了方便展示迷宫地图和迷宫路径。
    3.4 检测数字是否合法(函数int legal_number(int num))用来检测用户输入的行数、列数、起始点所在行、起始点所在列是否合法,如果小于0则需重新输入直至无误。
    3.5 检测坐标是否已在迷宫栈内(函数bool search(int x, int y))search函数用于检测(x,y)坐标是否已存在于迷宫栈内,即该位置是否走过。通过对迷宫栈内的每个(x,y)坐标与被检测的坐标进行比较,如果存在相同的数据则返回false,否则返回true。
    3.6 判断下一个坐标点是否可走(函数bool escape_maze(int x, int y))该函数为递归函数,在函数开始阶段,先将坐标点(x,y)加入路径栈和迷宫栈,因为该点一定为游戏中走过的坐标点。在加入迷宫栈之前,先判断其是否为终点,如果为终点则直接返回true,游戏结束。判断是否为终点的方法是利用函数search并判断其是否处在边界。
    在完成以上操作后,开始向四个方向探索,分别先是上、下、左、右,如果被探索的点不在迷宫栈内,且该点为通路,并且没有超出迷宫范围,则以该点为x,y进入下一次递归,再次调用escape_maze函数,直到所有方向都不行,迷宫栈元素出栈,并且该点的inPath标志为false,否则为true。
    3.7 开始进行递归的函数(函数void maze())该函数与excape_maze函数相似,只是进行迷宫游戏的第一步。
    3.8 判断坐标是否在通径栈内(函数bool inPath(int i, int j))该行数用于判断坐标是否在通径栈内,实现方法类似于search函数。
    3.9 展示迷宫(函数void display_maze())该函数用于展示迷宫地图和迷宫路径。
    3.10 迷宫游戏主程序(void maze_game())该函数用于建立迷宫,展示迷宫,展示路径等操作。
    1 评论 21 下载 2018-11-04 18:00:17 下载需要4点积分
  • 基于C++实现的考试报名系统

    一、使用说明1.1 项目简介考试报名工作给各高校报名工作带来了新的挑战,给教务管理部门增加了很大的工作量。本项目是对考试报名管理的简单模拟,用控制台选项的选择方式完成下列功能:输入考生信息;输出考生信息;查询考生信息;添加考生信息;修改考生信息;删除考生信息。
    1.2 项目功能要求本项目的实质是完成对考生信息的建立,查找,插入,修改,删除等功能。其中考生信息包括准考证号,姓名,性别,年龄和报考类别等信息。项目在设计时应首先确定系统的数据结构,定义类的成员变量和成员函数;然后实现各成员函数以完成对数据操作的相应功能;最后完成主函数以验证各个成员函数的功能并得到运行结果。
    1.3 操作手册运行程序后,进入欢迎界面,首先要输入考生的数据。
    第一步,输入所有考生的信息。

    之后会显示所有考生的信息。

    在指定位置插入一个考生信息,例如:

    在位置5插入stu5的信息。
    成功后,系统会显示插入完成后的所有信息。

    删除指定考号的考生信息,例如:

    删除考号为2的学生。删除后系统会显示删除的考生信息和删除操作后的所有信息。

    查找指定考号的考生信息,例如:

    查找考号为3的考试信息。查找完成后系统会显示查找到的考生信息。

    修改指定考号的考生信息,例如:

    修改考号为3的考生的信息。修改完成后系统会显示所有考生信息。

    既统计考生人数又可以统计报考某一类别的考生数量,系统会显示报考该类别的人数。

    1.4 注意事项
    本系统的考号请勿超过9位(16位操作系统请勿超过5位)
    本系统不自动储存考生信息,请自行复制系统打印的信息另行保存
    本系统没有设计排序功能,所以所有查找都采用顺序查找,大数据量可能较慢,请耐心等待

    二、概述2.1 基本思路使用双向链表来存储考生的基本信息,对考生报名系统的所有操作都是通过对双向链表来进行,用链表存储数据更加灵活方便,对双向链表进行插入、删除、查找等操作比较方便。链表是用linkedList类来实现,考生的信息是存储在结构体STU中。链表的头指针和尾指针作为全局变量使用。
    2.2 文件目录
    test.cpp (主文件)
    1.h (定义linkedlist类和STU结构体)
    2.h (linkedlist类相关函数的具体实现方法)
    test.exe (程序可执行文件)
    设计报告.doc (项目文档)

    2.3 具体实现linkedlist接口



    成员函数名
    功能
    参数




    linkedlist ()
    默认构造函数,设定链表长度为0。



    ~linkedlist ()
    析构函数,清空全表



    insertStu()
    插入函数,在输入pos位置后插入*insertStu



    copyStu(STU *student1,STU &student2)
    将student2的信息复制给student1
    被拷贝的信息student2 拷贝的信息student1


    addStu(STU &item)
    添加考生信息
    添加的信息item


    editStu()
    按考号编辑考生信息



    findStu ()
    查找考生信息



    findStu(int item)
    按考号查找考生信息
    item为考号


    showStu()
    展示所有考生信息



    StaStu()
    按报考类别统计考生信息



    2.2 结构体STU
    考生姓名 stuName(string)
    考号 stuTestNum(int)
    年龄 stuAge(int)
    性别 stuSex(string)
    报考类别 stuCategory(string)
    前指针 pre (STU*)
    后指针 next (STU*)

    2.3 方法实现具体说明
    构造函数:将链表长度设置为 0
    析构函数:释放结构体
    插入函数:用户输入插入位置,插入方式为调用类方法findStu(int pos)找到该位置,判断该位置是第一个、最后一个还是中间的位置。然后操作前后指针进行插入,同时动态生成需要插入的数据,最后链表长度加一
    查找函数:根据用户插入的位置,建立一个头指针,依次从头寻找到该位置,将该位置考生的信息输出
    删除函数:根据输入的考生的考号,先判断其位置,找到需要删除的结构体之后,对其前后指针进行操作,将其从链表中脱离出来,然后将其内存释放
    编辑函数:根据输入的考生的考号,找到考生的位置,重新输入考生的信息
    统计函数:先是统计出所有考生人数,然后根据输入的考生的报考类别,对当前链表进行一次遍历,遍历过程中,通过字符串比较,得出报考人数
    1 评论 31 下载 2018-11-04 17:57:05 下载需要2点积分
  • 基于C++的约瑟夫生者死者游戏

    一、使用说明1.1 项目简介约瑟夫生者死者游戏的大意是:30个旅客同乘一条船,因为严重超载,加上风高浪大危险万分;因此船长告诉乘客,只有将全船一半的旅客投入海中,其余人才能幸免于难。无奈,大家只得同意这种方法,并议定30个人围成一圈,由第一个人开始,依次报数,数到第9人,便将他投入大海中,然后从他的下一个人数起,数到第9人,再将他投入大海,如此循环,直到剩下15个乘客为止。问哪些位置是将被扔下大海的位置。
    1.2 项目功能要求本游戏的数学建模如下:假如N个旅客排成一个环形,依次顺序编号1, 2, …, N。从某个指定的第S号开始。沿环计数,每数到第M个人就让其出列,且从下一个人开始重新计数,继续进行下去。这个过程一直进行到剩下K个旅客为止。
    本游戏要求用户输入的内容包括:

    旅客的个数,也就是N的值
    离开旅客的间隔数,也就是M的值
    所有旅客的序号作为一组数据要求存放在某种数据结构中

    本游戏要求输出的内容包括:

    离开旅客的序号
    剩余旅客的序号

    1.3 操作手册运行程序后,进入欢迎界面,首先要输入数据。
    第一步,输入生死游戏的总人数N、游戏开始的位置S、死亡数字M、剩余的生者人数K。

    之后会显示离开旅客的序号和剩余旅客的序号。

    1.4 注意事项
    所有数据必须大于等于0
    生者人数要小于等于总人数
    死亡数字要小于总人数

    二、概述2.1 基本思路使用单循环链表来存储旅客的位置信息,然后在for循环内找到死亡旅客的位置,找到该位置后将前一个位置的next指针连接到下一个位置上,相当于删除了该死亡旅客的位置,并将其信息打印出来,之后总人数减一。逐步寻找并删除,直至总人数等于幸存者人数,然后按位置的大小顺序排序后打印出幸存者位置。
    2.2 文件目录:
    test.cpp (主文件)
    Struct.h (结构体Struct和核心函数)
    test.exe (程序可执行文件)
    设计报告.doc (项目文档)

    三、具体实现3.1 结构体travellerpos 位置 (int)、*next 链接用指针(traveller)
    3.2 方法实现具体说明创建一个单循环链表temp用来存储所有旅客的位置信息,然后处理一下位置为1时的特殊情况,之后用for循环开始寻找死亡旅客的位置,找到后将其前一个数据的指针链接到下一个数据,相当于删除了该数据,并将其位置信息打印出来,总人数减一,之后不断寻找直至总人数和幸存人数相同,然后按位置大小顺序将幸存者位置打印出来。
    1 评论 8 下载 2018-11-04 17:53:15 下载需要2点积分
  • 基于栈和队列的停车场管理系统

    1 问题描述设停车场内只有一个可停放n辆汽车的狭长通道,且只有一个大门可供汽车进出。汽车在停车场内按车辆到达时间的先后顺序,依次由北向南排列(大门在最南端,最先到达的第一辆车停放在车场的最北端),若车场内已停满n辆汽车,则后来的汽车只能在门外的便道上等候,一旦有车开走,则排在便道上的第一辆车即可开入。
    当停车场内某辆车要离开时,在它之后开入的车辆必须先退出车场为它让路,待该辆车开出大门外,其它车辆再按原次序进入车场,每辆停放在车场的车在它离开停车场时必须按它停留的时间长短交纳费用。
    试为停车场编制按上述要求进行管理的模拟程序。
    2 基本要求综合利用栈和队列模拟停车场管理,学习利用栈和队列解决实际问题。
    以栈模拟停车场,以队列模拟车场外的便道,按照从终端读入的输入数据序列进行模拟管理。每一组输入数据包括三个数据项:汽车“到达”或“离去”信息、汽车牌照号码及到达或离去的时刻,对每一组输入数据进行操作后的输出数据为:若是车辆到达,则输出汽车在停车场内或便道上的停车位置;若是车离去;则输出汽车在停车场内停留的时间和应交纳的费用(在便道上停留的时间不收费)。栈以顺序结构实现,队列以链表实现。
    需另设一个栈,临时停放为给要离去的汽车让路而从停车场退出来的汽车,也用顺序存储结构实现。输入数据按到达或离去的时刻有序。栈中每个元素表示一辆汽车,包含两个数据项:汽车的牌照号码和进入停车场的时刻。
    3 算法思想停车场运用栈的算法思想管理车辆信息;便道运用队列的算法思想管理等待进入停车场的车辆信息;临时停放让路的车辆信息也用队列算法思想管理。
    4 模块划分// 其功能为打印出场车的信息void PRINT(CarNode *p,int room);// 其功能为记录进场车和等待进场车信息void Arrive(SeqStackCar *Enter,LinkQueueCar *W);// 其功能为记录出场车信息void Leave(SeqStackCar *Enter,SeqStackCar *Temp,LinkQueueCar *W);// 其功能为显示存车信息void List1(SeqStackCar *S);
    5 数据结构数据类型Time定义如下:
    typedef struct time{ int hour; int min;}Time;
    数据类型CarNode定义如下:
    typedef struct node{ char num[10]; Time reach; Time leave;}CarNode;
    数据类型SeqStackCar定义如下:
    typedef struct NODE{ CarNode *stack[MAX+1]; int top;}SeqStackCar;
    数据类型QueueNode定义如下:
    typedef struct car{ CarNode *data; struct car *next;}QueueNode;
    数据类型LinkQueueCar定义如下:
    typedef struct Node{ QueueNode *head; QueueNode *rear;}LinkQueueCar;
    6 测试


    1 评论 16 下载 2018-11-04 17:13:54 下载需要3点积分
  • 基于C++实现的N皇后问题

    一、使用说明1.1 项目简介八皇后问题是一个古老而著名的问题,是回溯算法的经典问题。该问题是十九世纪著名的数学家高斯在1850年提出的:在8*8的国际象棋棋盘上,安放8个皇后,要求没有一个皇后能够“吃掉”任何其它一个皇后,即任意两个皇后不能处于同一行,同一列或者同一条对角线上,求解有多少种摆法。
    高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法得到结论,有92种摆法。
    本实验拓展了N皇后问题,即皇后个数由用户输入。
    1.2 项目要求八皇后在棋盘上分布的各种可能的格局数目非常大,约等于2的32次方种,但是,可以将一些明显不满足问题要求的格局排除掉。由于任意两个皇后不能同行,即每行只能放置一个皇后,因此将第i个皇后放在第i行上,这样在放置第i个皇后时,只要考虑它与前i-1个皇后处于不同列和不同对角线位置上即可。
    解决这个问题采用回溯法,首先将第一个皇后放置在第一行第一列,然后,依次在下一行上放置一个皇后,直到八个皇后全部放置安全。在放置每个皇后时,都依次对每一列进行检测,首先检测放在第一列是否与已放置的皇后冲突,如不冲突,则将皇后放置在该列,否则,选择该行的下一列进行检测。如整行的八列都冲突,则回到上一行,重新选择位置,依此类推。
    1.3 操作手册运行程序后,首先要输入皇后的个数N:

    这里输入4为皇后个数后出现该问题的2种解法:

    同样输入6时,会出现4种解法:

    每次完成一次解法后可以考虑是否重新输入皇后个数:

    1.4 注意事项
    每次输入的皇后的个数不要太大,且不能小于等于0
    再判断是否重新输入皇后个数时只能输入“Y”或“N”

    二、概述2.1 基本思路基本思想是采用了提示中的回溯和递归,但并未采用二维数组而是用了一维数组。回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。而用一维数组的原因是,这样做不仅压缩了空间规模,判断冲突也十分简单。首先每行只有一个皇后,且在数组中只占据一个元素的位置,行冲突就不存在了,其次是列冲突,判断一下是否有a[i]与当前要放置皇后的列j相等即可。至于斜线冲突,通过观察可以发现所有在斜线上冲突的皇后的位置都有规律即它们所在的行列互减的绝对值相等,即| row – i | = | col – a[i] | 。这样某个位置是否可以放置皇后的问题已经解决。
    2.2 文件目录test.cpp (主文件)test.exe (程序可执行文件)设计报告.doc (项目文档)NQueen.h(头文件)NQueen.cpp(迷宫相关函数实现)
    三、具体实现3.1 几个全局变量int sum; // 解法种类数 int Queen[20]; // 各皇后所在行号
    3.2 N皇后问题主函数(void NQueen(int iQueen))将sum置0,并且开始搜寻问题的解决方案。
    3.3 放置皇后到棋盘上(void place(int row, int iQueen))首先进行判断,如果此时的行数row已经大于iQueen皇后个数,则已经找到一种解法,直接展示该解法。否则开始试探row行的每一列,完成该列之后开始进行下一次递归。
    3.4 检验第i行的k列上是否可以摆放皇后 (int find(int i, int k))j=1~i-1是已经放置了皇后的行,当j<i时,判断第j行的皇后是否在k列或(j,Queen[j])与(i,k)是否在斜线上,如果不在,继续检测下一行,如果存在,返回0。
    3.5 展示每种解法(void print(int n))先打印出每种解法中皇后所在位置,从iX=1开始,对应Queen[iX]所存列数,之后通过“X”表示皇后的位置,其余位置用“0”表示,展示棋盘。
    1 评论 4 下载 2018-11-04 17:09:36 下载需要4点积分
  • Linux环境下的针对PL0语言的语法词法语义分析

    摘 要此次编译原理课程设计,我利用flex工具进行PL/0语言的词法分析、自己用C++语言实现了LR语法分析、语义分析以及中间代码生成,我选择的是布尔表达式文法,对符合文法的布尔表达式能够产生相应四元式,处理了控制结构的真链与假链,对错误的表达式能够给出错误提示。
    鉴于flex工具原本来自Unix以及个人日常习惯,本实验开发环境选用Linux,代码在Ubuntu16.10中测试通过。
    关键字:flex;词法分析;Linux;语法分析;中间代码生成;真假链
    引 言编译原理是一门实践性比较强的学科,学习了课本上理论知识,阅读了书后示例PL/0编译程序,此次课程设计针对PL/0语言进行了词法分析、语法分析及布尔表达式的中间代码(四元式)生成。其中PL/0的词法分析程序的功能是为语法语义分析提供单词,把输入的字符串形式的源程序分割成一个个单词符号传递给语法语义分析;而语法分析的任务是识别单词符号序列是否符合给定的语法规则。实验中在语法分析过程中需要用到flex工具,这个工具源于Unix和Linux,很多同学使用Windows还需要配置相关环境,我日常使用Linux,因此选用它作为编程环境,免去了不必要的麻烦,配置较为方便。
    一、实验目的实现对布尔表达式的词法分析、语法分析、语义分析并产生四元式中间代码,完成真假链的回填及合并。
    1.1 词法分析部分利用C++和flex工具设计编写一个词法分析程序,实现对标志符、数字、保留字和算符等一系列符号的识别,用于后面做语法分析和语义分析;
    1.2 语法分析部分采用LR(0)语法分析方法,设计、开发如下文法描述语言的语法分析程序,实现对词法分析程序所提供的单词序列进行语法检查和结构分析,加深课堂相关理论教学内容的理解,提高语法分析方法的实践能力。语法分析完成之后可用于语义分析。
    1.3 语义分析部分基于以上两步词法和语法分析的工作,采用一边归约一边建立语义栈并做语义分析的方法,分析给出的布尔表达式是否符合相应文法,最后产生四元式 ,实现真假链的回填及合并 。
    二、实验内容2.1 词法分析阶段词法分析阶段的任务是从左到右一个字符一个字符地读入源程序,对构成源程序的字符流进行扫描和分解,从而识别出一个个单词。利用词法分析程序实现对某输入串的词法分析,将各个符号识别并送给语法分析程序;
    2.2 语法分析阶段利用上述词法分析的结果,一个一个送进LR语法分析的函数 SLR_parser而判断整个输入串是否满足布尔表达式的文法,具体文法如下:
    布尔表达式文法
    [1]E -> E or E[2]E -> E and E[3]E -> not E[4]E -> ( E )[5]E -> id rop id[6]E -> true[7]E -> false
    其中:E-布尔表达式,rop-关系运算符
    2.3 语义分析阶段语义分析阶段,利用上述两步的结果,在归约和移进的过程做语义分析并产生四元式。语义分析时,我将用于语法分析的函数SLR_parser进行扩充,使之在每步归约的时候做中间代码生成以及回填的工作。
    具体的流程如下:

    词法分析之后的语法和语义分析详细过程如下图所示:

    三、主要实现功能3.1 功能一:词法分析接收字符串形式的源程序,按照源程序输入的次序依次扫描源程序,在扫描的同时根据语言的词法规则识别出具有独立意义的单词,并产生与源程序等价的属性字(Token)流。
    我的词法分析通过从读取源文件,得到输入串,利用flex工具,写正则表达式去匹配单词,识别它们的类型,分析结果存到mytoken数组中,待语法分析和语义分析程序使用。
    3.2 功能二:语法分析语法分析是编译过程的一个逻辑阶段。语法分析的任务是在词法分析的基础上将单词序列组合成各类语法短语,如“程序”,“语句”,“表达式”等等.语法分析程序判断源程序在结构上是否正确.源程序的结构由上下文无关文法描述.语法分析程序可以用YACC等工具自动生成。
    应所给题目要求,这里不借助yacc工具,而是自己手写C++代码,完成相应功能。SLR(1)分析核心是要得到一张SLR(1)分析表,理论上可以通过编程求first集和follow集,进而得到这张表。这里我们采用手写的方式得到这张分析表,然后建立状态栈和符号栈,不断查表判断是应该归约还是移进。
    3.3 功能三:语义分析语义分析是编译过程的一个逻辑阶段, 语义分析的任务是对结构上正确的源程序进行上下文有关性质的审查,进行类型审查。语义分析是审查源程序有无语义错误,为代码生成阶段收集类型信息。
    本次课程设计最终目的是产生布尔表达式对应的四元式。上一步语法分析主要通过一个函数SLR_parser实现,此步的语义分析及目标代码生成及真假链的回填与合并是在归约的时候做的,于是就把函数SLR_parser继续扩充,遇到归约就调用相应的函数来生成四元式,实现书上的布尔表达式翻译方案,完成回填和合并工作。如果输入串符合语法 , 把最终生成的四元式写入pcode.txt;若不合语法,给出错误提示。
    四、主要功能的设计4.1 词法分析词法分析部分借助flex工具,我的C++代码pl0lex.cpp完成这一功能,在%{和%}之间写自己的C++代码,之外写正则表达式去匹配相应的符号,在{和}之间写匹配后应该做的事务。通过man flex命令查看系统中flex的帮助文档,我学会了如何让程序既可以从控制台读入又可以从文本读入,主函数是int main( int argc, char **argv ),通过判断调用时后面是带参数函数还是不带参数,选择用标准输入输出还是从文件读入。在每次匹配成功,用于统计总共符号数的total_token加一,rawstr字符串数组存入已经读入的原字符串,mytoken数组存入识别后的结果。这两者有一种对应关系,比如rawstr字符串数组中有一个是x,则mytoken数组中相应位置就存其类型:标识符;rawstr中是大于小于大于等于这类符号,mytoken就存其类型:关系运算符。这样就达到了词法分析的目的,同时还存好了原来的语义值。
    对照课本P15的PL/0语言文法的EBNF表示,通过阅读EBNF范式,我将其逐步转化为正则表达式代码。
    PL/0语言文法的EBNF表示<程序>::=<分程序>.<分程序> ::=[<常量说明>][<变量说明>][<过程说明>]<语句><常量说明> ::=CONST<常量定义>{,<常量定义>};<常量定义> ::=<标识符>=<无符号整数><无符号整数> ::= <数字>{<数字>}<变量说明> ::=VAR <标识符>{, <标识符>};<标识符> ::=<字母>{<字母>|<数字>}<过程说明> ::=<过程首部><分程序>{; <过程说明> };<过程首部> ::=PROCEDURE <标识符>;<语句> ::=<赋值语句>|<条件语句>|<当循环语句>|<过程调用语句> |<复合语句>|<读语句><写语句>|<空><赋值语句> ::=<标识符>:=<表达式><复合语句> ::=BEGIN <语句> {;<语句> }END<条件语句> ::= <表达式> <关系运算符> <表达式> |ODD<表达式> <表达式> ::= [+|-]<项>{<加法运算符> <项>}<项> ::= <因子>{<乘法运算符> <因子>}<因子> ::= <标识符>|<无符号整数>| ‘(’<表达式>‘)’<加法运算符> ::= +|-<乘法运算符> ::= *|/<关系运算符> ::= =|#|<|<=|>|>=<条件语句> ::= IF <条件> THEN <语句><过程调用语句> ::= CALL 标识符<当循环语句> ::= WHILE <条件> DO <语句><读语句> ::= READ‘(’<标识符>{,<标识符>}‘)’<写语句> ::= WRITE‘(’<表达式>{,<表达式>}‘)’<字母> ::= a|b|…|X|Y|Z<数字> ::= 0|1|…|8|9
    PL/0语言的词汇表


    序号
    类别
    单词
    编码




    1
    基本字
    begin, call, const, do, end if, odd, procedure, read then, var, while, write
    beginsym, callsym, constsym dosym, endsym, ifsym, oddsym proceduresym, readsym, thensym varsym, whilesym, writesym


    2
    标识符

    ident


    3
    常数

    number


    4
    运算符
    +, -, *, /, odd =, <>, <, <=, >, >=, :=
    plus, minus, times, slash, oddsym eql, neq, lss, leq, gtr, geq, becomes


    5
    界符
    ( ) , ;.
    Lparen, rparen, comma, semicolon period



    这里像比较高层的程序分程序这种不在词法分析中识别,词法分析中识别的主要是比较低层的单词。
    正则匹配识别函数的设计:
    %%not {/*SLR*/printf("%s:非\n",yytext);mytoken[total_token]=notsym; rawstr[total_token]=yytext; total_token++;/*notsym*/}and {/*SLR*/printf("%s:与\n",yytext);mytoken[total_token]=andsym; rawstr[total_token]=yytext; total_token++;/*andsym*/}or {/*SLR*/printf("%s:或\n",yytext);mytoken[total_token]=orsym; rawstr[total_token]=yytext; total_token++;/*orsym*/}true {/*SLR*/printf("%s:真\n",yytext);mytoken[total_token]=truesym; rawstr[total_token]=yytext; total_token++;/*truesym*/}false {/*SLR*/printf("%s:假\n",yytext);mytoken[total_token]=falsesym; rawstr[total_token]=yytext; total_token++;/*falsesym*/}
    以下省略了匹配成功后相应的代码,基本只列出正则表达式,其中很多是直接匹配。
    begin { /*beginsym*/}call { /*callsym*/}const { /*constsym*/}do { /*dosym*/}end { /*endsym*/}if { /*ifsym*/}odd { /*oddsym*/}procedure { /*procsym*/}read { /*readsym*/}then { /*thensym*/}var { /*var*/}while { /*whilesym*/}write { /*writesym*/}[+] { /*plus*/}[-] { /*pminus*/}[*] { /*times*/}[/] { /*slash*/}[(] { /*lparen*/}[)] { /*rparen*/}[=] { /*eql*/}[,] { /*comma*/}[.] { /*period*/}[#] { /*neg*/}[;] { /*semicolon*/}[<>]|>=|<=|==|!= { /*relationop*/}[*|/] { /*乘法运算符*/}[0-9]* { /*number*/}:= { /*become*/}[0-9][0-9]* { /*无符号整数*/}^-?\d+$ { /*整数*/}[A-Za-z]|[A-Za-z][A-Za-z0-9]* { /*标识符ident*/}\n { /*printf("%s:换行符\n",yytext);*/ ++num_lines; ++num_chars;}[\t\r] { /*printf("%s:制表符\n",yytext);*//*过滤空白*/}[ ] { /*printf("%s:空白\n",yytext);*/} . { ++num_chars;}%%
    这样把所有合法符号判断其类型,映射到枚举类型Token中,Token中的类型如下:
    typedef enum{ /*Boolean expression SLR实验相关*/ notsym,andsym,orsym,truesym,falsesym, /*VN*/ beginsym,callsym,constsym,dosym,endsym,ifsym, oddsym,procsym,readsym,thensym,varsym,whilesym, writesym,pplus,pminus,times,slash,lparen,rparen, eql,comma,period,neg,semicolon, /*VT*/ ident,/*标识符*/ number,/*数字*/ block ,factor, expression, term,condition, relationop,/*关系运算符号*/ become/*赋值符号 :=*/}Token;
    这里类型的命名尽量和书上保持一直,体现规范,从名称不难得知其含义。
    4.2 语法分析要做语法分析需要先保存词法分析的结果,正如上文所说,rawstr字符串数组保存原始字符串,mytoken字符串数组保存识别后的类型,在做语法分析之前,首先得画文法的项目集,我分析的是布尔表达式文法。
    布尔表达式文法如下:
    [1]E -> E or E[2]E -> E and E[3]E -> not E[4]E -> ( E )[5]E -> id rop id[6]E -> true[7]E -> false
    其中:E-布尔表达式,rop-关系运算符
    这里为书写方便 or简写为| ,and 写为 & ,not写为!,rop简写为p ,true和false分别写为1和0。
    E -> E | EE -> E & EE -> ! EE -> ( E )E -> id p idE -> 1E -> 0
    分析之后,一共有I0到I15这16个项目集,项目集结果如下:
    I0 项目集 E' -> .E E -> .E | E E -> .E & E E -> .! E E -> .( E ) E -> .id p id E -> .1 E -> .0 达到状态I0 以后遇到E,转向状态I1以此类推E,1;!,2;(,3;id,4;1,5;0,6I1 项目集 E' -> E . E -> E .| E E -> E .& E 达到状态I1 以后遇到|,转向状态I7以此类推|,7;&,8I2 项目集 E -> ! .E E -> .E | E E -> .E & E E -> .! E E -> .( E ) E -> .id p id E -> .1 E -> .0 达到状态I2 以后遇到E,转向状态I9以此类推E,9 ;!,2 ;(,3 ;id,4 ;1,5 ; 0,6I3 项目集 E -> ( .E ) E -> .E | E E -> .E & E E -> .! E E -> .( E ) E -> .id p id E -> .1 E -> .0 达到状态I3 以后遇到E,转向状态I9以此类推E,10 ;!,2 ;(,3 ;id,4 ;1,5 ;0,6I4 项目集 E -> id .p id 达到状态I4 以后遇到p,转向状态I11以此类推p,11I5 项目集 E -> 1 . I6 项目集 E -> 0 . I7 项目集 E -> E | .E E -> .E | E E -> .E & E E -> .! E E -> .( E ) E -> .id p id E -> .1 E -> .0 达到状态I7 以后遇到E,转向状态I12以此类推E,12 ;!,2 ;(,3 ;id,4 ;1,5 ;0,6I8 项目集 E -> E & .E E -> .E | E E -> .E & E E -> .! E E -> .( E ) E -> .id p id E -> .1 E -> .0 达到状态I8 以后遇到E,转向状态I13以此类推E,13 ;!,2 ;(,3 ;id,4 ;1,5 ;0,6I9 项目集 E -> ! E . E -> E .| E E -> E .& E 达到状态I9 以后遇到|,转向状态I7以此类推|,7&,8I10 项目集 E -> ( E .) E -> E .| E E -> E .& E 达到状态I10 以后遇到),转向状态I14以此类推),14 ;|,7 ;&,8I11 项目集 E -> id p .id 达到状态I11 以后遇到id,转向状态I15以此类推id,15I12 项目集 E -> E | E . E -> E .| E E -> E .& E 达到状态I12 以后遇到E ,转向状态I7以此类推|,7 ;&,8I13 项目集 E -> E & E . E -> E .| E E -> E .& E 达到状态I13 以后遇到|,转向状态I7以此类推|,7 ;&,8I14 项目集 E -> ( E ) . I15 项目集 E -> id p id .
    在根据以上的项目集设计SLR(1)语法分析表如下:

    在实现的过程中,我发现自己对LR分析的理解还不够深入,通过阅读经典的龙书,学习其上对算法的描述,得到了启示。
    本次用到的布尔表达式文法:
    [1]E->E or E[2]E-> E and E[3]E->not E[4]E->(E)[5]E->id rop id[6]E->true[7]E->false
    龙书《Compiler》P251第四章语法分析对LR文法的算法描述:

    龙书中描述的LR分析的算法:
    let a be the first symbol of w$; while(l) { / * repeat forever * / let s be the state on top of the stack; if ( ACTION[S, a ] = shift t ) { push t onto the stack; let a be the next input symbol; } else if ( ACTION[S, a ] = reduce A -> beta) { pop beta symbols off the stack; let state t now be on top of the stack; push GOTO [t, A] onto the stack; output the production A -> beta; } else if ( ACTION[S, a ] = accept ) break; / * parsing is done * / else call error-recovery routine; }
    读懂了龙书中LR分析的算法描述, 我的SLR(1)分析的总控程序就好写了。
    我的语法分析的核心函数是SLR_parser,这个函数也是围绕一个while(1)的循环展开,在这个循环中,每次读入一个识别好的符号,然后就去查表,这里我把Action表和Goto表都放在同一个二维数组里了,这个Action-Goto表左部是ACTION表,右部是GOTO表。
    int ACTION_GOTO[16][11]={/*左部是ACTION表,右部是GOTO表*/ 2 ,0 ,0 ,3 ,0 ,0 ,5 ,6 ,4 ,0 , 1,//0 0 ,8 ,0 ,0 ,0 ,7 ,0 ,0 ,0 ,888 , 0,//1 888 ACC 2 ,0 ,0 ,3 ,0 ,0 ,5 ,6 ,4 ,0 , 9,//2 2 ,0 ,0 ,3 ,0 ,0 ,5 ,6 ,4 ,0 , 10,//3 0 ,0 ,0 ,0 ,11,0 ,0 ,0 ,0 ,0 , 0,//4 0 ,-6,-6,0 ,0 ,-6,0 ,0 ,0 ,-6, 0,//5 0 ,-7,-7,0 ,0 ,-7,0 ,0 ,0 ,-7, 0,//6 2 ,0 ,0 ,3 ,0 ,0 ,5 ,6 ,4 ,0 , 12,//7 2 ,0 ,0 ,3 ,0 ,0 ,5 ,6 ,4 ,0 , 13,//8 0 ,-3,-3,0 ,0 ,-3,0 ,0 ,0 ,-3, 0,//9 0 ,8 ,14,0 ,0 ,7 ,0 ,0 ,0 ,0 , 0,//10 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,15,0 , 0,//11 0 ,-1,-1,0 ,0 ,-1,0 ,0 ,0 ,-1, 0,//12 0 ,-2,-2,0 ,0 ,-2,0 ,0 ,0 ,-2, 0,//13 0 ,-4,-4,0 ,0 ,-4,0 ,0 ,0 ,-4, 0,//14 0 ,-5,-5,0 ,0 ,-5,0 ,0 ,0 ,-5, 0 //15 };
    在查表的时候,如果表中的相应值是正数,就移进;若是负数,就归约。接受的情况我设了一个比较特殊的数字888,由于状态有限,所以也不会发生冲突,遇到表中是888就接受该字符串,认为其符合文法,跳出循环,恭喜成功。我将表中其他空白处,也就是不合文法的那些地方都填0,所以若是查表遇到了0那可以认为不合文法,跳出循环,报告失败。

    4.3 语义分析我的语法分析核心由SLR_parser实现,语义分析及中间代码生成在归约的时候进行,因此我在上步的SLR_parser函数的基础上继续扩充,使之在归约的过程中能够分析语义和生成四元式,回填和合并真假链 。我的语义分析就是把上步的SLR_parser函数的归约部分进一步展开。
    我结合龙书和课本,把道理看明白,然后采用的是实现课本上的这7个函数,这7个函数分别对应7个产生式,在用相应产生式归约的时候,调用这些函数,生成相应中间代码,完成回填和合并。

    课本《编译原理 第二版(张素琴 吕映芝)》上P185的翻译模式:

    我设了一个语义栈,里面的元素不是string类型,而是自定义的结构体类型,其中是string类型的是这个结构体其中一项,作为它的语义值,这个结构体里还有codebegin记录中间代码的位置,E_true记录若真将转向何方,E_false记录若假将转向何方。按照以上的翻译模式完成相应的函数,在每次归约的时候看用的哪条产生式就调用相应的函数,在这个过程中也完成了真假链的回填和合并。
    伪代码算法描述:
    rule1: E -> E or E [rule1] backpatch(E1.false,E2.codebegin); E.codebegin := E1.codebegin; E.true := merge(E1.true,E2.true) E.false := E2.false rule2: E -> E and E [rule2] backpatch(E1.true,E2.codebegin); E.codebegin := E1.codebegin; E.true := E2.true; E.false := merge(E1.false,E2.false); rule3: E -> not E [rule3] E.true := E1.false; E.codebegin := E1.codebegin; E.false = E1.true; rule4: E -> ( E ) [rule4] E.true := E1.true; E.codebegin := E1.codebegin; E.false = E1.true; rule5: E -> id rop id [rule5] E.true := nextstat; E.codebegin := nextstat; E.false := nextstat+1; emit('if' id1.place 'rop' id2.place 'goto'-); emit('goto'-) rule6:E -> true [rule6] E.true := nextstat; E.codebegin:=nextstat; emit('goto'-) rule7:E -> false [rule7] E.false := nextstat; E.codebegin:=nextstat; emit('goto'-)
    Emit函数每调用一次,nextstat就加一,因为中间代码又多了一行,为了看起来比较清晰,书上默认设nextstat初值从100开始,我也采用这个初值。

    // 回填void Backpatch(int &p,int &t){//gen_pcode[Eid1.E_true]+=Int_to_String(Eid2.codebegin); gen_pcode[p][3]= Int_to_String(t);/*第四区段*/}// 合并int Merge(int &p1,int &p2){ gen_pcode[p2][3]=Int_to_String(p1);/*第四区段*/ return p1;/*返回链首*/}
    五、主要功能的实现5.1 词法分析部分这部分有很多是直接匹配,剩下的是写正则表达式去匹配,代码比较容易,象征性地列出。



    比如标识符 可以用正则表达式r=letter|(letter|digit)* 来表示,落实到代码可以表示为[A-Za-z]|[A-Za-z][A-Za-z0-9]*,这里要注意的是这里识别的顺序还有讲究,这里有先匹配谁的问题。最好要把直接匹配的代码放在上面,如果把标识符匹配的代码放在上面,可能会把关键字像begin和end之类也给识别为标识符。
    5.2 语法分析部分由第一部分的词法分析rawstr字符串数组保存原始字符串,mytoken字符串数组保存识别后的类型。语法分析部分我的核心函数在SLR_parser, 由于在后来的语义分析中,我对这个函数进行了很大扩充,使之具有生成中间代码和真假链回填合并的功能,这个函数变得比较复杂,为方便读者理解,我给出早期在做语法分析部分的SLR_parser函数, 这里称它为SLR_demo, 代码比较简短,是按照龙书上的算法描述进行相应代码实现的。
    SLR_demo函数如下:
    int SLR_demo(){ /*A complete simple demo for SLR Analysis,This is GOOD CODE.*//*This demo just judge whether a sentence is a boolean expression or not,you can change string str to test *Demo result:( ( id rop id ) or ( true and false ) ) # Success! (is boolean expression) */ /*int GOTO[16] = { 1,0,9,10,0,0,0,12,13,0,0,0,0,0,0,0};*/ string str[14]={"(","(","id","rop","id",")","or","(","true","and","false",")",")","#"}; for(int i=0;i<14;i++) cout<<str[i]<<" "; int ip=0,j=0;/*ip指示指到哪了*/ string sym;/*存符号*/ int state_row;/*状态数*/ int sym_col;/*符号数*/ int top; int Reduce_j;/*这是用来存归约Rj的下标j的*/ int Reduce_j_reducenum;/*由Rj知道用到是第几条产生式,这个产生式右部有几个符号(待归约的总数)*/ string nt[7]={"E","E","E","E","E","E","E"};/*布尔表达式文法7个产生式的左部都是E*/ int ACTION_GOTO[16][11]={/*左部是ACTION表,右部是GOTO表*/ 2 ,0 ,0 ,3 ,0 ,0 ,5 ,6 ,4 ,0 , 1,//0 0 ,8 ,0 ,0 ,0 ,7 ,0 ,0 ,0 ,888 , 0,//1 888 ACC 2 ,0 ,0 ,3 ,0 ,0 ,5 ,6 ,4 ,0 , 9,//2 2 ,0 ,0 ,3 ,0 ,0 ,5 ,6 ,4 ,0 , 10,//3 0 ,0 ,0 ,0 ,11,0 ,0 ,0 ,0 ,0 , 0,//4 0 ,-6,-6,0 ,0 ,-6,0 ,0 ,0 ,-6, 0,//5 0 ,-7,-7,0 ,0 ,-7,0 ,0 ,0 ,-7, 0,//6 2 ,0 ,0 ,3 ,0 ,0 ,5 ,6 ,4 ,0 , 12,//7 2 ,0 ,0 ,3 ,0 ,0 ,5 ,6 ,4 ,0 , 13,//8 0 ,-3,-3,0 ,0 ,-3,0 ,0 ,0 ,-3, 0,//9 0 ,8 ,14,0 ,0 ,7 ,0 ,0 ,0 ,0 , 0,//10 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,15,0 , 0,//11 0 ,-1,-1,0 ,0 ,-1,0 ,0 ,0 ,-1, 0,//12 0 ,-2,-2,0 ,0 ,-2,0 ,0 ,0 ,-2, 0,//13 0 ,-4,-4,0 ,0 ,-4,0 ,0 ,0 ,-4, 0,//14 0 ,-5,-5,0 ,0 ,-5,0 ,0 ,0 ,-5, 0 //15 }; stack<int> statestack;/*状态栈*/ statestack.push(0);/*开始是0状态*/ ip=0; while(1){ sym=str[ip];/*获得符号*/ sym_col=getsymcol(sym);/*获得符号数*/ state_row=statestack.top();/*得到栈顶当前状态*/ if(ACTION_GOTO[state_row][sym_col]>0){/*Shift 移进*/ if(ACTION_GOTO[state_row][sym_col]!=ACC){/*接受ACC在表中是888*/ statestack.push(ACTION_GOTO[state_row][sym_col]); }else{ cout<<" Success! (is boolean expression) \n"; break; } ip++;/*往下挪*/ }else if(ACTION_GOTO[state_row][sym_col]<0){/*Reduce 归约*/ Reduce_j=-ACTION_GOTO[state_row][sym_col]; Reduce_j_reducenum=reducenum(Reduce_j); for(j=0;j< Reduce_j_reducenum;j++){ statestack.pop(); } statestack.push(ACTION_GOTO[statestack.top()][getsymcol(nt[Reduce_j-1])]); }else{ cout<<" Fail! (not a boolean expression) \n"; break; } } return 0;}
    这个函数完成的功能画成下图:

    5.3 语义分析部分我的语义分析是对SLR_parser函数语法分析归约过程的扩展,语义栈中的元素是自定义结构体E的类型,因为是属性文法,需要这个结构体来存相关属性。
    typedef struct E{ int codebegin; int E_true; int E_false; string x;}E;
    其中codebegin存中间代码在第多少行,E_true表示若真,转向第E_true(其数值)行;E_false表示若假,转向第E_false行。
    扩展之后的SLR_parser函数如下:
    int SLR_parser(){/*这里使用plolex.cpp里定义的全局字符串数组globalstr*/ E tempE; int ip=0,j=0;/*ip指示指到哪了*/ string sym;/*存符号*/ int state_row;/*状态数*/ int sym_col;/*符号数*/ int top; int Reduce_j;/*这是用来存归约Rj的下标j的*/ int Reduce_j_reducenum;/*由Rj知道用到是第几条产生式,这个产生式右部有几个符号(待归约的总数)*/ string nt[7]={"E","E","E","E","E","E","E"};/*布尔表达式文法7个产生式的左部都是E*/ int ACTION_GOTO[16][11]={/*左部是ACTION表,右部是GOTO表*//*此处版面限制,省略ACTION_GOTO表,前面已给出*/ }; stack<int> statestack;/*状态栈*/ stack<string> symbolstack;/*符号栈*/ stack<E> semanticstack;/*语义栈*/ statestack.push(0);/*开始是0状态*/ ip=0; symbolstack.push("#"); E _E;_E.x="-"; semanticstack.push(_E); stack <E> Estack; while(1){ sym=globalstr[ip];/*这里从另一个文件pl0lex.cpp里定义的全局字符串数组获得符号*/ sym_col=getsymcol(sym);/*获得符号数*/ state_row=statestack.top();/*得到栈顶当前状态*/ showstack(statestack); showSTRstack(symbolstack); showSemanticstack(semanticstack); showsym(ip); cout<<endl; if(ACTION_GOTO[state_row][sym_col]>0){/*Shift 移进*/ if(ACTION_GOTO[state_row][sym_col]!=ACC){/*接受ACC在表中是888*/statestack.push(ACTION_GOTO[state_row][sym_col]); //cout<<"sym_col:"<<getstrsymcol(sym_col); symbolstack.push(getstrsymcol(sym_col)); E __temp;__temp.x=rawstr[ip]; semanticstack.push(__temp); }else{ cout<<" \nSuccess! (is boolean expression)该语句符合布尔表达式文法 \n"; break; } ip++;/*往下挪*/ }else if(ACTION_GOTO[state_row][sym_col]<0){/*Reduce 归约*/ Reduce_j=-ACTION_GOTO[state_row][sym_col]; Reduce_j_reducenum=reducenum(Reduce_j); cout<<"\n归约所用产生式:["<<Reduce_j<<"]",showBoolean_expression_rules(Reduce_j); for(j=0;j< Reduce_j_reducenum;j++){ statestack.pop(); symbolstack.pop(); } E newtempE;newtempE.x="x";if(Reduce_j==1||Reduce_j==2||Reduce_j==4||Reduce_j==5){ if(Reduce_j!=4){ E tempE2=semanticstack.top(); semanticstack.pop();//id2 E ropE=semanticstack.top(); semanticstack.pop();//rop E tempE1=semanticstack.top(); semanticstack.pop();//id1 if(Reduce_j==5){ /*rule5: E -> id rop id */ newtempE.E_true=nextstat; newtempE.codebegin=nextstat; newtempE.E_false=nextstat+1; gen_pcode[nextstat][0]=ropE.x; gen_pcode[nextstat][1]=tempE1.x; gen_pcode[nextstat][2]=tempE2.x; gen_pcode[nextstat][3]="true"; nextstat++; gen_pcode[nextstat][0]="j";gen_pcode[nextstat][1]="-";gen_pcode[nextstat][2]="-"; gen_pcode[nextstat][3]="false"; nextstat++; }else if(Reduce_j==2){ /*rule2: E -> E and E*/ Backpatch(tempE1.E_true,tempE2.codebegin); newtempE.codebegin=tempE1.codebegin; newtempE.E_true=tempE2.E_true; newtempE.E_false=Merge(tempE1.E_false,tempE2.E_false); }else if(Reduce_j==1){ /*rule1: E -> E or E*/ Backpatch(tempE1.E_false,tempE2.codebegin); newtempE.codebegin=tempE1.codebegin; newtempE.E_true=Merge(tempE1.E_true,tempE2.E_true); newtempE.E_false=tempE2.E_false; } Estack.push(tempE1); Estack.push(tempE2); }else{ /*rule4: E -> ( E )*/ semanticstack.pop();//括号 E E_1=semanticstack.top(); newtempE.E_true = E_1.E_true; newtempE.codebegin=E_1.codebegin; newtempE.E_false=E_1.E_false; Estack.push(tempE); semanticstack.pop(); semanticstack.pop();//括号 } }else if(Reduce_j==3){ /*rule3: E -> not E*/ E n_E=semanticstack.top(); newtempE.E_true=n_E.E_false; newtempE.codebegin=n_E.codebegin; newtempE.E_false=n_E.E_true; Estack.push(n_E); semanticstack.pop();//E semanticstack.pop();//not }else if(Reduce_j==6||Reduce_j==7){ if(Reduce_j==6){ /*rule6:E -> true*/ E t_E=semanticstack.top(); newtempE.E_true=nextstat; newtempE.codebegin=nextstat; Estack.push(t_E); gen_pcode[nextstat][0]="j";gen_pcode[nextstat][1]="-";gen_pcode[nextstat][2]="-"; gen_pcode[nextstat][3]="true"; nextstat++; }else if(Reduce_j==7){ /*rule7:E -> false*/ E f_E=semanticstack.top(); newtempE.E_false=nextstat; newtempE.codebegin=nextstat; Estack.push(f_E); gen_pcode[nextstat][0]="j";gen_pcode[nextstat][1]="-";gen_pcode[nextstat][2]="-"; gen_pcode[nextstat][3]="false"; nextstat++; } semanticstack.pop(); } semanticstack.push(newtempE); statestack.push(ACTION_GOTO[statestack.top()][getsymcol(nt[Reduce_j-1])]); symbolstack.push(getstrsymcol(getsymcol(nt[Reduce_j-1]))); }else{ cout<<" \nFail! (not a boolean expression) 该语句不符合布尔表达式文法 \n"; break; } } showSTRstack(symbolstack); return 0;}

    六、主要功能的测试6.1 词法分析部分语法测试用例及结果分析
    这里第一次实验时做的是针对整个PL/0语言的语法分析,到课设整合的时候面向的对象是布尔表达式,这里给出当时做完语法分析时的测试用例及结果分析。
    本学期为做此次课程设计一共做了4次实验,其中第二次是递归下降分析,比较独立;本课程设计综合了本学期第一次实验的词法分析,第三次实验的语法分析,第四次的语义分析及四元式生成,本次课程设计最终实现完成的功能是针对布尔表达式产生四元式中间代码。
    PL/0词法分析
    以下为第一次实验语法分析时的测试用例:
    词法分析测试用PL/0源程序hello.pl0:
    VAR x, y;VAR aa,bb;BEGIN x := 1/1; WHILE x <= 100 DO BEGIN CALL fun; ! aa; x := x + 1 ENDENDPROCEDURE fun; VAR a,b,c; BEGIN a:=1; b:=2; c:=3; aa:=a+b; END;BEGINCALL fun;END
    词法分析结果result.txt:
    我的词法分析器正在分析程序hello.pl0…
    VAR:变量x:标识符,:和y:标识符;:。VAR:变量aa:标识符,:和bb:标识符;:。BEGIN:复合语句开始x:标识符:=:赋值1:无符号整数/:乘法运算符1:无符号整数;:。WHILE:当型循环当x:标识符<=:关系运算符100:无符号整数DO:当型循环做BEGIN:复合语句开始CALL:过程调用fun:标识符;:。!aa:标识符;:。x:标识符:=:赋值x:标识符+:加法运算符1:无符号整数END:复合语句结束END:复合语句结束PROCEDURE:过程首部fun:标识符;:。VAR:变量a:标识符,:和b:标识符,:和c:标识符;:。BEGIN:复合语句开始a:标识符:=:赋值1:无符号整数;:。b:标识符:=:赋值2:无符号整数;:。c:标识符:=:赋值3:无符号整数;:。aa:标识符:=:赋值a:标识符+:加法运算符b:标识符;:。END:复合语句结束;:。BEGIN:复合语句开始CALL:过程调用fun:标识符;:。END:复合语句结束.:点!程序结束
    词法分析运行截图:


    6.2 语法分析部分第3次实验完成了语法分析,对于符合布尔表达式文法的列出每次归约所用产生式,篇幅有限,为方便观察,暂不列出每次栈的变化。关于栈的变化在整合后的课程设计里是详细给出的。
    运行截图:


    测试用例里有test1.pl0 test2.pl0 test3.pl0 test4.pl0运行结果分别为result1.txt result2.txt result3.txt result4.txt
    读者可以利用管道重定向得到。
    测试用例1 test1.pl0内容:
    ( ( x > y ) or ( true and false) )#
    测试用例1分析结果result1.txt:
    SLR_boolean_expression_parser 待分析语句如下:
    ( ( x > y ) or ( true and false ) )#
    以上为待分析语句
    flex pl0lex.cppg++ -o mypl0lex lex.yy.c -ll我的词法分析器正在分析程序test.pl0..../mypl0lex < test.pl0准备打开文件打开文件成功!,正在词法分析(:左括号(:左括号x:标识符>:关系运算符y:标识符):右括号or:或(:左括号true:真and:与false:假):右括号):右括号#:井号词法分析完成!共有14个符号( ( id rop id ) or ( true and false ) ) # 归约所用产生式:[5]E -> id rop id归约所用产生式:[4]E -> ( E )归约所用产生式:[6]E -> true归约所用产生式:[7]E -> flase归约所用产生式:[2]E -> E and E归约所用产生式:[4]E -> ( E )归约所用产生式:[1]E -> E or E归约所用产生式:[4]E -> ( E ) Success! (is boolean expression)该语句符合布尔表达式文法
    测试用例2 test2.pl0内容:
    ( ( x > y ) or ( true andfalse #
    测试用例2分析结果result2.txt:
    SLR_boolean_expression_parser 待分析语句如下:
    ( ( x > y ) or ( true and false #
    以上为待分析语句
    flex pl0lex.cppg++ -o mypl0lex lex.yy.c -ll我的词法分析器正在分析程序test.pl0..../mypl0lex < test.pl0准备打开文件打开文件成功!,正在词法分析(:左括号(:左括号x:标识符>:关系运算符y:标识符):右括号or:或(:左括号true:真and:与false:假#:井号词法分析完成!共有12个符号( ( id rop id ) or ( true and false # 归约所用产生式:[5]E -> id rop id归约所用产生式:[4]E -> ( E )归约所用产生式:[6]E -> true归约所用产生式:[7]E -> flase归约所用产生式:[2]E -> E and E Fail! (not a boolean expression) 该语句不符合布尔表达式文法
    测试用例3 test3.pl0内容:
    ( ( ( ( true or false ) and ( m > n) ) or (not true) ) and ( x > y) ) #
    测试用例3分析结果result3.txt:
    SLR_boolean_expression_parser 待分析语句如下:
    ( ( ( ( true or false ) and ( m > n) ) or ( not true) ) and ( x > y) ) #
    以上为待分析语句
    flex pl0lex.cppg++ -o mypl0lex lex.yy.c -ll我的词法分析器正在分析程序test.pl0..../mypl0lex < test.pl0准备打开文件打开文件成功!,正在词法分析(:左括号(:左括号(:左括号(:左括号true:真or:或false:假):右括号and:与(:左括号m:标识符>:关系运算符n:标识符):右括号):右括号or:或(:左括号not:非true:真):右括号):右括号and:与(:左括号x:标识符>:关系运算符y:标识符):右括号):右括号#:井号词法分析完成!共有29个符号( ( ( ( true or false ) and ( id rop id ) ) or ( not true ) ) and ( id rop id ) ) # 归约所用产生式:[6]E -> true归约所用产生式:[7]E -> flase归约所用产生式:[1]E -> E or E归约所用产生式:[4]E -> ( E )归约所用产生式:[5]E -> id rop id归约所用产生式:[4]E -> ( E )归约所用产生式:[2]E -> E and E归约所用产生式:[4]E -> ( E )归约所用产生式:[6]E -> true归约所用产生式:[3]E -> not E归约所用产生式:[4]E -> ( E )归约所用产生式:[1]E -> E or E归约所用产生式:[4]E -> ( E )归约所用产生式:[5]E -> id rop id归约所用产生式:[4]E -> ( E )归约所用产生式:[2]E -> E and E归约所用产生式:[4]E -> ( E ) Success! (is boolean expression)该语句符合布尔表达式文法
    测试用例4 test4.pl0内容:
    ( ( ( ( true or false ) and ( m > n) ) or (not true) and ( x > y) #
    测试用例4分析结果result4.txt:
    SLR_boolean_expression_parser 待分析语句如下:
    ( ( ( ( true or false ) and ( m > n) ) or ( not true) and ( x > y) #
    以上为待分析语句
    flex pl0lex.cppg++ -o mypl0lex lex.yy.c -ll我的词法分析器正在分析程序test.pl0..../mypl0lex < test.pl0准备打开文件打开文件成功!,正在词法分析(:左括号(:左括号(:左括号(:左括号true:真or:或false:假):右括号and:与(:左括号m:标识符>:关系运算符n:标识符):右括号):右括号or:或(:左括号not:非true:真):右括号and:与(:左括号x:标识符>:关系运算符y:标识符):右括号#:井号词法分析完成!共有27个符号( ( ( ( true or false ) and ( id rop id ) ) or ( not true ) and ( id rop id ) # 归约所用产生式:[6]E -> true归约所用产生式:[7]E -> flase归约所用产生式:[1]E -> E or E归约所用产生式:[4]E -> ( E )归约所用产生式:[5]E -> id rop id归约所用产生式:[4]E -> ( E )归约所用产生式:[2]E -> E and E归约所用产生式:[4]E -> ( E )归约所用产生式:[6]E -> true归约所用产生式:[3]E -> not E归约所用产生式:[4]E -> ( E )归约所用产生式:[1]E -> E or E归约所用产生式:[5]E -> id rop id归约所用产生式:[4]E -> ( E )归约所用产生式:[2]E -> E and E Fail! (not a boolean expression) 该语句不符合布尔表达式文法

    6.3 语义分析及中间代码生成部分详细给出状态栈和符号栈的变化过程,归约过程可以看得很清楚。从test.pl0读入布尔表达式(以#结尾),在终端敲make运行时在界面上显示分析和产生四元式的过程,同时把产生的四元式写入文本文件pcode.txt。
    测试用例:
    测试用的布尔表达式源文件(布尔表达式以#结尾):test.pl0
    ((( a > b ) and ( c < d ) )or ( e < f )) #
    我的编译程序通过分析test.pl0产生的四元式中间代码pcode.txt:
    [100] {> ,a ,b ,102 }[101] {j ,- ,- ,104 }[102] {< ,c ,d ,true }[103] {j ,- ,- ,101 }[104] {< ,e ,f ,102 }[105] {j ,- ,- ,false }







    这是一个不断移进和归约的过程,状态栈、符号栈和语义栈的变化过程都已详细给出,在控制台也输出了最后真假链接回填和合并后的四元式,同时程序还把生成的四元式写入文本pcode.txt,如下图:

    七、问题讨论以及感想本学期分了几次做实验,最后实验整合成课程设计。开始先是词法分析,然后语法分析,最后语义分析。做语法分析时我按照龙书的算法描述,写出相应实现代码,正确地完成了当时要求的语法分析功能,然而语法分析时我没有考虑语义值,只是对识别结果进行分析,比如原来输入的是x, 我词法分析已经识别其为标识符,那语法分析时我并不需要x这个原来的值,只要知道其类型是标识符即可。这对于光完成语法分析是没毛病的,但做后来的语义分析的时候就造成了麻烦,只知道其为标识符,却得不到它原来是x 。我知道问题在哪,就在它基础上改, 不过还是挺麻烦的,刚开始我的语义栈的类型不是一个结构体,而是字符串类型,那么对于属性文法,像E.codebegin 和E.false这种就没地方存。开始耽搁了很长时间,最后把语义栈改成结构体类型,string只作为这个结构体类型的其中一项,很快就解决了。这样我终于理解属性文法中“属性”二字的含义。
    理解算法是很重要的。理解了的时候,实现只是时间问题,胸有成竹;不理解时代码就无从下手,或是胡乱写了一通代码越写越乱,找不出bug。好好研读书上的算法,能够把思路捋清,节省时间。龙书和课本上的算法给我很大帮助,精炼的算法描述在没有细细研究和动手写代码之前很难一下读懂,等弄懂了,代码写好之后再去看就发现它思路无比清晰,结合代码看算法,对问题的理解也就更深入了。
    本次课程设计锻炼了我的动手能力,“纸上得来终觉浅,绝知此事要躬行”,通过编写代码,我更加深刻地理解了编译原理课本上的知识。
    1 评论 6 下载 2018-11-04 16:47:10 下载需要4点积分
  • 8种排序算法的比较案例

    一、使用说明1.1 操作手册
    运行程序后,进入欢迎界面
    输入排序的规模
    选择排序方法


    1.1.1 冒泡排序输入1冒泡排序,例如:

    程序将给出排序的时间和交换的次数。
    1.1.2 选择排序输入2进行选择排序,例如:

    程序将给出排序的时间和交换的次数。
    1.1.3 直接插入排序输入3进行直接插入排序,例如:

    程序将给出排序的时间和交换的次数。
    1.1.4 希尔排序输入4进行希尔排序,例如:

    程序将给出排序的时间和交换的次数。
    1.1.5 快速排序输入5进行快速排序,例如:

    程序将给出排序的时间和交换的次数。
    1.1.6 堆排序输入6进行堆排序,例如:

    程序将给出排序的时间和交换的次数。
    1.1.7 归并排序输入7进行归并排序,例如:

    程序将给出排序时间和比较次数。
    1.1.8 基数排序输入8进行基数排序,例如:

    二、概述2.1 程序设计目的本程序实现了冒泡、选择、直接插入、快排、希尔、堆、归并、基数八种基本排序方法,比较了它们的排序时间和比较次数。
    2.2 算法思路
    冒泡排序:重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成
    选择排序:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完
    直接插入排序:第一趟比较前两个数,然后把第二个数按大小插入到有序表中; 第二趟把第三个数据与前两个数从后向前扫描,把第三个数按大小插入到有序表中;依次进行下去,进行了(n-1)趟扫描以后就完成了整个排序过程
    快速排序:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列
    希尔排序:把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止
    堆排序:利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶
    归并排序:比较a[i]和a[j]的大小,若a[i]≤a[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令i和k分别加上1;否则将第二个有序表中 的元素a[j]复制到r[k]中,并令j和k分别加上1,如此循环下去,直到其中一个有序表取完,然后再将另一个有序表中剩余的元素复制到r中从下标k到 下标t的单元。归并排序的算法我们通常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区 间用一次归并操作合并成有序的区间[s,t]
    基数排序:透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用

    2.3 数据结构除了堆排序的数据结构为堆外,其他用的基本都是数组。
    三、函数接口3.1 冒泡排序


    成员函数名
    功能
    参数




    void bubble_sort(int arr[])
    进行冒泡排序
    需要排序的序列



    3.2 选择排序


    成员函数名
    功能
    参数




    void selection_sort(int arr[])
    进行选择排序
    需要排序的序列



    3.3 直接插入排序


    成员函数名
    功能
    参数




    void insertion_sort(int arr[])
    进行直接插入排序
    需要排序的序列



    3.4 希尔排序


    成员函数名
    功能
    参数




    void shell_sort(int arr[])
    进行希尔插入排序
    需要排序的序列



    3.5 快速排序


    成员函数名
    功能
    参数




    void quick_sort(int arr[])
    进行快速排序
    需要排序的序列


    void quick_sort_help(int arr[], cons tint left, cons tint right)
    辅助快速排序递归函数
    需要排序的序列;左值;右值



    3.6 堆排序


    成员函数名
    功能
    参数




    void heap_sort(int arr[])
    进行堆插入排序
    需要排序的序列


    void HeapAdjust(int arr[], int I, int nLength)
    堆维护函数
    需要排序的序列;节点;深度



    3.7 归并排序


    成员函数名
    功能
    参数




    void merge_sort(int arr[])
    进行堆归并排序
    需要排序的序列


    void merge_help1(int arr[], int temp[], int start, int end)
    归并辅助函数
    需要排序的序列;临时数组; 开始;结束


    void merge_help2(int arr[], int temp[], int start, int mid, int end)
    归并辅助函数
    需要排序的序列;临时数组;开始;中值;结束



    3.8 基数排序


    成员函数名
    功能
    参数




    void radix_sort(int arr[])
    进行基数排序
    需要排序的序列


    int getPlaces(int num)
    获取“桶子”
    存放的数


    int getMax(int arr[], int n)
    获取最大值
    需要排序的序列;个数


    void radix_help(int arr[], int n, int place)
    基数排序辅助函数
    需要排序的序列;个数;桶



    四、体会排序算法对于一个程序员来说是什么重要的,如何高效地进行排序,是一门学问。这八种基本的排序算法由简到难都凝聚着一代代人研究的心血,能去掌握它们令我非常开心。
    1 评论 21 下载 2018-11-04 16:33:30 下载需要6点积分
  • 基于ASP.NET的无纸化办公系统

    一、功能需求
    本系统支持新用户注册功能。注册信息包括用户名、密码。其中用户名要求:用户邮箱且必须唯一;密码要求:6-16个字符,为英文字母大小写、特殊字符的组合。注册后用户名不可更改,密码可修改(输入原密码与新密码,新密码格式要求同上)
    人员与职责管理。管理员自定义部门(1-10个中英文字符)和职位名称(1-10个中英文字符)。管理员可指定任一用户到任一职位。(管理员用户名“root@root.com”,初始密码密码默认为“Root123456.”)
    站内短信。可向公司内任意人员发送文字消息(一条1-256个字符)。
    任务提醒。编辑任务(1-256个字符)后选择其同一部门一名下级人员发布,被指定的下级人员得到任务信息。
    公文流转。编辑公文(1-256个字符)后选择其同一部门一名上级人员发送,被指定的上级人员得到公文,可选择(同意|不同意|指定同一部门的一名上级继续流转)。编辑该公文的下级可随时查看公文处理进度(同意|不同意|未处理)

    二、非功能需求
    在网络良好的状态下,用户发送站内短信后,必须在3秒钟之内送达指定用户
    在网络良好的状态下,用户发布任务后,必须在3秒钟之内送达指定下级
    在网络良好的状态下,用户提交公文后,必须在3秒钟之内送达指定上级
    本系统支持各大浏览器(IE, Microsoft Edge,Fire Fox,Chrome,Safari)

    三、功能模型
    四、数据(对象)模型
    五、系统分解
    六、模块(子系统)描述

    七、数据设计
    HumanResource表保存职员信息
    Message表保存站内短信内容及相关信息
    JobNotification表保存任务通知的内容及相关信息
    Ducument表保存公文内容及流转相关信息

    八、系统部署
    1 评论 8 下载 2018-11-04 15:50:16 下载需要6点积分
  • 基于JSP的网络硬盘

    1 可行性分析web开发技术是Internet应用的一个重要方而,而JSP又是web开发的最先进的技术,是当前web开发人员的首选技术。
    随着网络技术的日益普及和信息化建设的重视,网络硬盘是一种新型安全的网络存储系统,已越来越受到.人们的重视和喜欢,主要适用个人文件存储,可以用作个人的一个网络U盘,网络硬盘是一块专属的存储空间,用户通过上网登录网站的方式,可以方便上传、卜载文件。只要能上网就可以用网络硬盘登录到服务器上进行个人文件的上传、删除等操作,随时随地存储自己的个人文件。
    2 需求分析2.1 系统模块设计本系统做为一个较完整的网站,主要系统的主要模块如下:

    会员注册:新会员要登录该网站享有对自己的资源进行下载或者上传资料就必须的有自己的会员号码。会员在注册的时候需要填写会员名、登录密码、确认密码等信息。如果该会员已经存在与该系统的会员信息数据库中,则提示注册失败
    会员登录:会员根据自己注册的会员号码和密码登录该系统。首次登陆会在TOMCAT目录里建一个以自己用户名命名的目录
    修改密码:会员为了自己的信息的安全可以对自己的注册时候填写的密码进行修改
    上传文件:上传自己的文件,大小格式均有限制
    下载文件:下载你自己的文件

    2.2 数据库设计本系统使用MySQL数据库建立了一个userinfo数据库,表用来存储会员的信息,还有一个admin来存储管理员账号密码。
    2.2.1 userinfo表的字段会员的注册信息存入userinfo表中,userinfo的主键是id,标准的字段说明如下:

    id 会员注册时自动增长的识别码
    userName 会员用户名
    userPassword 会员密码

    2.2.2 userinfo的详细设计如图2-1所示
    2.2.3 admin表的字段管理员的注册信息存入admin表中,admin的主键是adminName,默认值为admin,键adminPassword默认值为admin888标准的字段说明如下:

    adminName 管理员账号
    adminPassword 管理员密码

    2.2.4 admin的详细设计如图2-2所示
    3 系统管理3.1 设计说明本设计使用的JSP引擎是Tomcat6.0,使用IDE为 MyEclipse8.0。
    连接数据库使用的建立连接桥来连接数据库,因此在设计系统之前在本地数据源新建mytest的系统数据源,方便实验Java连接数据库。
    3.2 页面管理本系统所有的JSP页面都保存在KO目录中。
    用户可以通过在浏览器的地址栏中输入 http://127.0.0.1:8080/KO/index.jsp 来访问该主页。
    3.3 JavaBean本系统使用的Javabean的包名均为com.其中,BaseConn.java为链接数据库JDBC,CheckAdmin.java为检查管理员,CheckLogin.java为检查普通用户。
    3.4 后台设计后台主要为注册用户的相关操作,比如增加,删除,修改,搜索等
    4 模型图
    5 会员注册要登录该网站就必须要有会员名,因此如果没有成为会员的用户必须注册会员。在填写注册信息的使用必须填写会员名,登录密码。
    5.1 模型(Javabean)描述注册信息的Javabean中要有会员的名称、密码、年龄、性别、邮箱、电话号码以及一个注册提示信息。
    在CheckLogin.java对这些信息进行描述。具体代码如下:
    package com;import java.sql.*;public class CheckLogin { /** * 检测用户登录信息 * @param String userName * 用户登录的用户名 * @param String userPassword * 用户登录的密码 * @return String * 返回一个字符串:如果用户名已经在数据库存在并且用户输入的密码也正确 返回字符串 SUCCESS_LOGIN * 如果用户名已经在数据库存在但是输入的密码不正确 返回字符串 WRONG_PASSWROD * 如果用户名不存在返回字符串 NONE_USER * */ public String checklogin(String userName,String userPassword) throws SQLException,ClassNotFoundException { BaseConn conn = null; try { conn = new BaseConn(); //创建一个用预处理的SQL语句 String sql = "select * from userinfo where userName=?"; //创建一个预处理SQL对象 PreparedStatement ps = conn.preparedStatement(sql); ps.setString(1,userName); //从数据库中查询该用户名是否在数据库存在 ResultSet rs = conn.executeQuery(); if(rs.next()) { if(rs.getString("userPassword").equals(userPassword)) { return "SUCCESS_LOGIN"; } else return "WRONG_PASSWORD"; } else return "NONE_USER"; }catch(SQLException ex) { ex.printStackTrace(); throw ex; }catch(ClassNotFoundException ex) { ex.printStackTrace(); throw ex; } finally { conn.closeDB(); //关闭数据库连接,释放JDBC资源 } } /** * 如果是新用户时,将用户登录用户名和密码保存到数据库中 * */ public boolean saveToDataBase(String userName,String userPassword) throws SQLException,ClassNotFoundException { BaseConn conn = null; try { conn = new BaseConn(); String sql = "insert into userinfo(userName,userPassword) values(?,?)"; PreparedStatement ps = conn.preparedStatement(sql); ps.setString(1,userName); ps.setString(2,userPassword); conn.executeUpdate(); return true; }catch(SQLException ex) { ex.printStackTrace(); throw ex; }catch(ClassNotFoundException ex) { ex.printStackTrace(); throw ex; }finally { conn.closeDB(); //关闭数据库连接,释放JDBC资源 } }}
    5.2 视图(JSP页面)本模块有两个视图,register.jsp与register_post.jsp.一个提供用户填写注册信息,一个提交用户提供注册信息,把会员填写的基本信息写入userinfo表。
    注册页面的效果图如下图所示:

    该页面提交给了register_post.jsp。
    register_post.jsp代码如下所示:
    <%@ page language="java" import="java.util.*" pageEncoding="gbk"%><%@page import="com.CheckLogin"%><jsp:useBean id="check" class="com.CheckLogin"/><html> <head> <title>注册吧^_^</title> <link rel="stylesheet" type="text/css" href="styles.css"> </head><style> <body><% request.setCharacterEncoding("gbk"); //获取用户昵称 String userName = request.getParameter("userName"); //获取用户密码 String userPassword=request.getParameter("userPassword"); //将获取到的用户登录信息与数据库中保存的用户信息进行比较 String loginMsg = check.checklogin(userName,userPassword); if(loginMsg.equals("SUCCESS_LOGIN")) { out.println("该用户已经存在,请重新选择用户名"); } else if(loginMsg.equals("WRONG_PASSWORD")) { out.println("该用户已经存在,请重新选择用户名"); } else if(loginMsg.equals("NONE_USER")) { boolean sf = check.saveToDataBase(userName,userPassword); if(sf) { out.println("注册成功"); %> <br> <a href="login.jsp">返回登录</a> <% } } else { out.println("该用户名已经存在,请选择另一个用户名注册"); } %> </body></html>
    6 会员登录用户在此页面输入自己的用户名、密码登录该网站,由于本设计只设计了会员管理系统,模块之中只有个人中心是有效模块。会员进行个人中心能进行相关操作。
    6.1 模型(Javabean)此意注册为同一Javabean,CheckLogin.java
    6.2 视图(JSP视图)本模块有两个视图:login.jsp提供给用户填写登录信息,checkLogin.jsp是在验证并提交用户填写的登录信息,在主页面提供给会员该网站的全部功能。
    登录页面的效果图如下图所示:

    其中,checkLogin.jsp代码如下:
    <%@ page language="java" import="java.util.*" pageEncoding="gbk"%><jsp:useBean id="check" class="com.CheckLogin"/><% request.setCharacterEncoding("GBK"); //获取用户昵称 String userName = request.getParameter("userName"); //获取用户密码 String userPassword=request.getParameter("userPassword"); //将获取到的用户登录信息与数据库中保存的用户信息进行比较 String loginMsg = check.checklogin(userName,userPassword); if(loginMsg.equals("SUCCESS_LOGIN")) { session.setAttribute("user",userName);//建立session对象以便以后对登陆作验证 response.sendRedirect("welcome.jsp"); } else if(loginMsg.equals("WRONG_PASSWORD")) { out.println("你输入的用户名或密码错误,请检正后重新输入"); } else if(loginMsg.equals("NONE_USER")) { out.println("用户名不存在!!!"); } %><html><<head><link rel="stylesheet" type="text/css" href="styles.css"></head><body><a href=login.jsp>返回</a></body></html>
    7 主页面设为主页面为welcome.jsp,登陆后的显示如下图所示:

    welcome.jsp具体代码如下:
    <%@ page language="java" contentType="text/html; charset=gbk" pageEncoding="GB18030" import="java.sql.*,java.util.*,java.io.*"%><%@include file="conn.jsp"%><%@include file="config.jsp"%><% //当用户登入此页时,到根目录box目录新建一个以此用户名为命名的目录 String Save_Location=getServletContext().getRealPath("/")+"box//"; try{ if (!(new java.io.File(Save_Location).isDirectory())) {//如果文件夹不存在 new java.io.File(Save_Location).mkdir(); //不存在 文件夹,则建立此文件夹 new java.io.File((Save_Location)+id+"//").mkdir(); //创建文件夹,命名为当前用户的名字 } else {//存在excel文件夹,则直接建立此文件夹 new java.io.File((Save_Location)+id+"//").mkdir(); //创建文件夹,命名为当前用户的名字 } }catch(Exception e){ e.printStackTrace(); //创建文件夹失败 out.print("error"); return; } File userBox=new File((Save_Location)+id+"//"); File userBoxfile[]=userBox.listFiles();%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <title>欢迎使用网络硬盘</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <link rel="stylesheet" type="text/css" href="styles.css"> </head><body> <br /> <table width="809" height="204" border="1" align="center"> <tr> <td height="26" align="left"> <font color="red">欢迎 <%=session.getAttribute("user")%>的到来!</font><font color="#0000ff">                                        </font>         <a href="update.jsp?userName=<%=session.getAttribute("user")%>">修改密码</a> <a href="logout.jsp">退出登录</a> <br> </td> </tr> <tr> <td height="58" align="left" valign="top"><a href="box_upload.jsp"><img src="images/box-upload.jpg" width="131" height="45"></a> <% for(int i=0;i<userBoxfile.length;i++){%> <tr><td height="22"> <p><font color="#FF00FF"><%=userBoxfile[i].getName()%></font></p></td><td height="25"> <span style="font-size: 9pt"> <a href="box_download.jsp?filename=<%=userBoxfile[i].getName()%>" target=_top> <font color="#0A9EE4">下载</font></a></span></td> <td height="25"><span style="font-size: 9pt"> <a href="box_del.jsp?action=confirm&filename=<%=userBoxfile[i].getName()%>" target=_top> <font color="#0A9EE4">删除</font></a></span> </td> </tr> <%} %> </td> </tr> <tr align="center"> <td height="26" align="center">  <font color="#c0c0c0"> 版本信息:重庆理工大学软件工程课程设计软件二班王兴龙</font></td> </tr> </table> </body></html>
    8 上传功能8.1 上传功能运用jspSmartUpload.jar组件,方便,灵活上传页面用box_upload_do.jsp,具体代码如下:
    <%@ page contentType="text/html; charset=gbk" language="java" import="java.util.*,com.jspsmart.upload.SmartUpload" errorPage=""%><%@ include file="conn.jsp"%><%@ include file="config.jsp"%><% // 新建一个SmartUpload对象 SmartUpload su = new SmartUpload(); // 上传初始化 su.initialize(pageContext); // 设定上传限制 // 1.限制每个上传文件的最大长度。 su.setMaxFileSize(5000000); // 2.限制总上传数据的长度。 su.setTotalMaxFileSize(150000000); // 3.设定允许上传的文件(通过扩展名限制)。 su.setAllowedFilesList("rar,zip,txt.mp3,jpg,gif,ppt,doc,xls,bmp,wav,mid,dat,ppt"); // 4.设定禁止上传的文件(通过扩展名限制),禁止上传带有exe,bat,jsp,htm,html扩展名的文件和没有扩展名的文件。 // su.setDeniedFilesList("exe,bat,jsp,htm,html,asp,php,com"); // 上传文件 su.upload(); // 将上传文件全部保存到指定目录 String cqutroot = dirPath.replace('\\', '/'); su.save(cqutroot + "box/" + id); out.print("<script>"); out.print("alert('文件上传成功!');"); out.print("location.href='welcome.jsp';"); out.print("</script>");%>
    上传页面为upload.jsp效果如下图所示:

    该页面为POST方式,提交到了box_upload_do.jsp,代码如下:
    <%@ page contentType="text/html; charset=gbk" language="java" import="java.util.*,com.jspsmart.upload.SmartUpload" errorPage=""%><%@ include file="conn.jsp"%><%@ include file="config.jsp"%><% // 新建一个SmartUpload对象 SmartUpload su = new SmartUpload(); // 上传初始化 su.initialize(pageContext); // 设定上传限制 // 1.限制每个上传文件的最大长度。 su.setMaxFileSize(5000000); // 2.限制总上传数据的长度。 su.setTotalMaxFileSize(150000000); // 3.设定允许上传的文件(通过扩展名限制)。 su.setAllowedFilesList("rar,zip"); // 4.设定禁止上传的文件(通过扩展名限制),禁止上传带有exe,bat,jsp,htm,html扩展名的文件和没有扩展名的文件。 su.setDeniedFilesList("exe,bat,jsp,htm,html,asp,php,com"); // 上传文件 su.upload(); // 将上传文件全部保存到指定目录 String cqutroot = dirPath.replace('\\', '/'); su.save(cqutroot + "box/" + id); out.print("<script>"); out.print("alert('文件上传成功!');"); out.print("location.href='welcome.jsp';"); out.print("</script>");%>
    9 下载文件9.1 jspSmartUpload.jar下载对中文支持不是很好,所以我用I/Obox_download.jsp,具体代码如下:
    <%@ page language="java" contentType="text/html; charset=gbk" import="com.jspsmart.upload.*,java.sql.*,java.io.*" %><%@ include file="config.jsp"%><%@ include file="conn.jsp"%><% java.io.BufferedInputStream bis=null; java.io.BufferedOutputStream bos=null;try{ String filename=request.getParameter("filename"); filename=new String(filename.getBytes("iso8859-1"),"gb2312"); response.setContentType("application/x-msdownload"); response.setHeader("Content-disposition","attachment; filename="+new String(filename.getBytes("gb2312"),"iso8859-1")); bis =new java.io.BufferedInputStream(new java.io.FileInputStream(config.getServletContext().getRealPath("box/"+id+"/" + filename))); bos=new java.io.BufferedOutputStream(response.getOutputStream()); byte[] buff = new byte[2048]; int bytesRead; while(-1 != (bytesRead = bis.read(buff, 0, buff.length))) { bos.write(buff,0,bytesRead); }}catch(Exception e){ e.printStackTrace();}finally { if (bis != null)bis.close(); if (bos != null)bos.close();}%>
    10 删除文件
    用户上传错误时需删除文件。
    box_del.jsp,具体代码如下
    <%@ page language="java" contentType="text/html; charset=gbk" pageEncoding="GB18030" import="java.sql.*,java.util.*,java.io.*"%><%@ include file="config.jsp"%><%@ include file="conn.jsp"%><% String cqutroot=dirPath.replace('\\','/'); //将dirPath的"\\"全替换为"/" try { //处理成中文字符 String filename=codeToString(request.getParameter("filename")); String action=codeToString(request.getParameter("action"));if(filename==null){ out.print("<script>"); out.print("alert('filename错误!');"); out.print("location.href='welcome.jsp';"); out.print("</script>");}if(action==null){ out.print("<script>"); out.print("alert('action错误!');"); out.print("location.href='box.jsp';"); out.print("</script>");}action=request.getParameter("action");if(action.equals("del")) { File delfile=new File(cqutroot+"box/"+id+"/"+filename); delfile.delete(); out.print("<script>"); out.print("alert('删除成功!');"); out.print("location.href='welcome.jsp';"); out.print("</script>"); }if(action.equals("confirm")) {%><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>确认删除文件?</title><link rel="stylesheet" type="text/css" href="styles.css"></head><body><table width="646" height="345" border="1" align="center"> <tr> <td><p align="center"><font color="#ff0000"><strong>确认删除<%=filename%>?</strong></font></p></td> </tr> <tr> <td><div align="center"> <input type="button" value="提交" name="B1" onClick="javascript:location.href='box_del.jsp?action=del&filename=<%=filename%>';">    <input type="button" value="返回" name="B2" onClick="javascript:location.href='welcome.jsp';"> </div></td> </tr></table></body></html><%}else{ out.print("<script>"); out.print("location.href='welcome.jsp';"); out.print("</script>");}}catch(Exception e){ out.print("<script>"); out.print("location.href='welcome.jsp';"); out.print("</script>");}%>
    11 后台系统后台我做了这几个页面:管理员登陆:admin_login.jsp;检查管理员是否正确:admin_check.jsp;后台首页:admin.jsp;用户删除:user_del.jsp;用户密码修改:user_edit.jsp;修改保存:user_save.jsp.搜索功能:search.jsp
    12 验证是否已经真正登陆为了防止不通过验证就登陆到操作界面,做出验证页面,主要是检查session是否存在。
    其中,conn.jsp是普通用户,admin_conn.jsp是管理员的。Conn.jsp具体代码如下:
    <%@ page language="java" import="java.util.*" pageEncoding="gbk"%><% String id = (String)session.getAttribute("user"); if (null==id || id.equals("")) { out.print("<script language='javascript'>alert('请先登录系统!');window.location = 'login.jsp';</script>"); }%>
    admin_conn.jsp的代码如下所示:
    <%@ page language="java" import="java.util.*" pageEncoding="gbk"%><% String admin_id = (String)session.getAttribute("admin"); if (null==admin_id || admin_id.equals("")) { out.print("<script language='javascript'>alert('请先登录系统!');window.location = 'admin_login.jsp';</script>"); }%>
    13 辅助页面获取程序文件夹的绝对路径功能和中文字符串的转换是好多页面都共有的,存在一个页面调用起来比较方便,Config.jsp代码如下:
    <%@ page language="java" contentType="text/html; charset=gbk"%><%//获取程序文件夹的绝对路径String dirPath=application.getRealPath(request.getRequestURI());dirPath=dirPath.substring(0,dirPath.lastIndexOf('\\')+1);dirPath=dirPath.substring(0,dirPath.lastIndexOf('\\'));dirPath=dirPath.substring(0,dirPath.lastIndexOf('\\')+1);%><%!//定义处理中文字符串的函数public String codeToString(String str01) throws Exception{//处理中文字符串的函数 String s=str01; if(true) { try { byte tempB[]=s.getBytes("ISO-8859-1"); s=new String(tempB); } catch(Exception e) { s=str01; } } return s;}%>
    14 退出系统会员在网站中可以选择退出系统,logon.jsp负责销毁用户的session对象,导致登录失效,用户能够随时退出系统。
    logon.jsp的代码如下:
    <%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%><% session.invalidate(); response.sendRedirect("login.jsp");%>
    15 设计总结通过本次设计,基本掌握了WEB设计的页面设计,首次接触学习并使用JSP,JAVABEAN以及文件的上传下载,数据的连接等知识,对以后的深入学习打下了基础。就这个设计而言,由于时间以及个人能力的限制,在网站编写的过程中有很多的不足。许多老师要求的功能都没有达到,比如文件的搜索与共享等,只是能初步实现功能。在这方面应该加强学习,争取取得好的成绩。最后谢谢老师给我们这次的课程设计锻炼的机会。
    参考文献[1] 王柯柯,Web网页设计技术,机械工业出版社,2011
    [2] 马建红,李占波,JSP应用与开发技术,2011
    [3] 常建功,Java Web典型模块与项目实战大全,2010
    [4] 史晓红,章立, Dreamweaver CS4网页设计与实训教程,2010
    1 评论 20 下载 2018-11-04 15:31:18 下载需要7点积分
  • 基于JAVA的记事本

    一、绪论1.1 引言现如今,电脑已经成为了每家每户甚至是每个人手头都必有的一种实用性工具,它改变了人们的生活,大大提高了人们的工作效率。在此基础上,电脑端的记事本应用一直是每台电脑所必备的实用性应用,不管是在台式电脑、笔记本电脑或者平板电脑上,都能看到它的身影。其功能基本有如下几种:文件、编辑、格式、查看、帮助,每个功能下又有多个子功能,为使用者提供了多种编辑上的便利,基本能满足人们记事的需求,特别是快速笔记。正因为它的这些特点,才让它成为每台电脑中必不可少的成分。
    1.2 编写目的电脑端记事本是每台电脑的标配,有相当大的实用性,方便人们平时的记事之用,尤其是在快速笔记这方面,更是有非常大的作用,基本能满足人们的记事需求,有很大的开发及继续完善开发的意义。
    基于记事本的诸多优点,本课程设计针对电脑端的记事本进行开发设计,并在原有基础上进行完善,使它的功能更完善、更人性化及更实用化。
    1.3 背景随着人们生活信息化的提高,记事本只拘泥于笔和纸的时代已经一去不复返了,越来越多的电子版记事本进入了人们的生活。但如今的电脑端记事本软件感觉功能不够丰富,缺少一些个性化功能,导致用户体验不是很好,故本课程设计将开发一个加强版的电脑端记事本,来满足用户的需求。
    二、系统可行性研究2.1 系统概述2.1.1 当前系统分析当前电脑系统自带的记事本实现的功能有如下几种:文件、编辑、格式、查看、帮助,每个功能下又有多个子功能:

    “文件”主菜单中有“新建”、“打开”、“保存”、“另存为”、“页面设置”、“打印”、“退出”这几个子功能
    “编辑”主菜单中有“撤销”、“剪切”、“复制”、“粘贴”、“删除”、“查找”、“查找下一个”、“替换”、“转到”、“全选”、“日期/时间”这几个子功能
    “格式”主菜单中有“自动换行”、“字体”这两个子功能
    “查看”主菜单中有“状态栏”子功能
    “帮助”主菜单中有“查看帮助”、“关于记事本”这两个子功能

    2.1.2 目标系统分析在实现系统自带笔记本的功能同时,再添加一些个性化功能,例如为记事本添加上行号(这大大提高了我们程序员看代码的方便性),在状态栏添加上当前时间以及字数统计,让用户能够对自己所写的字数一目了然,大大增强了用户体验。
    此外,此记事本支持用户自定义背景颜色以及字体颜色,增强了趣味性,用户可以根据自己的喜好选择符合自己的主题。
    即实现的功能有:

    “文件”主菜单中有“新建”、“打开”、“保存”、“另存为”、“页面设置”、“打印”、“退出”这几个子功能
    “编辑”主菜单中有“撤销”、“剪切”、“复制”、“粘贴”、“删除”、“查找”、“查找下一个”、“替换”、“转到”、“全选”、“日期/时间”这几个子功能
    “格式”主菜单中有“自动换行”、“字体”、“背景颜色”、“字体颜色”这四个子功能
    “查看”主菜单中有“状态栏”子功能
    “帮助”主菜单中有“查看帮助”、“关于记事本”这两个子功能

    2.2 可行性分析研究2.2.1 技术可行性由于计算机技术和互联网技术的发展突飞猛进,计算机的应用深入各行各业。涌现出各种编程语言。本电脑端记事本采用JAVA语言进行开发设计。JAVA语言是一门面向对象的语言,风格接近C、C++语言,但又舍弃了C和C++语言中易引起错误的指针、运算符重载、多重继承等特性,使开发的程序质量更高。由于开发记事本的难度不高,因此通过JAVA语言在Eclipse编译器上就可以实现开发了。
    综上,技术可行性满足。
    2.2.1.1 Java的基本信息及优势Java是一种可以撰写跨平台应用程序的面向对象的程序设计语言。Java 技术具有卓越的通用性、高效性、平台移植性和安全性,广泛应用于PC、数据中心、游戏控制台、科学超级计算机、移动电话和互联网,同时拥有全球最大的开发者专业社群。
    与传统程序不同,Sun 公司在推出 Java 之际就将其作为一种开放的技术。全球数以万计的 Java 开发公司被要求所设计的 Java软件必须相互兼容。“Java 语言靠群体的力量而非公司的力量”是Sun公司的口号之一,并获得了广大软件开发商的认同。这与微软公司所倡导的注重精英和封闭式的模式完全不同。
    Sun 公司对 Java 编程语言的解释是:Java 编程语言是个简单、面向对象、分布式、解释性、健壮、安全与系统无关、可移植、高性能、多线程和静态的语言。
    Java 平台是基于 Java 语言的平台。这样的平台非常流行。因此微软公司推出了与之竞争的.NET平台以及模仿Java的C#语言。
    Java是功能完善的通用程序设计语言,可以用来开发可靠的、要求严格的应用程序。
    2.2.1.2 Eclipse的基本信息Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse 附带了一个标准的插件集,包括Java开发工具(Java Development Kit,JDK)。

    2.2.2 经济可行性主要分为三方面进行分析,分别是开发的财力物力及时间。

    开发的财力物力:

    笔记本电脑X1其他成本几乎为零,因为该项目开发的难度不大,完成时即刻可以使用,也不需要另外研发硬件设施进行使用,用电脑就行
    开发的时间:

    从一开始的分析设计到最后的测试维护,时间约为一周就可以,时间成本不大,可行性高
    收益:

    由于开发这个程序可以更好地满足人们的日常需求,收益还算不错的

    综上,经济可行性满足。
    2.2.3 操作可行性本程序采用的是图形化界面方式,记事本的操作不难,一般会使用电脑的人都会操作,只需按照图形界面进行操作,而且每个操作都有相关的快捷键提示,不需要相关的操作指导即可使用,可操作性非常强。
    2.2.4 社会可行性根据前期电脑上的记事本的使用情况及普及率来看,记事本的功能是受社会所认可的,人们普遍接受及使用电脑上的记事本,是可以为社会带来利益的。因此,对电脑端的记事本进行再开发完善,发掘它更多的功能并创造出社会价值,可行性是很高的。
    三、需求分析3.1 系统功能概述
    3.2 系统功能描述3.2.1 功能图
    3.2.2 用例描述


    用例名称
    新建




    涉及的参与者
    用户


    用例描述
    在Windows 环境下,新建一个空白txt文档


    前置条件
    记事本系统可用


    正常事件流
    1.点击开始-所有程序-附件-记事本 2.在记事本系统界面,点击文件-新建






    用例名称
    打开




    涉及的参与者
    用户


    用例描述
    在Windows 环境下,新建一个空白txt文档


    前置条件
    记事本系统可用


    正常事件流
    1. 双击打开记事本 2. 左键单击记事本-点击打开






    用例名称
    保存或另存为




    涉及的参与者
    用户


    用例描述
    在Windows 环境下,新建一个空白txt文档


    前置条件
    记事本系统可用


    正常事件流
    1. 打开空白记事本,点击文件-保存-再次打开 2. 点击文件-另存为-保存






    用例名称
    编辑




    涉及的参与者
    用户


    用例描述
    在Windows 环境下,新建一个空白txt文档


    前置条件
    记事本系统可用


    正常事件流
    1. 在编辑区域输入“软件工程课程设计” 2. 在编辑区域输入“1504” 3. 选中内容“软件工程课程设计”,点击编辑-剪切 4. 点击编辑-撤销 5. 选中内容“软件工程课程设计”,点击编辑-复制 6. 点击编辑-粘贴 7. 选中内容“软件工程课程设计”,点击编辑-删除 8.点击编辑-查找,查找内容“软件工程课程设计” 9. 点击编辑-查找下一个






    用例名称
    字体




    涉及的参与者
    用户


    用例描述
    在Windows 环境下,新建一个空白txt文档


    前置条件
    记事本系统可用


    正常事件流
    1. 在记事本系统界面,点击格式-字体 2. 在编辑区域输入“软件工程课程设计”,字体改为Wingdings,点击确定 3. 选择字体-斜体 4. 选择字体-8



    3.3 系统功能实现3.3.1 时序图
    3.3.2 类图(只选取部分函数)
    3.4 系统角色用户可以在系统里创建新文件,编辑文字,并保存,以便查看,还可以进行批处理文件等操作。
    3.5 系统流程图3.5.1 主流程图如下图所示,打开记事本后,可在文本域进行文本输入;或者进行一系列执行操作包括:文件、编辑、格式以及帮助菜单。

    功能描述:打开文件、编辑、格式以及帮助菜单
    输入项:点相关按钮
    输出项:做出指定动作


    3.5.2文件菜单操作流程图文件菜单操作流程如下图,当打开文件菜单时,下拉显示子菜单包括新建、打开、保存、退出等功能。

    功能描述:实现文件新建、打开、保存、另存为、退出功能
    输入项:点相关按钮
    输出项:做出指定动作


    3.5.3 编辑菜单操作流程图编辑菜单操作流程如下图,当打开编辑菜单时,下拉显示子菜单包括复制、粘贴、剪切、全选、查找、替换等功能。

    功能描述:实现文本的剪切、复制、粘贴、撤销、查找、替换、删除功能
    输入项:点相关按钮
    输出项:做出指定动作


    3.5.5帮助菜单操作流程图帮助菜单操作流程如下图,当打开帮助菜单时,下拉显示子菜单关于记事本。

    功能描述:查看帮助信息
    输入项:点帮助按钮
    输出项:显示相关信息


    3.6 数据流图
    3.7 数据字典由于无数据库设计,所以无数据字典描述。
    四、概要设计4.1 系统运行环境4.1.1 操作系统
    操作系统无关性,Windows XP/7/8.1/10、Linux、Mac OS X下安装了Java的运行环境JRE即可运行
    JDK版本:1.8

    4.1.2 使用软件
    代码编写:Eclipse
    数据库:无
    建模工具:Rational Rose(自写)
    文档编写:Microsoft Word 2016

    4.1.3 开发语言
    Java
    4.2 系统总体设计4.2.1 基本设计概念和处理流程4.2.1.1 全局E-R图
    4.2.1.2 分E-R图


    4.2.2 系统总体结构与模块

    4.3 接口设计4.3.1 外部接口4.3.1.1 用户界面
    4.3.1.2 软件接口软件运行平台:Eclipse
    4.3.1.3 硬件接口硬件运行平台:PC端
    4.3.2 内部接口说明各个模块由谁调用,完成什么功能,完成后转入什么状态



    模块
    由谁调用
    功能
    完成后转入的状态




    clock
    NotepadMainFrame
    显示状态栏的时间



    MQFontChooser
    NotepadMainFrame
    字体选择器
    字体变更


    textLine
    NotepadMainFrame
    左边的行号



    状态栏
    NotepadMainFrame
    状态栏显示信息
    显示状态栏


    MainFrameWidowListener
    NotepadMainFrame
    退出窗口选择设置
    退出软件


    actionPerformed
    NotepadMainFrame
    记事本的状态操作
    更换操作模式


    exit,mySearch,paste等
    NotepadMainFrame
    退出,查找,暂停等操作
    记事本状态改变



    4.4 数据结构设计由于没有用到数据库设计,所以无数据结构设计。
    五、详细设计5.1 数据流图
    源点/终点:用户
    处理:编辑(包括撤销、复制、剪贴、粘贴等)、新建记事本、保存、打开记事本、格式、打印
    数据流:输入的字符,记事本,打印文档
    数据存储:本地磁盘

    此记事本的基本系统模型如下图

    细化后的数据流图如下图

    5.2 层次方框图
    5.3 功能模块图
    六、系统实现6.1 关键代码6.1.1 自动换行JTextArea有自己定义的策略。
    //设置文本区的换行策略。如果设置为 true,则当行的长度大于所分配的宽度时,将换行。此属性默认为 falsetextArea.setLineWrap(true);
    6.1.2背景颜色JColorChooser jcc1 = new JColorChooser();JOptionPane.showMessageDialog(this, jcc1,"选择背景颜色颜色",-1);color = jcc1.getColor();textArea.setBackground(color);

    6.1.3 字体颜色jcc1=new JColorChooser();JOptionPane.showMessageDialog(this, jcc1, "选择字体颜色", -1);color = jcc1.getColor();//String string=textArea.getSelectedText();textArea.setForeground(color);
    6.1.4鼠标右击菜单// 创建弹出菜单final JPopupMenu jp=new JPopupMenu(); //创建弹出式菜单,下面三项是菜单项textArea.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if(e.getButton()==MouseEvent.BUTTON3)//只响应鼠标右键单击事件 { jp.show(e.getComponent(),e.getX(),e.getY());//在鼠标位置显示弹出式菜单 } }});

    6.1.5 打印功能 public void Print() { try{ p = getToolkit().getPrintJob(this,"ok",null);//创建一个Printfjob 对象 p g = p.getGraphics();//p 获取一个用于打印的 Graphics 的对象 //g.translate(120,200);//改变组建的位置 this.textArea.printAll(g); p.end();//释放对象 g } catch(Exception a){ } }

    6.1.6 显示时间//用到了线程,每1秒刷新一次时间public class Clock extends Thread{ public void run() { while (true) { GregorianCalendar time = new GregorianCalendar(); int hour = time.get(Calendar.HOUR_OF_DAY); int min = time.get(Calendar.MINUTE); int second = time.get(Calendar.SECOND); NotepadMainFrame.label1.setText(" 当前时间:" + hour + ":" + min + ":" + second); try { Thread.sleep(1000); } catch (InterruptedException exception) { } } } }
    在NotepadMainFrame类中
    JToolBar toolState = new JToolBar();toolState.setSize(textArea.getSize().width, 10);//toolState.setLayout(new FlowLayout(FlowLayout.LEFT));toolState = new JToolBar(); toolState.setSize(textArea.getSize().width, 10);//toolState.setLayout(new FlowLayout(FlowLayout.LEFT)); label1 = new JLabel(" 当前系统时间:" + hour + ":" + min + ":" + second+" "); toolState.add(label1);Clock clock=new Clock();clock.start();
    6.1.7 显示行数和列数label2 = new JLabel(" 第 " + linenum + " 行, 第 " + columnnum+" 列 "); toolState.add(label2); toolState.addSeparator();textArea.addCaretListener(new CaretListener() { //记录行数和列数 public void caretUpdate(CaretEvent e) { JTextArea editArea = (JTextArea)e.getSource(); try { int caretpos = editArea.getCaretPosition(); linenum = editArea.getLineOfOffset(caretpos); columnnum = caretpos - textArea.getLineStartOffset(linenum); linenum += 1; label2.setText(" 第 " + linenum + " 行, 第 " + (columnnum+1)+" 列 "); } catch(Exception ex) { }}});contentPane.add(toolState, BorderLayout.SOUTH);toolState.setVisible(false);toolState.setFloatable(false);

    6.1.8 撤销、返回初始化一个UndoManger,然后通过body.getDocument().addUndoableEditListener(undoMgr)这个方法,就可以把撤销管理器的监听器添加到TextArea中。需要调用撤销的时候就调用unoMgr.undo()方法。
    UndoManager undoMgr=new UndoManager();JtextArea textArea.getDocument().addUndoableEditListener(undoMgr); if(undoMgr.canUndo()){ undoMgr.undo();//撤销} if(undoMgr.canRedo()){ undoMgr.redo();//恢复}
    6.2 程序目录截图
    七、系统测试


    测试评价
    针对实现的记事本的功能模块,基本上达到了预定的要求。
    缺陷(未实现功能):

    右键的菜单响应功能中的从右到左,Unicode码,汉字重选
    保存的文件设置了颜色和字体,打开后还是默认大小
    打印功能还可以再完善,现在只能打印一页多点,具体的设置还不是很懂,等我再多学点
    将自动换行和状态显示默认添加
    1 评论 22 下载 2018-11-04 15:19:13 下载需要6点积分
  • 基于汇编语言实现的带小数的四则运算

    一 需求分析从键盘输入一个简单的表达式,如“ S=4+6*9-1+8/5”,按回车键结束输入,则屏幕显示S=58.6,小数点保留1位。假设输入的表达式中只含个位十进制数和 “+”、“-”、“*”、“/”运算符,且同一运算符最多出现2次。
    二 程序设计2.1 设计思想这个程序应该能正确处理数字和数学表达式的输入。我的设想是使其进一步处理最多12位十进制小数的输入,以及带有括号、四则运算算式的正确处理,并给出可以精确到小数点后五位的正确结果。
    该程序应完成工作:

    公式的输入,包括处理数字输入、符号输入,以及正确处理输入公式的句法
    公式的计算。其中包括正确处理各种符号运算的优先级和结合性、中间数的临时保存、小数的正确处理等
    结果的正确显示

    主程序的大致框图如下:

    2.2 PARSEEXP逐字符进行读取,并根据读取到的字符判断算式中出现的token属于什么类型。若是数字,则调用INPUTDECIMAL将其处理成双精度浮点数;若是运算符,则对应处理(见下)。
    对于算式的处理和运算,采用了调度场算法(Shunting yard algorithm)。采用两个堆栈,一个放数字,一个放运算符。当算法执行到将运算符放置到数字上的步骤时,立即进行运算。这样,分析完算式后,数字栈顶便是结果。
    运算用Intel处理器的FLD、FADD等指令完成。
    流程见下图,省略了对部分错误状况的处理,详见源码。

    2.3 INPUTDECIMAL利用了英特尔处理器的FBLD指令,将十进制的小数转换为浮点数。
    FBLD指令可以将一段长度为10字节空间中的前9字节中的18位BCD码从十进制整数转换为FPU寄存器中的REAL10浮点数,后一字节最高位决定符号位。
    如一系列从低位到高位排列的字节:21 43 65 87 00 00 00 00 00 80可以转换为-87654321.0,存储在FPU的栈顶。
    对字符串倒序、去掉小数点转BCD码后,用FBLD转换为大浮点数;再根据小数点位置,用FBLD构造一个是10的若干次幂的浮点数,除前者,得到字符串表示的浮点数。再用FSTP将其作为双精度数存储于内存,该功能即得以实现。
    大致框图见下图。

    2.4 OUTPUTDECIMAL利用的是FBLD的逆向指令FBSTP。将双精度数用FLD载入FPU的寄存器后,乘以100000,用FBSTP转换为包含小数点后五位的BCD数码,再从右往左解析输出,在第五位加小数点即可。
    三 程序实现共四个源码文件:

    MAIN.ASM
    ID.ASM
    OD.ASM
    PARSEEXP.ASM

    3.1 处理十进制小数的转换:ID.ASM
    包含过程:INPUTDECIMAL,负责将内存中的十进制小数字符串转换为双精度浮点数,仍然存在内存中
    输入参数包括字符串的起始位置DS:SI,和输出位置ES:BX

    3.2 处理十进制小数的输出:OD.ASM
    包含过程:OUTPUTDECIMAL,负责将内存中的双精度浮点数以十进制小数形式输出
    输入参数包括字符串的起始位置DS:SI

    3.3 处理算式:PARSEEXP.ASM
    包含过程:PARSEEXP,负责将内存中的一串算式字符串进行解析运算,并将最终结果存在内存中
    也同时输出算式的逆波兰符号形式,方便调试找出错误
    会调用ID.ASM、OD.ASM中的功能
    输入参数包括字符串的起始位置DS:SI和输出位置DS:BX

    3.4 主程序:MAIN.ASM
    包含过程MAIN,是程序的入口
    功能包括显示输入提示、让用户输入算式,以及调用PARSEEXP处理算式,最后用OUTPUTDECIMAL输出计算结果

    四 运行测试4.1 程序环境、适用范围本程序应运行于:

    Intel 的16位处理器(或模拟其工作的模拟器上),如8086,并且应支持浮点运算功能
    MS-DOS,或Windows 7及以下x86操作系统的命令提示符中(在NTVDM中运行)

    本程序的测试环境为:

    DOSBox,以及VMWare Workstation下的Windows 7 x86虚拟机的cmd.exe
    本程序适用于:

    简单的、输入式的、包括+、-、*、/、()的数学运算式,结果精度不超过5位小数
    4.2 使用方法
    启动MS-DOS操作系统(或DOSBox),或Windows x86系统的命令提示符(CMD.exe)
    用cd命令和盘符命令,将当前路径定位到本程序可执行文件的目录下
    输入MAIN.EXE,按回车键
    此时程序提示用户输入“S=”。用户应该输入一个算式,之后按回车键
    程序应能给出S的最终结果

    4.3 测试数据




    1 评论 33 下载 2018-11-04 10:29:53 下载需要4点积分
显示 780 到 795 ,共 15 条
eject