第二次作业

发布时间 2023-12-03 18:06:21作者: 故意123

实验目的

   1.掌握软件开发的基本流程。

   2.掌握常用的软件开发方式和工具。

实验内容     

   设计一个包含登录界面的计算器软件,该软件可以实现第一次作业中的全部功能,同时可以保存用户的历史计算记录(保存数据最好使用数据库)。

实验过程

  1.流程图

   (1)用户登录流程图

 

    (2)计算器功能模块流程图

   

     2.用户登录模块

   (1)用户登录界面

(2)用户登录界面代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<title></title>
<meta name="keywords" content="" />
<meta name="description" content="" />
<style type="text/css">
body {
     background-size: cover;

                min-width: 1000px;

    background-image:url(https://s1.ax1x.com/2020/08/23/d0t8ln.jpg);

    font-size:14px;
}
#main {
    width:360px;
    height:320px;
    background:#fff;
    border:1px solid #ddd;
    position:absolute;
    top:50%;
    left:50%;
    margin-left:-180px;
    margin-top:-160px;
    background-size: cover;
    background-image:url(https://s1.ax1x.com/2020/07/31/alzGMn.jpg);
}
#main .title {
    height: 48px;
    line-height: 48px;
    color:#333;
    font-size:16px;
    font-weight:bold;
    text-indent:30px;
    border-bottom:1px dashed #eee;
    
}
#main form {
    width:300px;
    margin:20px 0 0 40px;
}
#main form label {
    margin:20px 0 0 40px;
    display:block;
}
#main form label input.text {
    width:200px;
    height:25px;
}

#main form label input.submit {
    width:200px;
    display:block;
    height:35px;
    cursor:pointer;
    margin:20px 0 0 2px;
}
</style>
</head>
<body>
    <div id="main">
        <div class="title">欢迎使用,请登录</div>
        <form  action="login.php" method="post" onsubmit="return enter()">
            <label><input class="text" type="text"  placeholder="用户名" name="username" /></label>
            <label><input class="text" type="password"  placeholder="密码" name="password" /></label>
            
            <label><input class="submit" type="submit" name="submit" value="登录" /></label>
            
            <a href="register.html" target="_self"  style="position: absolute; bottom: 10px; right: 10px;">没有账号,先注册</a>
        
        </form>
    </div>
    
</body>
</html>

(3)登录页面后台代码

<?php
error_reporting(0);
//第一次登陆的时候,通过用户输入的信息来确认用户
 if ( (($_POST['username']))!=NULL && (($_POST['password'])!=NULL)) {
    $userName = $_POST['username'];
    $password = $_POST['password'];
    //从db获取用户信息
    //PS:数据库连接信息改成自己的 分别为主机 数据库用户名 密码
    $conn = mysqli_connect('localhost:3306','root','root');

    mysqli_select_db($conn,'test');

    $sql = "select username,password from user where username = '$userName' and password='$password'";
    $res = mysqli_query($conn,$sql);
    $row = mysqli_fetch_array($res);
    if    ($row['username']!=$userName) {
        echo '不能登陆!';
        header('Location:index.html');
    }
    else if($row['username']==$userName&&$row['password']!=$password)
    {
        echo '不能登陆!!';
        header('Location:index.html');
    }
    else if($row['username']!=$userName&&$row['password']!=$password) {
        echo '不能登陆!!!';
        header('Location:index.html');
    }
    
    else if($row['username']==$userName&&$row['password'] ==$password) {
        //如果密码验证通过,设置一个cookies,把用户名保存在客户端
        setcookie('username',$userName,time()+3600);//设置一个小时
        //最后跳转到登录后的欢迎页面
        /*echo '登陆成功!';
        header('Location:https://y.qq.com/n/yqq/mv/v/o0013f4q6uz.html');//跳转到最后的欢迎页面*/
        echo "<script>alert('登陆成功!');location.href='https://pan.baidu.com/s/1qK_KopZzMZIKfT7jkZ1FVQ';</script>";
    }
    }
    else {
        echo '登陆失败';
        header('Location:index.html');//跳转到失败页面
    }
  
if ( (($_COOKIE['username']) != null)  && (($_COOKIE['password']) != null) ) {
    $userName = $_COOKIE['username'];
    $password = $_COOKIE['password'];

    //从db获取用户信息
    //PS:数据库连接信息改成自己的 分别为主机 数据库用户名 密码
    $conn = mysqli_connect('localhost','root','password','test');
    $res = mysqli_query($conn,"select * from user where username =  '$userName' ");
    $row = mysqli_fetch_assoc($res);
    if ($row['password'] == $password) {
        //验证通过后跳转到登录后的欢迎页面
        header('Location: https://pan.baidu.com/s/1qK_KopZzMZIKfT7jkZ1FVQ' . "?username=$userName");
    }
}
else {
    echo "<script>alert('用户名或密码错误');location.href='index.html';</script>";
}

?>

(4)测试结果

           1>数据库用户信息

 

     2>登录成功页面

 

     3>登录失败界面

3.计算器功能模块

    (1)计算器界面

       

(2)计算器功能测试结果

   

 

  (3)数据库实现

 

 

 (4)计算器代码

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.Objects;
import java.util.Stack;

import javax.swing.*;

//Calculator类,继承JFrame框架,实现事件监听器接口
public class Calculator extends JFrame implements ActionListener {
    private final String[] KEYS = {"mc", "m+", "m-", "mr", "c", "/", "*", "ms", "7", "8", "9", "4", "5", "6", "1", "2", "3", "+", "√", "0", ".", "="};
    private JButton keys[] = new JButton[KEYS.length];
    private JTextArea resultText = new JTextArea("0.0");    // 文本域组件TextArea可容纳多行文本;文本框内容初始值设为0.0
    private JTextArea History = new JTextArea();    // 历史记录文本框初始值设为空
    private JPanel jp2 = new JPanel();
    private JScrollPane gdt1 = new JScrollPane(resultText);     //给输入显示屏文本域新建一个垂直滚动滑条
    private JScrollPane gdt2 = new JScrollPane(History);    //给历史记录文本域新建一个垂直滚动滑条
    private JLabel label = new JLabel("历史记录");
    private String input = "";   //计算文本框输入的中缀表达式

    // 构造方法
    public Calculator() {
        super("Caculator");//“超”关键字,表示调用父类的构造函数,
        resultText.setBounds(20, 18, 255, 115);     // 设置文本框大小
        resultText.setAlignmentX(RIGHT_ALIGNMENT);  // 文本框内容右对齐
        resultText.setEditable(false);  // 文本框不允许修改结果
        resultText.setFont(new Font("monospaced", Font.PLAIN, 18));    //设置结果文本框输入文字的字体、类型、大小
        History.setFont(new Font("monospaced", Font.PLAIN, 18));    //设置历史记录文本框输入文字的字体、类型、大小
        History.setBounds(290, 40, 250, 370);   // 设置文本框大小
        History.setAlignmentX(LEFT_ALIGNMENT);  // 文本框内容右对齐
        History.setEditable(false);     // 文本框不允许修改结果
        label.setBounds(300, 15, 100, 20);  //设置标签位置及大小
        jp2.setBounds(290, 40, 250, 370);   //设置面板窗口位置及大小
        jp2.setLayout(new GridLayout());
        JPanel jp1 = new JPanel();
        jp1.setBounds(20, 18, 255, 115);    //设置面板窗口位置及大小
        jp1.setLayout(new GridLayout());
        resultText.setLineWrap(true);   // 激活自动换行功能
        resultText.setWrapStyleWord(true);  // 激活断行不断字功能
        resultText.setSelectedTextColor(Color.RED);
        History.setLineWrap(true);  //自动换行
        History.setWrapStyleWord(true);
        History.setSelectedTextColor(Color.blue);
        gdt1.setViewportView(resultText);   //使滚动条显示出来
        gdt2.setViewportView(History);
        gdt1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);     //设置让垂直滚动条一直显示
        gdt2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);     //设置让垂直滚动条一直显示
        gdt2.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);     //设置让水平滚动条一直显示
        jp1.add(gdt1);  //将滚动条添加入面板窗口中
        jp2.add(gdt2);
        this.add(jp1);  //将面板添加到总窗体中
        this.add(jp2);  //将面板添加到总窗体中
        this.setLayout(null);
        this.add(label);    // 新建“历史记录”标签

        // 放置按钮 x,y为按钮的横纵坐标
        int x = 20, y = 150;
        for (int i = 0; i < KEYS.length; i++) {
            keys[i] = new JButton();
            keys[i].setText(KEYS[i]);
            keys[i].setBounds(x, y, 60, 40);
            if (x < 215) {
                x += 65;
            } else {
                x = 20;
                y += 45;
            }
            this.add(keys[i]);
        }
        for (int i = 0; i < KEYS.length; i++)   // 每个按钮都注册事件监听器
        {
            keys[i].addActionListener(this);
        }
        this.setResizable(false);
        this.setBounds(500, 200, 567, 480);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    // 事件处理
    public void actionPerformed(ActionEvent e) {
        String label = e.getActionCommand();    //获得事件源的标签
        if (Objects.equals(label, "AC"))    //清空按钮,消除显示屏文本框前面所有的输入和结果
        {
            input = "";
            resultText.setText("0.0");      //更新文本域的显示,显示初始值;
        } else if (Objects.equals(label, "sqrt")) {
            String n;
            if(input.isEmpty()) n="error!";     //加判断,先输入数字再输入开方符号才是合法的
            else n = String.valueOf(kfys(input));
            resultText.setText("sqrt" + "(" + input + ")" + "=" + n);       //使运算表达式显示在输入界面
            History.setText(History.getText() + resultText.getText() + "\n");       //获取输入界面的运算表达式并使其显示在历史记录文本框
            input = n;
        } else if (Objects.equals(label, "x*x")) {
            String m;
            if(input.isEmpty()) m="error!";
            else m = String.valueOf(pfys(input));
            resultText.setText(input + "^2" + "=" + m);
            History.setText(History.getText() + resultText.getText() + "\n");
            input = m;
        } else if (Objects.equals(label, "=")) {
            if (input.isEmpty()) return;
            String[] s = houzhui(input);    //将中缀表达式转换为后缀表达式
            double result = Result(s);     //计算后缀表达式得出最终算式结果
            resultText.setText(input + "=" + result);
            History.setText(History.getText() + resultText.getText() + "\n");
        } else {
            if (Objects.equals(label, "e")) {
                String m = String.valueOf(2.71828);     //将e的值以字符串的形式传给m
                label = m;
            } else if (Objects.equals(label, "pi")) {
                String m = String.valueOf(3.14159);
                label = m;
            }
            input = input + label;
            resultText.setText(input);
        }
    }

    //将中缀表达式转换为后缀表达式
    private String[] houzhui(String infix) {  //infix 中缀
        String s = "";// 用于承接多位数的字符串
        Stack<String> opStack = new Stack<String>();    // 操作符静态栈,对用户输入的操作符进行处理,用于存储运算符
        Stack<String> postQueue = new Stack<String>();   // 后缀表达式,为了将多位数存储为独立的字符串
        System.out.println("中缀:" + infix);
        for (int i = 0; i < infix.length(); i++)    // 遍历中缀表达式
        // indexof函数,返回字串首次出现的位置;charAt函数返回index位置处的字符;
        {
            if ("1234567890.".indexOf(infix.charAt(i)) >= 0) {    // 遇到数字字符直接入队
                //判断并记录多位操作数,比如,中缀表达式:234+4*2,我们扫描这个字符串的时候,s的作用相当于用来存储长度为3个字符的操作数:234
                s = "";// 作为承接字符,每次开始时都要清空
                for (; i < infix.length() &amp;&amp; "0123456789.".indexOf(infix.charAt(i)) >= 0; i++) {
                    s = s + infix.charAt(i);
                }
                i--;   //避免跳过对非数字字符的处理
                postQueue.push(s);  // 数字字符直接加入后缀表达式
            } else if ("(".indexOf(infix.charAt(i)) >= 0) {   // 遇到左括号
                opStack.push(String.valueOf(infix.charAt(i)));   // 左括号入栈
            } else if (")".indexOf(infix.charAt(i)) >= 0) {   // 遇到右括号
                while (!opStack.peek().equals("(")) {    // 栈顶元素循环出栈,直到遇到左括号为止
                    postQueue.push(opStack.pop());
                }
                opStack.pop();     //删除左括号
            } else if ("*%/+-".indexOf(infix.charAt(i)) >= 0)   // 遇到运算符
            {
                if (opStack.empty() || "(".contains(opStack.peek())) {   // 若栈为空或栈顶元素为左括号则直接入栈
                    opStack.push(String.valueOf(infix.charAt(i)));
                } else {
                    // 当栈顶元素为高优先级或同级运算符时,让栈顶元素出栈进入后缀表达式后,直到符合规则后,当前运算符再入栈
                    boolean rule = ("*%/+-".contains(opStack.peek()) &amp;&amp; "+-".indexOf(infix.charAt(i)) >= 0) || ("*%/".contains(opStack.peek()) &amp;&amp; "*%/".indexOf(infix.charAt(i)) >= 0);
                    while (!opStack.empty() &amp;&amp; rule) {
                        postQueue.push(opStack.peek());  //peek()方法:返回栈顶的元素但不移除它
                        opStack.pop();
                    }
                    opStack.push(String.valueOf(infix.charAt(i)));   // 当前元素入栈
                }
            }
        }
        while (!opStack.empty()) {// 遍历结束后将栈中剩余元素依次出栈进入后缀表达式
            postQueue.push(opStack.pop());
        }
        //将后缀表达式栈转换为字符串数组格式
        String[] suffix = new String[postQueue.size()];
        for (int i = postQueue.size() - 1; i >= 0; i--) {
            suffix[i] = postQueue.pop();
        }
        System.out.println("后缀:" + Arrays.toString(suffix.clone()));
        return suffix;
    }

    //开方运算方法
    public double kfys(String str) {
        double a = Double.parseDouble(str);
        return Math.sqrt(a);
    }


    // 计算后缀表达式,并返回最终结果
    public double Result(String[] suffix) {  //suffix 后缀
        Stack<String> Result = new Stack<>();// 顺序存储的栈,数据类型为字符串
        int i;
        for (i = 0; i < suffix.length; i++) {
            if ("1234567890.".indexOf(suffix[i].charAt(0)) >= 0) {  //遇到数字,直接入栈
                Result.push(suffix[i]);
            } else {    // 遇到运算符字符,将栈顶两个元素出栈计算并将结果返回栈顶
                double x, y, n = 0;
                x = Double.parseDouble(Result.pop());   // 顺序出栈两个数字字符串,并转换为double类型
                y = Double.parseDouble(Result.pop());
                switch (suffix[i]) {
                    case "*":
                        n = y * x;
                        break;
                    case "/":
                        if (x == 0) return 000000;
                        else n = y / x;
                        break;
                    case "%":
                        if (x == 0) return 000000;
                        else n = y % x;
                        break;
                    case "-":
                        n = y - x;
                        break;
                    case "+":
                        n = y + x;
                        break;
                }
                Result.push(String.valueOf(n)); // 将运算结果重新入栈
            }
        }

        System.out.println("return:" + Result.peek());
        return Double.parseDouble(Result.peek());   // 返回最终结果
    }

    // 主函数
    public static void main(String[] args) {
        Calculator a = new Calculator();
    }
}