分类

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

资源列表

  • 基于Java的聊天室系统

    一 需求分析编写一个小型Java聊天室系统,掌握Java网络通信、多线程、IO文件操作等高级应用编程技能。
    完成如下功能:

    多客户端模式下,实现客户与客户的单独通信,要求信息通过服务器中转;
    端到端的通信,实现并行通信模式(一端的信息发送不受另一端的影响);
    添加图形界面.

    二 程序设计2.1 设计思想
    利用socket套接字通信
    多线程处理不同任务
    用Properties在本地存储注册账号
    下载安装windowbuilder插件并用其设计图形界面

    2.2 整体设计(类之间关系)
    Server类(服务器),包含几个继承Runnable的内部类,用于处理客户端请求
    Client类、Login类、TalkFrame类、Regist类。Login类用于登陆,Regist类用于注册,TalkFrame类用于对话,Client实例则被这三个类调用
    Account类,这个类很简单,只有id和password两个属性和相应的set方法。

    类之间关系如下图所示:

    2.3 类的设计
    Client类中只定义了Login、Regist、TalkFrame中需要的socket和流,和 对这些属性初始化的构造函数。
    Login类用于登陆,包含账号label、密码label、账号JTextField、密码 JPasswordField、登陆按钮和注册按钮这些组件,还有一些监听器。
    如下图所示:



    Regist类用于注册,包含用户名label、密码labl、确认密码label、用 户名JTextField、密码JPasswordField、确认密码JpasswordField和注册按钮这些组件,还有一些监听器。
    如下图所示:



    TalkFrame类用于对话,包含选择对话用户label、UserList在线用户JscrollPane 面板、list用户列表、显示对话文本区域、发送信息文本区域和发送消息按钮这些组件,以及对应的监听器。TalkFrame中有两个继承了Runnable的内部类,分别处理在线用户显示和消息的发送。
    如下图所示:



    Server类有三个ServerSocket分别监听登陆端口、注册端口、显示在线用户端口。还有好几个继承Runnable的内部类,用于处理注册、登陆、显示在线用户、用户对话。
    三 程序实现由于涉及了账号注册的功能,必须能在本地保存账号,但我不会在Java中数据库插入数据库,所以只能想办法保存数据,一开始用的是hashtable,但保存到存盘有点困难,在网上搜到的方法是使用XML文件存取可序列化的对象的类,由于hashtable已经实现了序列化接口,所以可以这样实现,我用这种方法写了一遍,发现非常麻烦,再次百度,发现了properties这个集合类,查看JDK文档,了解了properties的load和store方法,这两个方法能很容易地将账号存储在磁盘的文件中。进而,我改用了properties存储账号。
    public Properties userInformation;//与磁盘文件建立联系userInformation = new Properties();uis = new FileInputStream("f:/userInfo.properties");uos = new FileOutputStream("f:/userInfo.properties", true);userInformation.load(uis);//将用户名和密码存储到内存中 userInformation.setProperty(account.getId(),account.getPW());//将用户名和密码保存到文件中userInformation.store(uos, null);
    在处理server接收到的信息时,我用了三个ServerSocket进行处理,RegistServer监听规定的注册端口,对接收的注册信息进行处理;LoginServer监听登陆端口,对登陆后的对话信息进行规定,要求客户端传递的信息必须以接收者id+“:”+发送者id+“:”+发送信息的形式传递给服务器,服务器对传递过来的字符串进行截取,将信息发送给接收者客户端。
    监听发送消息按钮,在字符串前面加上规定的信息然后传递给服务器sendButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { String str = sendMessageTextArea.getText(); String sendMessage = recieverId+":"+id+":"+"\n"; if(!sendMessage.equals("")){ client.pw.print(sendMessage); client.pw.flush(); oldMessageTextArea.append(sendMessage); oldMessageTextArea.setText(""); } } });
    服务器截取信息,获取接收者id,找到对应socket,将信息传递过去。
    String s = br.readLine(); String accountId =s.substring(0,s.indexOf(":")); String message = s.substring(s.indexOf(":")+1); receiveClient = clientConnection.get(accountId); PrintWriter pw= new PrintWriter(receiveClient.getOutputStream()); pw.println(message);
    四 运行测试登陆界面如下图:

    注册界面如下图:
    2 评论 90 下载 2018-10-27 22:17:51 下载需要5点积分
  • 基于java语言的C/S模式网络聊天室软件

    一 需求分析
    采用C/S模式,基于TCP协议编程的方式,使得各个用户通过服务器转发实现聊天的功能
    分为三大模块:客户端模块、服务器端模块和公共辅助类模块
    客户端模块的主要功能:

    登陆功能:用户可以注册,然后选择服务器登入聊天室
    显示用户:将在线用户显示在列表中
    接收信息:能接收其他用户发出的信息
    发送信息:能发出用户要发出的信息

    服务器端模块的主要功能:

    检验登陆信息:检查登陆信息是否正确,并向客户端返回登陆信息,如信息正确。就允许用户登陆
    显示在线状态:将该用户的状态发给各在线用户
    转发聊天信息:将消息转发给所有在线的用户

    公共辅助类模块的主要功能:

    定义完整的消息传递机制
    对消息转发的方式进行有效约束
    规定消息类型


    二 程序设计2.1 程序设计思想实现网络聊天室编程,关键在于Socket通信,程序的功能都是在Socket的基础上一层一层增加的。
    实现Socket通信的基本方法为以下4个步骤:

    客户端与服务器端分别实例化ServerSockot/Socket
    打开连接到Socket的面向对象输入输出流
    利用输入输出流按照TCP协议对Socket进行读写操作
    关闭输入输出流和Socket

    我们要实现的功能都是在第3步对Socket的输入输出流做相应的操作:

    涉及到多客户端并发访问,必须用线程进行控制,不同的处理线程为不同的客户服务,主线程只负责循环等待,处理线程负责网络连接,接受客户输入的信息,根据消息类型对消息转发。
    2.2 系统模块划分客户端

    Client类模块,ClientUI界面模块,ClientMsgType消息处理模块
    服务器

    Server类模块,ServerHandler模块,ServerMsgType消息处理模块
    公共类:

    Message消息结构类,Common公共函数类,Config系统配置类
    系统模块结构图

    2.3 模块功能设计客户端

    Client类模块:与服务端建立连接,完成客户端登录,实现消息的群聊,私聊,用户列表更新,文件转换等功能
    ClientUI界面模块:客户端窗口界面,实现用户友好,方便用户使用,在窗口上有消息显示,用户列表,消息类型,在线人数等视图信息
    ClientMsgType消息处理模块:定义了常用的消息处理方法

    服务端

    Server类模块:启动监听,与客户端建立连接
    ServerHandler模块:提供线程管理,实现多用户的管理。针对每一个用户发过来的消息,进行相应处理,再转发给客户
    ServerMsgType消息处理模块:对客户端发送的消息进行指定操作模式处理,只进行消息转发,涉及一对多和一对一两种模式

    公共类

    Message消息结构类:客户端和服务器传输的消息被定义为类,通过对Message对象的序列化传输
    Common公共函数类:提供文件序列化的处理以及常用的功能性函数
    Config系统配置类:包含连接IP及端口号的配置信息

    三 程序实现3.1 客户端实现Client类模块
    关键模块,通过服务器IP地址与端口建立连接。客户端有套接字和输入输出流。首先输入用户名,然后包装成Message对象发送给服务器,之后进入主循环对来自服务器的消息进行处理。
    public void run() { try { // 获取用户名 while (name == null || name.equals("")) name = JOptionPane.showInputDialog("请输入名字").trim(); // 第一次运行,发送登陆消息 send(new Message("login", name, null, null)); // 显示界面 ui = new ClientUI(this, name + " 的客户端"); // 消息处理主循环 while (true) { Message rcv = (Message) in.readObject(); // 服务器端发来的消息 System.out.println(rcv.toString()); new ClientMsgType(this, rcv).process(); } } catch (Exception e) { System.out.println(e.toString() + " 客户端退出"); } }
    ClientUI类模块
    用户界面,所有的界面元素及相关的操作都在里面定义了方法,不论UI界面如何改变都不会影响到外部类。里面含有各种控件极其功能定义,还包括文件传输。主体代码如下:
    public ClientUI(Client client, String winname) { super(winname); // 继承父类的名字 setSize(600, 400); this.client = client; // 消息文本显示区域 msgArea = new JTextArea(400, 400); msgArea.setEditable(false); textAreaScrollPane = new JScrollPane(msgArea); add(textAreaScrollPane, BorderLayout.CENTER); // 发送消息区域 textFieldPanel.setLayout(new FlowLayout(0, 10, 10)); add(textFieldPanel, BorderLayout.SOUTH); clientList = new JComboBox<String>(); // 在线用户列表 clientList.addItem("All"); textFieldPanel.add(clientList); msgType = new JComboBox<String>(); // 聊天文本或者文件 msgType.addItem("chat"); msgType.addItem("file"); textFieldPanel.add(msgType); msgField = new JTextField(20); // 输入消息文本 textFieldPanel.add(msgField); btn = new JButton("发送"); // 发送消息按钮 btn.setMnemonic(KeyEvent.VK_ENTER); textFieldPanel.add(btn); cntLabel = new JLabel("在线人数:1"); // 显示在线人数 textFieldPanel.add(cntLabel); // 发送消息按钮监听器 btn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String type = (String) msgType.getSelectedItem(); String title = ""; String content = getText().trim(); byte[] fbyte = null; if (content.equals("")) { JOptionPane.showMessageDialog(client.ui, "输入不能为空"); return; } if (type.equals("file")) { // 获取客户端发送文件 JFileChooser dlg = new JFileChooser(); dlg.setDialogTitle("选择文件"); int result = dlg.showOpenDialog(client.ui); if (result == JFileChooser.APPROVE_OPTION) { File file = dlg.getSelectedFile(); fbyte = Common.file2Byte(file); title = file.getName(); } else { return; } append("TO " + getName() + ": " + title + "\n"); } append("TO " + getName() + ": " + content + "\n"); client.send(new Message(type, client.name, getName(), title, content, fbyte)); clear(); } }); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { client.close(); System.exit(0); } }); setVisible(true);}
    ClientMsgType类模块
    定义了客户端处理消息的方法,主体架构和服务区类似,这里我们只介绍处理文件的方法:
    /** * 文件处理 利用字节数组可以处理所有文件 */ public void file() { String toAll = "[public]"; if (msg.getTo().equals(client.name)) toAll = "[private]"; client.ui.append(toAll + msg.getFrom() + ": " + msg.getContent() + "\n"); int confirm = JOptionPane.showConfirmDialog(client.ui, "收到了来自" + msg.getFrom() + "的文件:" + msg.getTitle() + ",需要保存吗?"); if (confirm != JOptionPane.YES_OPTION) return; // 获取保存路径 JFileChooser dlg = new JFileChooser(); dlg.setDialogTitle("选择保存路径"); dlg.setSelectedFile(new File(msg.getTitle())); int result = dlg.showSaveDialog(client.ui); if (result != JFileChooser.APPROVE_OPTION) return; File file = dlg.getSelectedFile(); if (file.exists()) { int copy = JOptionPane.showConfirmDialog(null, "是否要覆盖当前文件?", "保存", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (copy == JOptionPane.YES_OPTION) { dlg.approveSelection(); } } else { dlg.approveSelection(); } if (Common.byte2File(msg.getFile(), file)) { JOptionPane.showMessageDialog(client.ui, "文件保存成功"); } else { JOptionPane.showMessageDialog(client.ui, "文件保存失败"); }}
    3.2 服务器实现Server类模块
    创建ServerSocket,在指定端口监听,当有用户登录,接受套接字输入流,显示其登录信息,调用ServerHandler线程进行管理。
    public Server(int port) { try { @SuppressWarnings("resource") ServerSocket server = new ServerSocket(port); while (true) { Socket conn = server.accept(); // 建立客户端套接字 // 新建线程和客户端建立全双工通信 new Thread(new ServerHandler(conn)).start(); } } catch (Exception e) { System.out.println(e.toString()); } }
    ServerHandler模块
    服务端的核心,其中包含客户端线程连接类,套接字及输入输出流的初始化。每个新连接的客户端被初始化为一个ServerHandler对象线程,首先进行的是客户端登陆合法化检验,通过后将客户端添加到服务器线程池并通知所有在线用户。之后采用一个无限循环来和该客户端进行双向通信。
    代码中数据结构定义:
    // 输入流private ObjectInputStream in; // 输出流private ObjectOutputStream out; // 用户名private String name;// 把用户线程放入对象数组private static ArrayList<ServerHandler> clientList = new ArrayList<ServerHandler>(); // 把用户名和连接线程关联便于查找private static HashMap<String, ServerHandler> clientMap = new HashMap<>();
    初始化代码
    public ServerHandler(Socket socket) { try { in = new ObjectInputStream(socket.getInputStream()); out = new ObjectOutputStream(socket.getOutputStream()); } catch (IOException e) { System.out.println(e.toString() + " 服务端初始化失败"); } }
    核心代码
    public void run() { if (!loginVerify()) return; // 登陆校验 try { addClient(); // 客户端经过登陆检验 添加到列表 sendMessage(new Message("online", name, "All", "上线了!")); // 广播用户列表 while (true) { Message rcv = (Message) in.readObject(); // 等待客户端消息 sendMessage(rcv); } } catch (Exception e) { System.out.println(e.toString() + " 客户端已退出"); } finally { removeClient(); sendMessage(new Message("offline", name, "All", "下线了!")); // 广播下线通知 } }
    登陆校验
    private Boolean loginVerify() { try { Message rcv = (Message) in.readObject(); System.out.println(rcv.toString()); this.name = rcv.getFrom(); if (clientMap.containsKey(name)) { // 用户名重复 sendToClient(this, new Message("login", null, name, "用户名已存在")); return false; } } catch (Exception e) { System.out.println(e.toString() + "登陆检验失败"); return false; } return true; }
    消息广播
    把msg消息发送给每一个用户。其中clientList是ServerHandler的客户端容器类型,可以指向每一个用户。clientMap 是用户名到线程的映射。
    public void sendAllClient(Message msg) { String name = msg.getFrom(); ServerHandler sh = clientMap.get(name); synchronized (clientList) { for (ServerHandler client : clientList) { if (client != sh) { sendToClient(client, msg); } } } }
    上线提醒
    sendClientList函数:更新用户列表,当有新用户上线时,对以前的用户的好友列表,加上这个新用户名字,对于这个新用户,加上所有用户的名字。通过将消息包装成Message对象进行数据交互。
    public synchronized void sendClientList(Message msg) { // 新上线的用户 ServerHandler newclient = clientMap.get(msg.getFrom()); for (ServerHandler client : clientList) { if (client == newclient) continue; // 给新上线用户所有列表 sendToClient(newclient, new Message(msg.getType(), client.name, newclient.name, null)); // 更新老用户 sendToClient(client, new Message(msg.getType(), newclient.name, client.name, msg.getContent())); }}
    ServerMsgType模块
    定义了服务端对客户端发送消息的处理方式。消息包装成Message类,经过序列化后在服务器和客户端间传输。服务端根据消息的收发对象和类型进行转发。这里利用到Java的反射机制,根据消息类型获取相应处理函数。
    public class ServerMsgType { ServerHandler server; Message msg; public ServerMsgType(ServerHandler server, Message msg) { this.server = server; this.msg = msg; } public void process() { try { Method method = this.getClass().getDeclaredMethod(msg.getType()); method.invoke(this); } catch (Exception e) { System.out.println(e.toString() + " 使用了未定义的操作"); try { Method method = this.getClass().getDeclaredMethod("chat"); method.invoke(this); } catch (Exception e1) { System.out.println(e1.toString() + " 默认处理失败"); } } } public void chat() { if (msg.getTo().equals("All")) { // 给所有人发送消息 server.sendAllClient(msg); } else { // 发送消息给指定的人 server.sendToClient(msg); } } public void online() { server.sendClientList(msg); } public void offline() { server.sendAllClient(msg); } public void login() { server.sendToClient(msg); }}
    3.3 公共类实现Message模块
    底层数据封装的核心。所有的消息全部通过封装成Message从而在服务器和客户端之间收发。主体内容如下:
    private String type; // 消息类型private String from; // 来源private String to; // 目的private String title; // 附件名private String content; // 消息内容private byte[] file; // 附件内容
    其中系统已经定义的消息类型包括:

    chat:聊天内容、online:客户端上线、offline:客户端下线、login:客户端登陆
    每种类型在服务器和客户端的MsgType类中都有定义对应的处理函数
    From字段标识消息发送的来源,为客户端的用户名
    To字段标识消息发送的对象,为客户端的用户名,如果为All则是广播消息
    Title字段当发送文件时有效,为原文件名
    Content字段为消息的内容
    File字段为附件的字节数组

    把以上字段组装成类之后经过序列化就可以在客户端和服务器之间传输。
    Common模块
    定义了文件处理相关的函数,这里我们把文件作为字节数组处理,这样实现了文件的透明化传输。
    文件转化为字节数组
    public static byte[] file2Byte(File file) { byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; int len = 0; try { FileInputStream fin = new FileInputStream(file); len = fin.read(buffer); fin.close(); } catch (Exception e) { System.out.println(e.toString() + " 文件无法转化成字节"); } byte[] ret = new byte[len]; for (int i = 0; i < len; i++) ret[i] = buffer[i]; return ret; }
    字节数组转化为文件
    public static boolean byte2File(byte[] in, File out) { try { FileOutputStream fout = new FileOutputStream(out); fout.write(in); fout.close(); } catch (Exception e) { System.out.println(e.toString() + " 字节转换失败"); return false; } return true; }
    Config模块
    主要包括IP和port的配置。
    四 运行测试4.1 客户端进入聊天室在客户端输入名字进入聊天室,如果名字重复会弹框提示。


    4.2 聊天界面聊天界面显示客户端名字,好友列表,在线人数,消息类型等信息。

    4.3 选择聊天类型4.3.1 聊天类型为chat分为群聊和私聊:群聊是选择All,私聊是选择具体的用户名。


    4.3.2 聊天类型为file发送文件,可以输入描述信息,客户端可以选择是否接收文件。




    4.4 人数动态刷新
    4 评论 107 下载 2018-11-04 21:43:30 下载需要4点积分
  • 基于Java实现的数独游戏

    一、本游戏背景介绍相传数独源起于拉丁方阵(Latin Square),1970年代在美国发展,改名为数字拼图(Number Place),之后流传至日本并发扬光大,以数字智力游戏智力拼图游戏发表。在1984年一本游戏杂志《パズル通信ニコリ》正式把它命名为数独,意思是“在每一格只有一个数字”。拼图是九宫格(即3格宽×3格高)的正方形状,每一格又细分为一个九宫格。在每一个小九宫格中,分别填上1至9的数字,让整个大九宫格每一列、每一行的数字都不重复。 数独的玩法逻辑简单,数字排列方式千变万化,不少教育者认为数独是锻炼脑筋的好方法。
    二、需求分析2.1 数独游戏规则
    在9×9的大九宫格内,已给定若干数字,其他宫位留白,玩家需要自己按照逻辑推敲出剩下的空格里是什么数字
    必须满足的条件:每一行与每一列都有1到9的数字,每个小九宫格里也有1到9的数字,并且一个数字在每行、每列及每个小九宫格里只能出现一次,既不能重复也不能少
    每个数独游戏都可根据给定的数字为线索,推算解答出来

    2.2 需求分析按照数独的游戏规则:

    用计算机实现已知数独的求解
    数独根据用户设定的题目难度的随机出题
    用户能玩预设的经典的数独题目
    用户能自定义题目并求解
    用户能保存和读取游戏

    三、实现3.1 采用的数据结构一维数组、二维数组以及十字链表。
    3.2 实现的方法3.2.1 二维数组// 用来处理相应的数据,即显示的数据private int[][] form1 = new int[20][20];// 用来辅助form1进行相关判断private int[][] form2 = new int[20][20];// 存储答案,用来检验答案private int[][] form3 = new int[20][20];// 用来储存form1 的数据,用于重新开始菜单private int[][] form4 = new int[20][20];
    数组的下标都是从1开始的,数据空白处则初始化为0。之所以把数组范围设计为20*20,是为了程序的可读性,符合人的习惯思维。
    // 用来显示用户的输入界面数组的下标是从0开始的JButton[][] bords = new JButton[20][20];
    3.2.2 一维数组// 副界面的9个按键JButton[] numbers = new JButton[9];
    数组的下标是从0开始的。
    3.2.3 十字链表解题时用。
    四、GUI界面设计介绍构造菜单并添加监听器。

    使用GridLayout将centrePanel面板分成9*9的格子,两个for循环各添加一个按键,并且设置每个按键属性,可通过监听器修改每个按键的背景、前景、字体。
    使用BorderLayout将JFame布局分为四块,主窗口上边显示菜单栏bar,中间显示9*9的数独题目centrePanel,下面显示JLabel欢迎提示bottomPanel,右边显示游戏的时间。
    副界面显示为3*3的按键,一个for循环各添加一个按键,并且设置每个按键属性,可通过监听器修改每个按键的背景、前景、字体,用来在主界面填入数字。
    最主要的两个监听器,用来控制主窗口和副窗口之间的转换,不同模式下,监听器会有不同的动作设置字体,两个窗口切换时还添加了渐渐出现和消失动画。
    frame 中的每个按键的监听器当点击按键时,先判断当前是否已经完成游戏,如果已经完成则按键不会有响应,如果没有完成则判断当前是否是用户自定义题目模式或正常游戏模式下是否已经有题目给出的数字,如果不是用户自定义题目模式且按键不是题目初始化时已经给出的数字则会调出副窗口,如果是用户自定义题目模式,判断k是否符合数独规则,若符合则显示为红色,提示可填写,若不符合数独规则,则显示为黑色,点击后窗口消失,但是主窗口中对应的位置不会改变,如果是和主窗口中的数字相同,则改变颜色为红,继续提示可点击。
    subframe中的每个按键的监听器当点击时,先判断当前是否已经完成游戏,如果已经完成则按键不会有响应,如果没有完成则判断是否为用户自定义题目模式,如果是则再判断,如果为已经填过的数或者不符合数独游戏规则,那么将主窗口对应的位置清空,否则填入对应的黑色的数字。如果是非用户自定义题目模式,在主窗口题目的数字为黑色,填入相对应的数字为红色,点击的数字和主界面相同,那么主界面的数会清空。
    时间控制的面板设计,在类TimeKeeper中,时间格式为 “00:00:00”,通过字符串实现,当小时为双位数时,将原数值转换为字符串,当小时为一位数时,在字符串前添加一个”0”,从而保证了字符串的长度始终保持一致。
    五、测试游戏运行时的界面
    选择数字填入的副窗口
    开始快速游戏游戏的窗口显示游戏的名”SUDOKU”,在”Game”菜单的”Difficulty”中可以更改游戏的难度,共设3个级别的难度,分别为”easy”,”normal”,”hard”,默认级别为”normal”。

    设置好难度后就可以开始游戏了,在”File”菜单中点击”QUICK PLAY”,将随机生成的本难度数独题目。

    QUICK PLAY显示界面如下:

    窗口的面板中出现了随机产生的题目,开始游戏并右边开始计时,用户能点击按键选择填入的数字。
    填入数字,单击要填入的地方然后将会出现一个有九个数字的小窗口。单击数字即可填入,会有一个小窗口由小而大的显示出来,如点击左上角:

    红色为提示当前位置中行、列、宫里没有的数字,黑色的按键点击后将不会改变填入的数值。
    单击想填入的数字,比如1,此时填入数字的窗口将由大变小到消失。

    为区分用户填入的数字和题目给出的数字,用户填入的数字为红色,题目给出的数字为黑色。其中已经填过的数字只需要在副窗口中再点击相同的数字就可以清除。
    当游戏结束时会祝贺玩家,点击确定后时间暂停:

    在”File”菜单中点击”PLAY AGAIN”,将清空本题目中填过的数据,重新开始。

    开始闯关模式游戏:在”Game”菜单的”Difficulty”中可以更改游戏的难度,共设3个级别的难度,分别为”easy”,”normal”,”hard”,默认级别为”normal”。

    设置好难度后就可以开始游戏了,在”File”菜单中点击”PLAY GAME”。

    将弹出以下对话框:

    输入一个关卡,输入数字为1到9,如果填入空白,则提示:

    如果为含有字母则会提示并回到主界面:

    如果数字不符合范围,则需要继续重新填写,点右上角的标记可以退出对话框。
    输入正确则会生成预先设定好的题目:

    接下来和快速游戏相同。
    用户录入题目在”File”菜单中点击”DIYPuzzle”,点击后提示进入用户录入题目模式,按键可以点了,主要用于智能解题。


    智能解题在快速游戏时,在闯关模式游戏时,在自定义题目之后,在”Game”菜单中点击”Answer”,都会智能解题。

    无解时给出提示:

    唯一解是直接给出答案;多解时给出其中一个答案并提示:


    游戏中途保存在游戏中途,在”Game”菜单中点击”SAVE”,将自动保存游戏。

    载入游戏存档在游戏中途,在”Game”菜单中点击”LOAD”,将自动读取游戏存档。

    关于游戏信息在”About”菜单中点击”Info”,获取游戏的主要信息。

    六、难点解决在实验中当然了遇到了不少问题:
    首先是那个9*9的按钮不能正确显示,只能看到一个,通过查看代码,用了比较久的时间,发现中心面板并没有加到frame上才出现这个问题,然后这个问题就解决了。
    然后遇到的另外一个比较难纠错的问题,问题出在团队的合作上,开始快速游戏就会随机生成题目,这时问题来了,有时候会溢出,一开始以为是我界面这边的算法错了,于是就开始检查,删剩下主体部分,后来发现是另一个组员做的类写错了,由此可知,组员之间的交流是很重要的。
    其次遇到个比较难解决的问题是游戏时间一直显示为0,不会自动加,TimeKeeper的类中的其中一个方法我写成了public void TimeKeeper(){},这个当然是不对的,我重新编写这个部分还是没发现到这个问题,最后在同学的帮助下解决了问题,应当改为public TimeKeeper(){},有返回值就不对了,void 和空是不一样的。
    另一个比较难解决的问题是闯关模式选择关卡时的对话框。有种错误是一直输入不能关闭,因为没有把输入的值转换成正确的数据类型再判断导致死循环,增加个类型转换语句就可以解决问题了;有种错误是点击关闭的时候,总会提示NullPointerException,于是我在原本Exception-Handling的NumberFormatException基础上在添加个try-catch语句来排除这个错误,结果能够顺利关闭窗口了。
    1 评论 4 下载 2020-01-01 14:50:20 下载需要15点积分
  • 基于C#和Sql Server的网上书店管理系统

    摘 要本系统是建立在 Windows 平台上,基于 B/S 结构的一个网上书店。通过这个网上书店,可以实 现简单的电子商务功能。
    整个网站风格一致,较为美观,有完善的导航机制。普通用户从前台首页进入,员工用户从后台首页进入(也即实现两个模板页)。用户使用的页面上必须有书籍广告轮播。
    前台部分由普通用户使用,后台部分由员工使用,任何对新闻、产品等的修改,只可从后台登录操作。
    1 绪 论1.1 概述本文以网上书店项目系统建设为开发为背景,以电子商务系统为原型,论述了管理信息系统的概念、结构及系统开发的基本原理和方法,全文共分为绪论、系统分析、系统设计、系统实施、开发总结等。
    1.1.1 问题的提出数据库是一门研究数据管理的技术,始于20世纪60年代,经过40多年的发展,现在已经形成了理论体系,成为计算机软件的一个重要分支。数据库技术体现了当代先进的数据管理方法,使计算机的应用真正渗透到国民经济各个部门,在数据处理领域发挥着越来越大的作用。
    随着社会的发展和经济时代的到来,管理信息系统在各行各业都越来越重要,特别是教育事业。在经济发达的国家,许多教育机构(如公办、私立、培训机构等),都投入了大量的资金开发 MIS 系统,以求在将来激烈的竞争中立于不败之地。在我国,民办教育是新兴的一个行业,是随着改革开放和市场经济的发展根据中国特有的国情发展起来的,特别是中国民办教育促进法的出台,从一定的程度上规范和促进了中国民办教育的发展,这是一个很有发展前途的新兴产业,但是同发达国家相比,我国的民办教育行业的信息技术的应用程度还很低,只有在大城市中发展较早、规模较大的民办院校中 才使用计算机进行大规模操作,从各方面提高工作效率,取得良好的社会和经济效益,而一些新兴的、规模较小的民办机构还没有全部具备这种功能。因此可见,随着我国民办教育的迅速发展,信息技术在其上的应用会更加地广泛和深入。
    1.1.2 本实训项目的意义首先,是一个简单的电子商务模型。一个书店,如果还采用原始的手动管理,那么将会极大的影响书店的工作效率,采用一种专门的管理系统,那么将会解放人力资源,提高学校的工作效率。其次,方便用书对图书的查询,本系统采用B/S模式,不需要安装任何附加的软件,只需要一个浏览器,就可以完成用户对自己喜爱的图书查询。
    1.2 开发环境与工具介绍
    开发环境

    windows 10Visual Studio 2015SqlServer 2016
    Web服务器

    IIS
    开发语言

    C# ASP.NET
    开发工具

    Visual Studio 2015
    网络协议

    TCP/IP

    1.2.1 C#简介C#是微软公司发布的一种面向对象的、运行于.NET Framework 之上的高级程序设计语言。并定于在微软职业开发者论坛(PDC)上登台亮相。C#是微软公司研究员 Anders Hejlsberg 的最新成果。C#看起来与 Java 有着惊人的相似;它包括了诸如单一继承、接口、与 Java 几乎同样的语法和编译成中间代码再运行的过程。但是C#与Java有着明显的不同,它借 鉴了 Delphi 的一个特点,与 COM(组件对象模型)是直接集成的,而且它是微软公司 .NET windows网络框架的主角。
    1.2.2 SqlServer 简介SQL Server是由Microsoft开发和推广的关系数据库管理系统(DBMS),它最初是由 Microsoft、Sybase和Ashton-Tate三家公司共同开发的,并于1988年推出了第一个OS/2 版本。Microsoft SQL Server近年来不断更新版本,1996年,Microsoft 推出了SQL Server 6.5版本;1998年,SQL Server 7.0版本和用户见面;SQL Server 2000 是Microsoft 公司于2000年推出,目前最新版本是2015年份推出的SQL SERVER 2015。
    1.2.3 IIS 简介iis 是 Internet Information Services 的缩 写,意为互联网信息服务,是由微软公司提供的基于运行 Microsoft Windows 的互联网基本服 务。最初是Windows NT版本的可选包,随后内置在 Windows 2000 、 Windows XP Professional和Windows Server 2003 一起发行,但在Windows XP Home版本上并没有IIS。IIS是一种Web(网页)服务组件,其中包括Web 服务器、FTP服务器、NNTP服务器和 SMTP服务器,分别用于网页浏览、文件传输、新闻服务和邮件发送等方面,它使得在网络(包 括互联网和局域网)上发布信息成了一件很容易的事。
    2 系统需求分析与设计2.1 用户需求分析2.1.1 用户需求通过对网上书店日常管理中的内容、广告、用户、管理员、新闻等相关内容进行分析,完成具有公司介绍、产品浏览、产品管理等相关功能的小型数据库管理应用系统。
    2.1.2 系统功能需求
    产品管理
    新闻
    产品浏览
    产品管理
    用户管理
    购物
    订单管理

    2.1.3 系统性能需求
    操作简便、快捷
    具备一定的安全性
    具有良好的用户体验

    2.1.4 数据分析系统中角色主要有:用户、类别、新闻、管理员

    用户:账号、姓名、性别、类型、登录密码等
    类别:青春文学、小说、悠闲、所有等
    新闻:类型、关键字、时间等
    管理员:管理员号、管理员名称、登录密码等

    2.2 功能模块图及分模块功能描述2.2.1 系统的功能模块图
    2.2.2 系统功能模块简介
    3 系统实施3.1 登录模块的开发系统登录

    注册模块

    如上图所示,系统的登录模块有二种登录方式,分别是:用户、管理员。从不同的登录方式登录后,会有不同的功能提供给用户。该登录模块采用的是Ajax技术,实现无刷新的登录验证,登录 信息错误时将会弹出图3.3-2所示的提示框,信息正确则自动跳转到系统主界面。

    3.2 系统主页模块的开发
    系统主页主要是为登录后的用户提供方便的访问服务,有前往系统各个模块的快捷方式。
    3.3 用户管理模块的开发用户管理——录入用户信息

    用户管理——个人信息

    用户管理——修改密码

    用户管理中有两个功能,分别是个人信息和修改密码。个人信息中可以查看、修改用户自己的相关信息,修改密码可以修改用户的登录密码。
    3.4 管理员后台管理模块的开发新闻管理—录入新闻、查看新闻、编辑新闻、增加新闻、删除新闻

    书籍管理

    分类管理

    用户管理—修改权限 搜集用户详细信息,修改用户详细信息

    3.5 管理员管理模块的开发
    3.6 系统测试系统运行正常,各种功能使用方便,但是在浏览器兼容性方面有些问题,虽然在 Chrome 和 FireFox下未出现问题,但是在IE下表现有些不尽人意。
    4 系统说明4.1 开发环境本系统的是在ASP.NET平台下开发的,系统的后台数据库为SQLSERVER数据库; 因此在使用本系统前,应先安装framework框架和SQLSERVER数据库,否则该系统无法运行。
    4.2 系统安装、配置与发布应用程序的步骤系统的原文件直接复制到机器上后,在网上书店管理系统上建立一个的数据库后,系统便可以运行。
    5 总 结这次的网上书店管理系统实训作业使我感触颇深,通过做这个小型的项目,我思考了很多。此次项目给我们提供了一个很好的契机,以此为动力,完成一个自己独自开发的小型项目。我们所做的课题是网上书店管理系统,整个过程没有想象中的那么容易,但好在最终还是完成了这次的作业。自己对整个课程设计工作的评价是:项目一般,水平有点一般,但付出却不是减半的。在整个过程中,我又把一本开发工具的书仔细的看了一遍。
    经过了一段时间努力,最近终于把项目做得完善了,在这过程中很想谢谢和我一起自习的朋友,你们的鼓励和帮助让我一直坚持着做下来。谢谢老师给的指点,我们会在接下来的时间把缺少的那些功能逐渐完善。
    5 评论 54 下载 2019-06-15 12:59:26 下载需要11点积分
  • 基于PHP和MySQL数据库实现的学生成绩管理系统

    一、项目介绍1.1 课程设计的题目学生成绩管理系统
    1.2 系统的总体功能描述1.2.1 基本要求学校希望建立一个学生成绩在线公布系统,对学生成绩信息进行存储、管理和发布,并能通过分类、查询、统计等操作从数据库中获取有效信息,在方便学生了解自己各科考试成绩的同时,各院系可对学生成绩有直接和明确的掌握。
    1.2.2 基本功能
    系统设置:考试科目、等级分值等参数的设置、权限设置、更改密码等
    学生查询:通过学号和密码,学生可查询每学期各科成绩
    成绩录入:提供学生成绩的添加功能,并能从表格和其他数据库文件中导入信息
    信息更新:提供学生成绩信息的删除和修改和功能
    信息统计:按院系、专业、班级分类汇总、统计。可查询学生的成绩分布,不及格人数,最低分、最高分和平均分
    信息打印:主要提供成绩的分类打印功能

    1.2.1 登录功能
    输入管理员,老师,学生的id和密码会登录到不同界面,与之对应有不用的功能和权限。
    1.2.2 管理员功能
    添加考试

    在输入框输入需要添加的考试科目,点击添加,完成添加考试科目内容
    删除分数

    输入要删除分数的名字和对应的科目,点击删除即可删除分数
    修改分数

    输入需要需改分数的姓名和对应的科目对应的新成绩,点击修改完成修改分数功能
    修改密码

    管理员可以修改任何id的密码
    1.3 开发工具本系统后台数据库采用MySQL 数据库,该数据库系统在安全性、准确性和运行速度方面有绝对的优势,并且处理数据量大,效率高;前台sublime text作为主要开发工具,使用B/S模式,开发简单。
    1.4 开发环境
    编辑器:Sublime Text
    系统开发语言:HTML + CSS + PHP
    数据库:MySQL

    二、需求分析2.1 概括描述随着在校大学生人数的不断增加,教务系统的数据量也不断的上涨。学校工作繁杂、资料重多,虽然各类管理信息系统已进入高校,但还未普及,而对于学生成绩管理来说,目前还没有一套完整的、统一的系统。因此,开发一套适和大众的、兼容性好的系统是很有必要的。
    2.2 用户特点本系统有管理员,老师,学生。系统的管理员主要是对学生信息以及学生成绩进行一些操作。这些操作包括对学生信息的添加、对学生成绩的录入以及对学生成绩进行分析等。老师对学生成绩的录入,自己密码的修改,学生查询自己的成绩和统计自己的成绩,修改自己密码。
    2.3 可行性分析目前,随着办公信息化的开展,高校的扩招,新生入学以及期末考试结束后,学校都需要对一些繁琐的流程进行管理,通过一个基于B/S架构的管理系统,可以很好的将这一个过程进行化繁为简。此项目具有普遍性,能够应用于很多学校。因此,该类型系统可以大量投入使用。
    2.4 数据项管理员id,密码,老师id,密码,学生id密码,考试科目,考试成绩
    2.5 数据结构
    管理员:权限最高,管理员ID,管理员登录密码
    老师:老师ID,老师登录密码
    学生:学生ID,学生密码,学生考试科目,考试成绩

    2.6数据存储管理员ID,管理员登录密码,老师ID,老师登录密码,学生ID,学生密码,学生考试科目,考试成绩
    三、数据库概念结构设计3.1 局部ER图
    管理员(ID,密码)
    老师(ID,密码)
    学生(ID,密码,科目,成绩)

    四、数据库逻辑结构设计4.1 关系模式
    管理员(ID,密码)
    老师(ID,密码)
    学生(ID,密码,科目,成绩)

    4.2 存储过程信息->数据库
    4.3 触发器建立触发器以实现分数删除功能,当删除一个学生的分数时将此学生的相关信息全部删除。
    4.4视图这部分主要是考虑使用方便性和效率问题,主要借助视图手段实现。
    4.5索引使用经典存取方法,建立索引:

    学生成绩经常用到而且按其排序,在学生成绩名称属性建立索引。
    用户ID是本软件的重要的数据,在ID属性建立索引。

    五、应用系统功能结构图
    六、各功能模块程序流程图及其说明6.1 添加考试输入框获取用户输入的考试科目,通过post方法发送到服务器,添加到数据库
    6.2 删除模块输入框获取用户输入要删除的分数,通过post方法发送到服务器,MqSQL数据库匹配对应的分数,然后删除
    6.3 修改密码模块通过更新数据库达到修改密码的功能
    6.4 查询模块查询对应的分数,使用对应的MySQL语句
    七、程序源代码及其说明7.1 登录
    7.2 查询

    7.3 服务器端对数据库的读取和写入操作采用JDBC进行数据库的连接与操作。
    7.4 客户端与服务器端的通信采用Socket进行通信。
    7.5 系统主界面管理员主界面

    学生主界面

    教师主界面

    八、总结课程设计中遇到的主要问题和解决方法;创新和得意之处;课程设计中存在的不足,需进一步改进的设想;课程设计的感想和心得体会。
    8.1 主要问题和解决方法界面的排版问题,还有用户输入的数据怎样传给数据库和php处理。
    排版问题使用css的定位解决,绝对定位能让控件定位到我想要的地方。数据传输使用post方法,在php要使用_POST+name接受控件输入的内容。
    8.2 创新和得意之处系统界面简单单调,但是功能齐全,开发容易,使用XAMPP集合环境,不需要单独安装数据库和web服务器,使用起来方便简单。
    8.3 不足之处功能过域简单,更加复杂的功能没有实现,系统存在SQL注入等安全问题,界面也不够美观,进一步的学习和研究,学习更多css、html、php、mysql,进一步掌握安全问题,维护更好的网站。
    8.4 心得体会在这次课程设计中,重点是要掌握数据库设计方法。数据库设计特点:三分技术,七分管理,十二分基础数据。在数据库建设中不仅涉及技术,还设计管理。要建设好一个数据库应用系统,开发技术固然重要,但相比之下则管理更重要。
    进行数据库的设计首先必须了解与分析用户需求。需求分析是整个设计过程的基础,是最困难、最耗时的一部分。需求分析做的不好,甚至会导致整个数据库设计返工重做。需求分析的任务是通过详细的调查现实世界要处理的对象,充分了解用户的各种需求,然后在此基础上确定系统功能。系统还必须充分考虑今后可能的扩充和改变。
    概念结构设计是整个数据库设计的关键,他通过对用户需求进行综合、归纳与抽象,形成一个独立与具体DBMS的概念模型。逻辑结构设计是将概念结构转换为某个DBMS所支持的数据模型,并对其进行优化。概念结构的特点:能真实、充分的反映现实世界,包括失误和事物之间的联系,能满足用户对数据的处理要求,是对现实世界的一个真实模型;易于理解,从而可以用它和不熟悉计算机的用户交流意见,用户的积极参与是数据库设计成功的关键;易于更改,当应用环境和应用要求改变时,容易对概念模型修改和扩充;易于向关系、网状、层次等各种数据模型转换。
    本系统主要针对的用户是学生、教务员和老师。该设计的优点是:不同用户的功能都单独存储在数据库中相应的表中使他们可以很方便地满足自己的需求。不足是:存在部分数据冗余,不利于该数据库向大型数据库扩展。但该设计基本能满足三类的用户的需求,完成简单的功能。时间有限,部分功能还没有实现,有待完善!
    通过将近一个星期的设计,我对数据库在生活中的广泛应用有了更深的体会,同时也加深了对MySQL的认识,了解了相关的SQL的知识,尤其是对数据库的原理与开发程序的应用有了更进一步的认识和了解。 课程设计是我们数据库专业课程知识综合应用的实践训练,在课设中也遇到了很多问题,经过向老师询问和跟同学们交流,认真的思考,积极解决问题,终于解决了那些问题。在解决问题的过程中,我也学到了很多知识,更是让我把课本的知识应用到实际之中,让我了解了我们学习的知识有什么用,增强我的自信心和学习的动力。同时感谢老师和同学在课程设计过程中的帮助!
    九、参考文献[1] 王珊.数据库技术与应用[M].北京:清华大学出版社,2005.
    [2] 姚卿达.数据库设计[M].北京:高等教育出版社,1987.
    [3] 戴维尔.JavaScript程序员教程[M].电子工业出版社.2010.333-334.
    [4] 武欣 PHP和MySQL Web开发(原书第4版)机械工业出版社9787111262817
    3 评论 243 下载 2018-11-30 19:10:01 下载需要10点积分
  • 基于Android平台的个人理财软件的设计与实现

    摘要个人理财管理系统是基于Android系统开发的一款手机应用程序。它主要是为了满足人们在快节奏的生活中可以随时记下自己的收支情况的需求。个人理财管理系统与传统的记账方式相比,体现了它的便捷性、安全性及可扩展性。系统采用Eclipse+ Android Developer Tools作为开发工具,以SQLite为数据库。系统功能包括:用户账户的注册、用户切换、用户名密码修改、账户删除,语音识别记账,收入信息的增删改,支出信息的增删改,收入类型的增删,支出类型的增删,收入信息分类统计,支出信息分类统计,收入支出总额统计,数据还原、清空等等。系统具有界面简洁鲜明、功能便捷易用、操作人性化等特征。
    关键字:收支管理;Android;SQLite
    AbstractPersonal financial management system is based on the Android system developed a mobile application.It is mainly to meet those people who want to write down their income and expenditure more quickly and conveniently in the fast-paced life. Compared with the traditional method, Personal financial management system reflects more convenience,security and scalability. Especially in this day and age, people are on the side of the phone. System uses Eclipse and Android Developer Tools as a development tool and SQLite as the database. Android technology is now very mature, we can easily develop.Personal Financial Management System of the main functional modules,including: user account registration, user switching, username password changes, account deletion, Income information management,Expenditure information management,voice recognition accounting,Type of income management, Expenditure Type Manager, Income information classification statistics, expenditure information classification statistics, total income and expenditure statistics,Data reduction, data emptied and so on. The system has a simple and clear interface, easy-to-use features, user-friendly operation and other features.
    Keywords: Revenue and expenditure management; Android;SQLite
    1 绪论1.1 选题背景出门口诀“伸手要赏钱”分别代表着身份证、手机、钥匙、伞、钱。在后PC时代的今天,手机成为最重要的移动终端,是我们出门出行的必携物。以前,或许我们的手机只能为我们提供基本通信功能,而随着Android系统的诞生,我们的生活迎来了一场不亚于“工业革命”带给我们的巨大变化。基于Android系统的手机处理各类信息的能力得到了质的提升。而Android这个开源的操作系统,将享受这项优质服务的权利给了所有愿意使用它的人。?Android从08年9月的1.1版到去年的L版,一路走来,从基本走向优化,全方面地为我们提供了出色的网络、多媒体、通讯、娱乐等功能。这匹黑马,从诞生伊始到去年7月的“全球市场占有率达81.9%”,告诉我们它的出现是多么受人们欢迎。它备受追捧的原因不外乎它完全的对第三方软件开发商和我们这些开发者的开发。我们可以在它上面实现无限自主的“自定义”。它宛如一张白纸,我们可以在上面随意画出自己想要的东西。它,贵在给予了我们自由。
    1.2 课题研究的目的和意义随着高速经济化,我们的生活节奏越来越快。我们忙东忙西,总会容易忽略忘记了一些生活细节,比如收支管理。为了更好的释放一些时间来享受我们的生活,我们期待有这么一款软件来帮助管理这些小数据。建立在Android操作系统上的个人理财系统,方便我们随时随地地记录着这些零散的数据,从此我们不必再为收支费心,清心地查看数据统计结果是我们唯一要做的事。
    1.3 国内外现状和发展趋势安卓在手机上的应用使得手机的功能有了很大改善,这使得越来越多的入主要依靠手机查询大量信息,而用户们不断提高的需求也决定了越来越多的基于安卓平台的应用软件及系统的产生。
    若是基于安卓平台的个人理财系统得到广泛推广,人们能从该应用显而易见的了解到个人的财务状况,明确的使用自身钱财,了解到日常中支出比例,调整支出,正确理财。

    开放性手机平台:android是Google开发的基于Linux平台的开源手机操作系统。Google通过与运营商、设备制造商、手机公司和其他有关各方结成深层次的合作伙伴关系,希望借助建立标准化、开放式的智能手机操作系统,在移动产业内形成一个开放式的生态系统
    网络集成性很高:涵盖了生活中各个方面的网络应用,对长期使用网络、信息依赖度比较高的人群很合适
    Android具备创新性自从:Google开发出Android后,许多人认为其技术可信度要比其它操作系统略胜一筹,但这并不是用户购买Android智能手机的唯一原因。人们认为Android是一种相对较新的、又较为成熟的技术,在达到巅峰之前还有很大发展空间
    Android平台在数量上逐渐主宰市场:市场分析机构NPD发布的数据显示,2014年4-6月份发售的智能手机中,33%为Android手机,而RIM手机发售比例为28%,iPhone为22%
    Android在其它领域的拓展:android不仅促进了手机产业的发展,它的全面计算服务和丰富的功能支持,已将应用拓展到手机以外的其他领域。Android平台的通用性可以适用于不同的屏幕、有线和无线设备。Android的系统和应用程序开发人员将更多的涉足多媒体、移动互联网设备、数字视频和家庭娱乐设备、汽车、医药、网络、监测仪器和工业管理、机顶盒等新领域

    2 设计开发所用到的工具和技术2.1系统开发工具个人财务管理系统的开发及运行环境如下所述:

    操作系统:Windows7
    JDK环境:Java Development Kit version=1.7.0_45
    开发工具

    Eclipse version=4.2.0Android Software Development Kit version=4.4.2Android Developer Tools Build: v22.3.0-887826
    开发语言:Java、XML
    数据库软件:SQLite
    运行平台:Windows
    虚拟机:720P(1080x720)

    2.2 Android的介绍Android是一种基于Linux的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由Google公司和开放手机联盟领导及开发。尚未有统一中文名称,中国大陆地区较多人使用“安卓”或“安致”。
    Android操作系统最初由Andy Rubin开发,主要支持手机。2005年8月由Google收购注资。2007年11月,Google与84家硬件制造商、软件开发商及电信营运商组建开放手机联盟共同研发改良Android系统。
    该平台由操作系统、中间件、用户界面和应用软件组成。它采用软件堆层(Software Stack,又名软件叠层)的架构,主要分为三部分。底层以Linux内核工作为基础,由C语言开发,只提供基本功能;中间层包括函数库Library和虚拟机Virtual Machine,由C++开发。最上层是各种应用软件,包括通话程序,短信程序等,应用软件则由各公司自行开发,以Java作为编写程序的一部分。不存在任何以往阻碍移动产业创新的专有权障碍,号称是首个为移动终端打造的真正开放和完整的移动软件。
    android分为四个层,从高层到低层分别是应用程序层、应用程序框架层、系统运行库层和linux核心层。
    蓝色的代表java程序,黄色的代码为运行JAVA程序而实现的虚拟机,绿色部分为C/C++语言编写的程序库,红色的代码内核(linux内核+driver)。在Application Framework之下,由C/C++的程序库组成,通过JNI完成从JAVA到C的调用。

    2.3 Eclipse的介绍eclipse-galileoEclipse 是一个开放源代码的、基于 Java 的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse 附带了一个标准的插件集,包括 Java 开发工具(Java Development Tools,JDT)。
    Eclipse最初是由IBM公司开发的替代商业软件Visual Age for Java的下一代IDE开发环境,2001年11月贡献给开源社区,现在它由非营利软件供应商联盟Eclipse基金会(Eclipse Foundation)管理。 2003年,Eclipse 3.0选择OSGi服务平台规范为运行时架构。 2007年6月,稳定版3.3发布。2008年6月发布代号为Ganymede的3.4版。2009年7月发布代号为GALILEO的3.5版。
    Eclipse是著名的跨平台的自由集成开发环境(IDE)。最初主要用来Java语言开发,但是目前亦有人通过插件使其作为其他计算机语言比如C++和Python的开发工具。Eclipse的本身只是一个框架平台,但是众多插件的支持使得Eclipse拥有其他功能相对固定的IDE软件很难具有的灵活性。许多软件开发商以Eclipse为框架开发自己的IDE。
    Eclipse 最初由OTI和IBM两家公司的IDE产品开发组创建,起始于1999年4月。IBM提供了最初的Eclipse代码基础,包括Platform、JDT 和PDE。目前由IBM牵头,围绕着Eclipse项目已经发展成为了一个庞大的Eclipse联盟,有150多家软件公司参与到Eclipse项目中,其中包括Borland、Rational Software、Red Hat及Sybase等。Eclipse是一个开发源码项目,它其实是 Visual Age for Java的替代品,其界面跟先前的Visual Age for Java差不多,但由于其开放源码,任何人都可以免费得到,并可以在此基础上开发各自的插件,因此越来越受人们关注。近期还有包括Oracle在内的许多大公司也纷纷加入了该项目,并宣称Eclipse将来能成为可进行任何语言开发的IDE集大成者,使用者只需下载各种语言的插件即可。
    2.4 SQLite的介绍SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源的世界著名数据库管理系统来讲,它的处理速度比他们都快。SQLite第一个Alpha版本诞生于2000年5月。 至2015年已经有15个年头,SQLite也迎来了一个版本 SQLite 3已经发布。
    3 系统需求分析3.1 需求分析生活中记录日常花销、收入总是琐碎累心的。特别是纸质记录,除了携带不方便外,还很容易丢失损毁。
    这个时候,若是我们身边常带的手机上有这么一款可以随时记录收入和支出的软件,因为在记录收支的同时,会有一些注意事项需要标注,比如欠钱还钱注意事项。在对各项数据进行记录后,用户会希望看到对各类数据的统计,所以需要满足基本需求的统计模块。为了保证数据安全,增设账户模块。为了区分各用户的操作习惯,所以增设收入支出类型管理模块和系统设置模块。
    3.2 可行性分析3.2.1 系统可行性可行性分析实在目前市场己有的类似系统调查的基础上,辩证新系统的研发是否具备开发必要性和可能性,对新系统的研发从技术、经济、社会因素等多个方面进行相关的分析和研究,以避免造成不必要的投资失误,保证和提高新系统开发成功的把握。可行性研究的目的就是以最小的时间、金钱代价确定疑难问题是否能够妥善解决。
    3.2.2 技术可行性此系统需要在Android手机操作系统上运行,用Eclipse进行开发,数据库我选用轻量级的SqLite。开发所需的软件技术成熟稳定,且支持Android系统的手机也分布广泛,可以完全满足所以的开发需求。
    至于自己在Android方面虽然没有基础,鉴于之前有Java编程经验且时间充裕,有足够时间来学习空白的知识。
    3.2.3 经济可行性从市场经济来看,近年来,国人生活品质提升,逐渐重视生活品质,手机应用便成了人们生活的一部分。当今社会己步入了一个全新的信息时代,人类的每个活动都和“信息”紧密的联系在一起,小至个人的衣食住行,大及国家大事新闻发布,都依与信息的传播与发布,而社会中最活跃的,在市场经济高速发展的现在,手机应用普遍化,生活化,低端化成了不可阻挡的趋势。
    从技术经济来看,由于SQLite数据库和Android SDK都是开源的免费的开发学习工具,而且本系统使用灵活方便,技术也不是很复杂,开发周期较短,因此开发成本较低。市场前景非常看好,所以说技术经济方面来看,本项目也是可行的。由此可以看出本系统开发所产生的效益将大于投入,所以开发本项目是可行的、必要的。
    综上所述,个人理财系统充分利用了软硬件资源,技术成熟,成本低廉,操作简单,管理方便,使理财记账摆脱空间的限制,实现自动化处理和信息化管理,因此,本系统的实施是可行的。
    3.3 系统功能模块
    用户管理:可以设置当前用户
    类别维护:用户可以添加日常收入、日常支出的类别,并且可以删除相应的类别,填写类别详细
    日常收入:用户可以按照日常收入日期、金额、类别、备注进行数据的增添
    日常支出:用户可以按照日常支出日期、金额、类别、备注进行数据的增添
    收入支出统计:按照一定的数据查询条件,用户可以对数据进行统计


    4 系统总体设计4.1 系统总体设计分析系统设计是系统开发过程中的核心,从需求出发,总体上描述系统架构应该包含的组成要素。系统总体设计尽可能模块化,描述了各个模块之间的关联。模块化是一种很重要的设计思想,把一个复杂的系统分解为一些规模较小、功能简单的、更易于建立和修改的部分。一方面,各个模块具有相对独立性,可以分别加以设计实现;另一方面,模块之间的相互关系则通过一定的方式予以说明。各模块在这些关系的约束下共同构成一个统一的整体,完成系统的功能。
    总体设计的核心内容就是依据需求分析定义的功能,合理、有效地实现系统中定义的各种需求,包括模块设计、数据库设计等。
    4.2 系统流程图根据系统分析以及功能需求,系统的基本流程可以描述为:主界面→选择各子功能模块,如下图所示:

    4.3 系统特点
    目的明确:理财就是以管钱为中心,通过抓好赚钱、生钱、护钱,三个环节,管好自己手中的现金流动,让资产在保值的基础上,实现稳定持续的增长
    功能齐全:系统覆盖了理财所需要的功能,收支,统计
    适应性强:系统采用基于模型的设计思,用户的特点抽象出管理模型,根据模型进行系统设计,使系统具有很好的开放性的拓展性,能够高效率地适应各用户群体的需求

    4.4 数据库设计4.4.1 tb_account(账户表)tb_account用于管理系统各个用户信息。_id为用户的唯一标识,为表的主键,也为其他表的_id相对应。Username和pwd分别代表着用户名和密码。这两个是用户后期可以修改的。
    账户表中存在着一个特殊的用户:默认用户。它的用户名密码用户不可见。用户在没有登陆的情况下,数据保存在这个用户下方。



    字段名
    数据类型
    是否主键
    描述




    _id
    Integer
    Y
    用户id


    USERNAME
    VARCHAR(20)
    N
    用户名


    PWD
    VARCHAR(50)
    N
    密码



    4.4.2 tb_income(收入信息表)TYPE_ID与tb_itype表的type_id对应。No为收入信息的编号,不同用户的收入信息存入数据库的时候,都是以no=1为起始的,进而往后递增。



    字段名
    数据类型
    是否主键
    描述




    _id
    INTEGER
    N
    用户id


    NO
    INTEGER
    Y
    编号


    MONEY
    DECIMAL
    N
    收入金额


    TIME
    DATE
    N
    收入时间


    TYPE_ID
    INTEGER
    N
    收入类别


    HANDLER
    VARCHAR(100)
    N
    放款方


    MARK
    VARCHAR(200)
    N
    备注


    PHOTO
    VARCHAR(200)
    N
    照片


    KIND
    VARCHAR(10)
    N
    类别



    4.4.3 tb_pay(支出信息表)TYPE_ID与tb_ptype表的type_id对应。No为收入信息的编号,不同用户的支出信息存入数据库的时候,都是以no=1为起始的,进而往后递增。



    字段名
    数据类型
    是否主键
    描述




    _id
    INTEGER
    N
    用户id


    NO
    INTEGER
    Y
    自增


    MONEY
    DECIMAL
    N
    支出金额


    TIME
    DATE
    N
    支出时间


    TYPE_ID
    INTEGER
    N
    支出类别


    ADDRESS
    VARCHAR(100)
    N
    消费地点


    MARK
    VARCHAR(200)
    N
    备注


    PHOTO
    VARCHAR(200)
    N
    照片


    KIND
    VARCHAR(10)
    N
    类别



    4.4.4 tb_ptype(支出类型表)tb_ ptype为支出类型表。由于每个用户都有对应的用户习惯,在添加支出信息时,收入类型的种类、使用频率都会有所不同。在此设计支出类型表,可以为用户提供修改支出类型的服务,根据自己的使用频率修改支出类型。



    字段名
    数据类型
    是否主键
    可否为空
    描述




    _id
    INTEGER
    N
    N
    用户id


    no
    INTEGER
    Y
    N
    类型编号


    type_id
    INTEGER
    N
    N
    类型id



    4.4.5 tb_itype(收入类型表)tb_itype为收入类型表。由于每个用户都有对应的用户习惯,在添加收入信息时,收入类型的种类、使用频率都会有所不同。在此设计收入类型表,可以为用户提供修改收入类型的服务,根据自己的使用频率修改收入类型。



    字段名
    数据类型
    是否主键
    可否为空
    描述




    _id
    INTEGER
    N
    N
    用户id


    no
    INTEGER
    Y
    N
    类型编号


    type_id
    INTEGER
    N
    N
    类型id



    5 系统详细设计与实现5.1 主界面设计快速记个人记账软件主界面,有4个Fragment页面。

    主界面使用4个Fragment和PopupWindow生成,部分代码:
    /** * 显示PopupWindow弹出菜单 */ private void showPopupWindow(View parent) { DisplayMetrics dm = parent.getResources().getDisplayMetrics(); int w_screen = dm.widthPixels; int h_screen = dm.heightPixels; // System.out.println("你的设备w_screen:" + w_screen + " h_screen:" + // h_screen); if (popWindow == null) { LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = layoutInflater.inflate(R.layout.popwindow_layout, null); popWinLayout = (LinearLayout) view.findViewById(R.id.popwindow); // 创建一个PopuWidow对象 float radiowith = w_screen / 480.0f; float radioheight = h_screen / 800.0f; popWindow = new PopupWindow(view, (int) (popWinLayout.getLayoutParams().width), h_screen / 4); } // 使其聚集 ,要想监听菜单里控件的事件就必须要调用此方法 popWindow.setFocusable(true); pop_voiceView = (LinearLayout) popWinLayout .findViewById(R.id.pop_voice); pop_quickView = (LinearLayout) popWinLayout .findViewById(R.id.pop_quick); pop_voiceView.setOnClickListener(this); pop_quickView.setOnClickListener(this); // 设置允许在外点击消失 popWindow.setOutsideTouchable(true); // 设置背景,这个是为了点击“返回Back”也能使其消失,并且并不会影响你的背景 popWindow.setBackgroundDrawable(new BitmapDrawable()); // 设置菜单显示的位置 int xPos = (w_screen - popWinLayout.getLayoutParams().width) / 2; popWindow.showAsDropDown(parent, xPos, 12); // popWindow.showAsDropDown(parent, Gravity.CENTER, 0); // 监听菜单的关闭事件 popWindow.setOnDismissListener(new OnDismissListener() { @Override public void onDismiss() { // 改变显示的按钮图片为正常状态 changeButtonImage(); } }); // 监听触屏事件 popWindow.setTouchInterceptor(new OnTouchListener() { public boolean onTouch(View view, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { // 改变显示的按钮图片为正常状态 changeButtonImage(); } return false; } }); } /** * 点击了“明细”按钮 */ private void clickFriendfeedBtn() { // 实例化Fragment页面 fragmentPage1 = new FragmentPage1(); // 得到Fragment事务管理器 FragmentTransaction fragmentTransaction = this .getSupportFragmentManager().beginTransaction(); // 替换当前的页面 fragmentTransaction.replace(R.id.frame_content, fragmentPage1); // 事务管理提交 fragmentTransaction.commit(); friendfeedFl.setSelected(true); friendfeedIv.setSelected(true); myfeedFl.setSelected(false); myfeedIv.setSelected(false); homeFl.setSelected(false); homeIv.setSelected(false); moreFl.setSelected(false); moreIv.setSelected(false); }
    此处省略类似的函数…
    private void clickPop_voiceBtn() { Intent intent = new Intent(MainActivity.this, AddPay.class);// 创建Intent对象 intent.putExtra("cwp.id", userid); intent.putExtra("cwp.voice", "");// 设置传递数据 startActivity(intent); } private void clickPop_quickBtn() { Intent intent = new Intent(MainActivity.this, AddPay.class);// 创建Intent对象 intent.putExtra("cwp.id", userid); startActivity(intent); } /** * 点击了中间按钮 */ private void clickToggleBtn() { showPopupWindow(plusImageView); // 改变按钮显示的图片为按下时的状态 plusImageView.setImageResource(R.drawable.toolbar_plusback); toggleImageView.setImageResource(R.drawable.toolbar_btn_pressed); } /** * 改变显示的按钮图片为正常状态 */ private void changeButtonImage() { plusImageView.setImageResource(R.drawable.toolbar_plus); toggleImageView.setImageResource(R.drawable.toolbar_btn_normal); }
    5.2 收入/支出插入数据界面添加软键盘,来添加收入/支出消费记录

    添加收/支部分代码,需要判断是添加模式还是修改模式,是添加收入还是添加支出:
    btnSaveButton.setOnClickListener(new OnClickListener() {// 为保存按钮设置监听事件 @SuppressLint("NewApi") @Override public void onClick(View arg0) { if (typemode == "add") { // 添加模式 String strMoney = txtMoney.getText().toString();// 获取金额文本框的值 if (type == "pay") { // 支出 if (!strMoney.isEmpty()) {// 判断金额不为空 // 创建InaccountDAO对象 PayDAO payDAO = new PayDAO(AddPay.this); // 创建Tb_inaccount对象 Tb_pay tb_pay = new Tb_pay( userid, payDAO.getMaxNo(userid) + 1, get2Double(strMoney), setTimeFormat(null), (spType.getSelectedItemPosition() + 1), txtAddress.getText().toString(), txtMark.getText().toString()); payDAO.add(tb_pay);// 添加收入信息 Toast.makeText(AddPay.this, "〖新增收入〗数据添加成功!", Toast.LENGTH_SHORT) .show(); gotoback(); } else { Toast.makeText(AddPay.this, "请输入收入金额!", Toast.LENGTH_SHORT).show(); } } else { // 收入 if (!strMoney.isEmpty()) {// 判断金额不为空 // 创建InaccountDAO对象 IncomeDAO incomeDAO = new IncomeDAO( AddPay.this); // 创建Tb_inaccount对象 Tb_income tb_income = new Tb_income( userid, incomeDAO.getMaxNo(userid) + 1, get2Double(strMoney), setTimeFormat(null), (spType.getSelectedItemPosition() + 1), txtInhandler.getText().toString(), txtMark.getText().toString()); System.out.println("money" + get2Double(strMoney)); incomeDAO.add(tb_income);// 添加收入信息 // 弹出信息提示 Toast.makeText(AddPay.this, "〖新增收入〗数据添加成功!", Toast.LENGTH_SHORT) .show(); gotoback(); } else { Toast.makeText(AddPay.this, "请输入收入金额!", Toast.LENGTH_SHORT).show(); } } } else { // 修改模式 if (type == "pay") { // 支出 if (!txtMoney.getText().toString().isEmpty()) {// 判断金额不为空 Tb_pay tb_pay = new Tb_pay(); // 创建Tb_pay对象 tb_pay.set_id(userid); // 设置userid tb_pay.setNo(Integer.parseInt(strno)); // 设置编号 tb_pay.setMoney(get2Double(txtMoney .getText().toString()));// 设置金额 tb_pay.setTime(setTimeFormat(txtTime .getText().toString()));// 设置时间 tb_pay.setType(spType .getSelectedItemPosition() + 1);// 设置类别 tb_pay.setAddress(txtAddress.getText() .toString());// 设置地点 tb_pay.setMark(txtMark.getText().toString());// 设置备注 payDAO.update(tb_pay);// 更新支出信息 Toast.makeText(AddPay.this, "〖数据〗修改成功!", Toast.LENGTH_SHORT).show(); gotoback(); } else { Toast.makeText(AddPay.this, "请输入收入金额!", Toast.LENGTH_SHORT).show(); } } else { // 收入 if (!txtMoney.getText().toString().isEmpty()) {// 判断金额不为空 Tb_income tb_income = new Tb_income();// 创建Tb_income对象 tb_income.set_id(userid);// 设置编号 tb_income.setNo(Integer.parseInt(strno));// 设置编号 tb_income.setMoney(get2Double(txtMoney .getText().toString()));// 设置金额 tb_income.setTime(setTimeFormat(txtTime .getText().toString()));// 设置时间 tb_income.setType(spType .getSelectedItemPosition() + 1);// 设置类别 tb_income.setHandler(txtInhandler.getText() .toString());// 设置付款方 tb_income.setMark(txtMark.getText() .toString());// 设置备注 incomeDAO.update(tb_income);// 更新收入信息 Toast.makeText(AddPay.this, "〖数据〗修改成功!", Toast.LENGTH_SHORT).show(); gotoback(); } else { Toast.makeText(AddPay.this, "请输入收入金额!", Toast.LENGTH_SHORT).show(); } } } } });
    软键盘部分代码:
    public void onKey(int primaryCode, int[] keyCodes) { Editable editable = ed.getText(); if (typemode.equals("ModifyInP")) { // 添加模式获取开始光标 ed.setSelection(editable.length()); } int start = ed.getSelectionStart(); if (primaryCode == Keyboard.KEYCODE_DELETE) { // 删除键 if (editable != null && editable.length() > 0) { if (start > 0) { editable.delete(start - 1, start); if (ed.getText().toString().indexOf(".") < 0) { a = true; } } } } else if (primaryCode == -7) { //隐藏键盘 hideKeyboard(); } else if (primaryCode == -8) { //小数点 if (start > 0 && a) { editable.insert(start, "."); a = false; } } else if (primaryCode == -9) { //语音识别 hideKeyboard(); dialogShowUtil.dialogShow("rotatebottom", "first", "", ""); } else { editable.insert(start, Character.toString((char) primaryCode)); } } }; public void showKeyboard() { //显示键盘 int visibility = keyboardView.getVisibility(); if (visibility == View.GONE || visibility == View.INVISIBLE) { keyboardView.setVisibility(View.VISIBLE); } } public void hideKeyboard() { //隐藏键盘 int visibility = keyboardView.getVisibility(); if (visibility == View.VISIBLE) { keyboardView.setVisibility(View.INVISIBLE); } }
    5.3 语音记账界面语音记账使用了百度语音识别api,通过响应用户的触发,调用api动态生成百度自定义的dialog来进行用户语音录音。当用户录入语音后,返回语音识别的数据,然后转为字符串并进行分析判断。目前主要以金额和类别来作为关键字来进行判断,首先通过将识别字符串通过跟收入/支出类别进行对比,如果存在相关类别即标记下当前录入的类别;如果没有当前这个类别,将会弹出自定义Dialog让用户去选择即使没有匹配的类别,是否依然要录入,如果“是”,该笔类型就会默认为“语音识别”类别,并会让用户去选择当前记录是“支出”还是“收入”,再去结合当前的金额录入数据;如果用户录入的类别同时存在于“收入”和“支出”之中,那么就会让用户去选择该笔记录是“收入”还是“支出”,在去结合当前的金额录入数据。另一方面,将识别字符串与自定义的列如“一”,“二”,“元”,“钱”…去对比,然后标记下这个金额的起始和结束位置,然后截取出来,并调用工具类把这个汉字的金额转变为阿拉伯数字。最后使用方法去判断当前是“添加模式”还是“修改模式”,然后再判断是“支出”还是“收入”,然后录入数据库。














    百度识别回调部分代码:
    mRecognitionListener = new DialogRecognitionListener() { // 百度识别返回数据 @Override public void onResults(Bundle results) { ArrayList<String> rs = results != null ? results .getStringArrayList(RESULTS_RECOGNITION) : null; if (rs != null && rs.size() > 0) { Recognition(rs.get(0)); // Toast.makeText(AddPay.this, rs.get(0), // Toast.LENGTH_SHORT).show(); } }};void VoiceRecognition() { // 百度语音识别// mResult.setText(null);mCurrentTheme = Config.DIALOG_THEME;if (mDialog != null) { mDialog.dismiss();}Bundle params = new Bundle();params.putString(BaiduASRDigitalDialog.PARAM_API_KEY, Constants.API_KEY); //百度语音api_keyparams.putString(BaiduASRDigitalDialog.PARAM_SECRET_KEY, Constants.SECRET_KEY);params.putInt(BaiduASRDigitalDialog.PARAM_DIALOG_THEME, //百度语音主题 Config.DIALOG_THEME);mDialog = new BaiduASRDigitalDialog(this, params); mDialog.setDialogRecognitionListener(mRecognitionListener);mDialog.getParams().putInt(BaiduASRDigitalDialog.PARAM_PROP, //百度识别类别 Config.CURRENT_PROP);mDialog.getParams().putString(BaiduASRDigitalDialog.PARAM_LANGUAGE,//百度识别语言 Config.getCurrentLanguage());mDialog.getParams().putBoolean( //百度识别音效相关 BaiduASRDigitalDialog.PARAM_START_TONE_ENABLE, Config.PLAY_START_SOUND);mDialog.getParams().putBoolean( BaiduASRDigitalDialog.PARAM_END_TONE_ENABLE, Config.PLAY_END_SOUND);mDialog.getParams().putBoolean( BaiduASRDigitalDialog.PARAM_TIPS_TONE_ENABLE, Config.DIALOG_TIPS_SOUND);mDialog.show();}/* * 识别结果处理函数 * * @param VoiceSave[0] 收入类别的值 * * @param VoiceSave[1] 金额的值 * * @param VoiceSave[3] 重复类别的值,仅用于显示提醒 * * @param VoiceSave[4] 支出类别的值 * * @param VoiceSave[5] "语音识别"类别的值 */private void Recognition(String t) { int mfirst = 100, mend = 0, temp = 0; Boolean ismoney = false, intype = false, outtype = false; String w = "", strmoney = "", inname = "1", outname = "2"; spdatalist = ptypeDAO.getPtypeName(userid); spdatalist2 = itypeDAO.getItypeName(userid); VoiceSave[2] = t; for (int i = 0; i < spdatalist.size(); i++) { // 判断是否包含支出 if (t.indexOf(spdatalist.get(i).toString()) > -1) { type = "pay"; intype = true; inname = spdatalist.get(i).toString(); VoiceSave[0] = Integer.toString(i); // VoiceSave[0]为收入类别的值 } } for (int i = 0; i < spdatalist2.size(); i++) { // 判断是否包含收入 if (t.indexOf(spdatalist2.get(i).toString()) > -1) { type = "income"; outtype = true; outname = spdatalist2.get(i).toString(); VoiceSave[4] = Integer.toString(i); // VoiceSave[4]为支出类别的值 } } for (int i = 0; i < number.length; i++) { // 判断是否包含金额,获得开头 if (t.indexOf(number[i]) > -1) { temp = t.indexOf(number[i]); if (temp < mfirst) { mfirst = temp; } } } for (int i = 0; i < money.length; i++) { // 判断是否包含金额,获得结尾 if (t.indexOf(money[i]) > -1) { temp = t.indexOf(money[i]); if (temp > -1 && temp >= mend) { mend = temp; } } } for (int i = 0; i < money2.length; i++) { // 判断是否包含金额,获得结尾 if (t.indexOf(money2[i]) > -1) { temp = t.indexOf(money2[i]); if (temp > -1 && temp >= mend) { mend = temp; } mend = mend + 1; } } if (!(mfirst == 100 || mend == 0)) { // 转换为阿拉伯数字 ismoney = true; strmoney = t.substring(mfirst, mend); DigitUtil Util = new DigitUtil(); VoiceSave[1] = Integer.toString(Util.parse(strmoney)); // 调用工具类处理汉字的金额 } if (intype && outtype) { // 如果含金额 if (outname.equals(inname)) { if (ismoney) { VoiceSave[3] = outname; // VoiceSave[3]为重复类别的值,仅用于显示提醒 dialogShowUtil.dialogShow("shake", "judge", t, w); // 如果含有金额 } else { w = "提示:\n你的话中没有包含消费或开支的<金额>\n"; dialogShowUtil.dialogShow("shake", "wrong", t, w); } } else { w = "**提示:\n一次只能记录一条记录哦\n"; // 如果含有收入并且支出的类别 dialogShowUtil.dialogShow("shake", "wrong", t, w); } } else { if (!((intype || outtype) || ismoney)) { // 如果不含金额 w = "**提示:\n你的话中没有包含<类别>(" + listToString(spdatalist, ',') + "," + listToString(spdatalist2, ',') + ")\n\n**提示:\n你的话中没有包含消费或开支的<金额>"; dialogShowUtil.dialogShow("shake", "wrong", t, w); } else if ((intype || outtype) && (!ismoney)) { w = "提示:\n你的话中没有包含消费或开支的<金额>\n"; dialogShowUtil.dialogShow("shake", "wrong", t, w); } else if ((!(intype || outtype)) && ismoney) { for (int i = 0; i < spdatalist.size(); i++) { // 判断是否包含支出 if ("语音识别".indexOf(spdatalist.get(i).toString()) > -1) { VoiceSave[5] = Integer.toString(i); VoiceSave[3] = "语音识别"; } } w = "**提示:\n你的话中没有包含<(默认)类别>(" + listToString(spdatalist, ',') + ")\n\n\n将会记录为<语音识别>类别,是否依然记录?\n"; dialogShowUtil.dialogShow("shake", "notype", t, w); } else { dialogShowUtil.dialogShow("rotatebottom", "OK", t, w); } }}
    Dialog部分处理代码:
    public void dialogShow(String showtype, String style,final String context1, String context2) {dialogBuilder = new NiftyDialogBuilder(ctx, R.style.dialog_untran); // 自定义dialogBuilderswitch (showtype) {case "rotatebottom":effect = Effectstype.RotateBottom;break;case "shake":effect = Effectstype.Shake;break;}switch (style) {case "first":dialogBuilder.withTitle("语音记账") // .withTitle(null) no title .withTitleColor("#FFFFFF") // def .withDividerColor("#11000000") // def .withMessage("语音格式:\n早餐在餐厅食了20元。\n\n") // .withMessage(null) no Msg .withMessageColor("#FFFFFF") // def .withIcon(ctx.getResources().getDrawable(R.drawable.icon)) .isCancelableOnTouchOutside(false) // def |// // isCancelable(true) .withDuration(700) // def .withEffect(effect) // def Effectstype.Slidetop .withButton1Text("取消") // def gone .withButton2Text("开始语音") // def gone .setButton1Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); } }).setButton2Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); ((AddPay) act).VoiceRecognition(); } }).show();break;case "notype":dialogBuilder.withTitle("识别成功") // .withTitle(null) no title .withTitleColor("#FFFFFF") // def .withDividerColor("#11000000") // def .withMessage("你刚刚说了“ " + context1 + "”\n\n" + context2) // .withMessage(null) no Msg .withMessageColor("#FFFFFF") // def .withIcon(ctx.getResources().getDrawable(R.drawable.icon)) .isCancelableOnTouchOutside(false) // def |// .withDuration(700) // def .withEffect(effect) // def Effectstype.Slidetop .withButton1Text("取消") // def gone .withButton2Text("是") // def gone .setButton1Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); } }).setButton2Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); VoiceDefault = "notype"; VoiceSave[3]=VoiceSave[3]; dialogShow("shake", "judge", context1, ""); } }).show();break;case "wrong":dialogBuilder .withTitle("识别失败") // .withTitle(null) no title .withTitleColor("#FFFFFF") // def .withDividerColor("#11000000") // def .withMessage( "你刚刚说了“ " + context1 + "”不符合格式,请再试一次\n\n" + context2) // .withMessage(null) no Msg .withMessageColor("#FFFFFF") // def .withIcon(ctx.getResources().getDrawable(R.drawable.icon)) .isCancelableOnTouchOutside(false) // def | // isCancelable(true) .withDuration(700) // def .withEffect(effect) // def Effectstype.Slidetop .withButton1Text("取消") // def gone .withButton2Text("再次语音") // def gone .setButton1Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); } }).setButton2Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); ((AddPay) act).VoiceRecognition(); } }).show();break;case "OK":dialogBuilder.withTitle("识别成功") // .withTitle(null) no title .withTitleColor("#FFFFFF") // def .withDividerColor("#11000000") // def .withMessage("成功!\n你刚刚说了“" + context1 + "”,\n是否确定要记录这条数据?") // .withMessage(null) no Msg .withMessageColor("#FFFFFF") // def .withIcon(ctx.getResources().getDrawable(R.drawable.icon)) .isCancelableOnTouchOutside(false) // def | // isCancelable(true) .withDuration(700) // def .withEffect(effect) // def Effectstype.Slidetop .withButton1Text("取消") // def gone .withButton2Text("确定") // def gone .setButton1Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); } }).setButton2Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); ((AddPay) act).VoiceSuccess(); } }).show();break;case "judge":dialogBuilder .withTitle("识别成功") // .withTitle(null) no title .withTitleColor("#FFFFFF") // def .withDividerColor("#11000000") // def .withMessage( "成功!\n你刚刚说了“" + context1 + "”,\n<" + VoiceSave[3] + ">类别需要你请确认该笔是<开支>还是<收入>?\n") // .withMessage(null) no Msg .withMessageColor("#FFFFFF") // def .withIcon(ctx.getResources().getDrawable(R.drawable.icon)) .isCancelableOnTouchOutside(false) // def | // isCancelable(true) .withDuration(700) // def .withEffect(effect) // def Effectstype.Slidetop .withButton1Text("开支") // def gone .withButton2Text("收入") // def gone .setButton1Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); type = "pay"; ((AddPay) act).VoiceSuccess(); } }).setButton2Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); type = "income"; ((AddPay) act).VoiceSuccess(); } }).show();break;case "quit":dialogBuilder.withTitle("退出程序") // .withTitle(null) no title .withTitleColor("#FFFFFF") // def .withDividerColor("#11000000") // def .withMessage("是否要退出程序?\n\n") // .withMessage(null) no Msg .withMessageColor("#FFFFFF") // def .withIcon(ctx.getResources().getDrawable(R.drawable.icon)) .isCancelableOnTouchOutside(false) // def | // isCancelable(true) .withDuration(700) // def .withEffect(effect) // def Effectstype.Slidetop .withButton1Text("取消") // def gone .withButton2Text("退出") // def gone .setButton1Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); } }).setButton2Click(new View.OnClickListener() { @Override public void onClick(View v) { SysApplication.getInstance().exit(); } }).show();break;}}
    5.4 类别维护界面增加或删除收入/支出类别,并判断类别是否重复是否为空。

    部分代码:
    private void inputTitleDialog() { final EditText inputServer = new EditText(InPtypeManager.this); inputServer.setFocusable(true); AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("添加类型").setView(inputServer) .setNegativeButton("取消", null); builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { inputStr = inputServer.getText().toString(); int i = (int) itypeDAO.getCount(userid) + 1; if (inputStr.trim().equals("")) { Toast.makeText(InPtypeManager.this, "输入内容不能为空!", Toast.LENGTH_LONG).show(); refresh(); } else{if(flag){Toast.makeText(InPtypeManager.this, "不可以重复插入!", Toast.LENGTH_LONG).show();}else{if (type == 0) { itypeDAO.add(new Tb_itype(userid, i, inputStr)); } else { ptypeDAO.add(new Tb_ptype(userid, i, inputStr)); }} refresh(); } }); builder.show(); }
    5.5 收入/支出统计界面收入支出统计页面,通过使用第三方开发的图表显示控件,通过从SQLite获取的数据和计算所占总数的百分比,去初始化图表显示,然后通过线程更新当前的UI。左右的按钮是调用上一个月和下一个月的数据,然后重新调用该activity来显示。另外我还添加了侧滑菜单来让用户选择是“收入图表”和“支出图表”,另外用户还能选择指定的日期范围,数据也是调用该页面显示。

    public void initView() { time = new Time("GMT+8"); time.setToNow(); defaultMonth = time.month + 1;// 设置默认月份 defaultYear = time.year; intentr = getIntent(); userid = intentr.getIntExtra("cwp.id", 100000001); defaultMonth = intentr.getIntExtra("default", defaultMonth); defaultYear = intentr.getIntExtra("defaulty", defaultYear); type = intentr.getIntExtra("type", 0);// 为0,选择上下月,为1,选择任意时间 pieView = (PieView) this.findViewById(R.id.lotteryView); myButton = (MyButton) this.findViewById(R.id.MyBt); textView = (TextView) this.findViewById(R.id.MyTV); textView2 = (TextView) this.findViewById(R.id.MyTVbottom); example_left = (ImageView) findViewById(R.id.example_left); example_right = (ImageView) findViewById(R.id.example_right); example_center = (TextView) this.findViewById(R.id.example_center); textView.setOnClickListener(this); example_left.setOnClickListener(this); example_right.setOnClickListener(this); example_center.setText(String.valueOf(defaultYear) + "年 - " + String.valueOf(defaultMonth) + "月"); //获取数据 if (type == 0) { KindDatap = payDAO.getKDataOnMonth(userid, defaultYear, defaultMonth); } else { date1 = intentr.getStringExtra("date1"); date2 = intentr.getStringExtra("date2"); KindDatap = payDAO.getKDataOnDay(userid, date1, date2); } initItem(); //初始化数据 if (!(KindDatap.size() == 0)) { //当获取到数据时 Message msg = new Message(); msg.obj = pieView.getCurrentChartProp(); handler.sendMessage(msg); //发送消息,更新UI } pieView.setChartPropChangeListener(new ChartPropChangeListener() { @Override public void getChartProp(ChartProp chartProp) { Message msg = new Message(); msg.obj = chartProp; handler.sendMessage(msg); //发送消息,更新UI } }); pieView.start();}public Handler handler = new Handler() { //创建线程 public void handleMessage(android.os.Message msg) { ChartProp chartProp = (ChartProp) msg.obj; myButton.setBackgroundPaintColor(chartProp.getColor()); textView.setText(chartProp.getName()); textView2.setText(chartProp.getName2()); textView.setTextColor(chartProp.getColor()); };};/** * * Description:初始化转盘的颜色 * */public void initItem() { int i = 0; int fivecolor[] = new int[] { Color.rgb(56, 220, 244), Color.GREEN, Color.RED, Color.YELLOW, Color.CYAN }; if (KindDatap.size() == 0) { // 没有数据的情况 amount = "暂无数据"; // 无数据下总数的提示文字 } else { // 获取数据的情况 double sum = 0.00; for (KindData kp : KindDatap) { sum += kp.getAmount();// 取得总和 i++; } // 初始化数组 String names[] = new String[i]; float percent[] = new float[i]; String names2[] = new String[i]; int color[] = new int[i]; i = 0; for (KindData kp : KindDatap) { names[i] = ptypeDAO.getOneName(userid, kp.getKindname()); if (i < fivecolor.length) { color[i] = fivecolor[i]; // 使用自定义颜色 } else { color[i] = getRandomColor(); // 使用随机生成的颜色 } java.text.NumberFormat percentFormat = java.text.NumberFormat .getPercentInstance(); percentFormat.setMaximumFractionDigits(2); // 最大小数位数 // 自动转换成百分比显示. names2[i] = percentFormat.format(kp.getAmount() / sum) + ":¥" + kp.getAmount(); percent[i] = (float) (kp.getAmount() / sum); // 计算所占百分比 i++; } amount = Double.toString(sum); // 总数的费用 // 创建图表 ArrayList<ChartProp> acps = pieView.createCharts(i); int size = acps.size(); for (int k = 0; k < size; k++) { // 把数据传入图表 ChartProp chartProp = acps.get(k); chartProp.setColor(color[k]); chartProp.setPercent(percent[k]); chartProp.setName(names[k]); chartProp.setName2(names2[k]); } pieView.initPercents(); }}private int getRandomColor() {// 分别产生RBG数值 Random random = new Random(); int R = random.nextInt(255); int G = random.nextInt(255); int B = random.nextInt(255); return Color.rgb(R, G, B);}public static String getamount() { return amount;}@Overridepublic void onClick(View v) { switch (v.getId()) { case R.id.example_left: // 上一个月的按键 if (defaultMonth != 1) defaultMonth = defaultMonth - 1; else { defaultMonth = 12; defaultYear = defaultYear - 1; } Intent intentl = new Intent(PayChart.this, PayChart.class); intentl.putExtra("defaulty", defaultYear); intentl.putExtra("default", defaultMonth); intentl.putExtra("cwp.id", userid); startActivity(intentl); break; case R.id.example_right: // 下一个月的按键 if (defaultMonth != 12) defaultMonth = defaultMonth + 1; else { defaultMonth = 1; defaultYear = defaultYear + 1; } Intent intentr = new Intent(PayChart.this, PayChart.class); intentr.putExtra("defaulty", defaultYear); intentr.putExtra("default", defaultMonth); intentr.putExtra("cwp.id", userid); startActivity(intentr); overridePendingTransition(android.R.anim.slide_in_left,android.R.anim.slide_out_right); break; }}
    6 论文总结从一月开始,经过这三个月的学习奋斗,毕业设计到现在终于基本上已经完成了。在这段日子里从系统的需求分析开始,然后到对系统功能。
    进行详细设计,最后到系统的编码实现,最后到论文的完成,我通过查阅大量的图书与文献自学相关知识,同时也诚心请教同学和老师,通过与他们的交流我学到了很多专业知识和经验,让我受益匪浅,对相关专业知识有了一个新的认识,当遇到有不认识或未曾遇到过的错误,上网寻找资料,解决难题,让我懂得网上的资源是十分之多,令我获益良多,这些锻炼了我坚强的意志,同时也使我的专业知识更加扎实,让我在以后的人生职业道路上可以更加的自信和顽强。
    毕设做到现在,算是告一段落。谈起为什么选Android,我只能说感兴趣,而又没毅力。在没有压力的情况下,让我去专研一门不懂的东西可行性太低。只能说很了解自己的性子。我想通过这种方式逼自己,幸好没让自己失望。虽然此次的毕设没有很有技术含量,但还是算勉强够凑合吧。
    希望经过后期的维护肯定能达到使用要求,更好完善自己系统的功能。
    参考文献[1] FrankAbleson.Introductionto Android development[J].developerWorks,2009,10(7).
    [2] Ed Burnett, Hello, Android: Introducing Google’s Mobile Development Platform[J]. PRAGMATIC BOOKSHELF,2010.7:10-11.
    [3] 徐娜子.Android江湖 [M].电子工业出版社.2011.11
    [4] 郭志宏. Android应用开发详解[M].电子工业出版社. 2010.
    [5] 明日科技.Android从入门到精通 [M].清华大学出版社.2012.9
    [6] 杨丰盛.Android应用开发揭秘[M]. 机械工业出版社. 2010.
    [7] 孙宏明.Android手机程序设计入门、运用到精通 [M].中国水利水电出版社.2012.3
    [8] 张仕成.基于Google Android平台的应用程序开发与研究[j].电脑知识与技,术2009.(5)
    [9] 靳岩,姚尚朗. Google Android开发入门与sss实践[M].人民邮电出版社. 2009
    [10] 姚永明,吕建平. 基于Android平台的用户管理软件的设计与实现[J]. 西安文理学院学报(自然科学版),2013,01:79-83.
    [11] 武玉坤.基于Android移动学习平台的设计[J]. 计算机光盘软件与应用,2013,01:20-21+47.
    [12] 姜海岚.基于Android的多功能手机相册设计与实现[J]. 电脑知识与术,2013,15:3614-3616.
    [13] 李刚.疯狂Android讲义 [M].电子工业出版社.2011.6
    [14] 李宁.Android应用开发实战[M].第2版. 机械工业出版社.2013
    3 评论 103 下载 2019-03-24 12:23:31 下载需要19点积分
  • 基于MySql和php的团购平台

    一、实现环境1.1 前端编程环境:Microsoft Windows 10 64bit
    1.2 后端
    编程环境:Microsoft Windows 10 64bit
    服务器: Apache Server v 2.4.23 本地部署
    数据库:MySQL + PHPMyAdmin v 4.5.1
    PHP 版本:PHP 5.6.24

    二、系统功能结构图2.1 系统功能需求分析“乐购”团购平台的设计目标是:方便顾客在网上完成团购工作,享受线上折扣;为商家提供在线的商品展示、推广与销售渠道;顾客可以给购买过的商品进行评价。为此,本系统应该具有以下几个功能:
    2.1.1 注册与登陆用户在使用本网站之前需要首先进行注册,否则将不能够使用完整的购物流程。在进行注册时,用户需要输入自己注册的用户名、密码以及邮箱等信息,不能够使用同一个邮箱进行重复注册。在注册完成后,用户便可以通过登录界面进行登陆,并可以使用网站的全部购物功能。
    商家在本平台进行商品销售之前需要首先进行商家注册。在进行注册时,商家需要输入自己注册的用户名、密码以及邮箱等信息,不同的商家不能够使用相同的邮箱重复注册。在注册完成之后,商家可以通过登录界面进行登陆。登陆之后的商家可以随时查看自己的信息并作出修改,还可以看到自己正在出售的商品以及销量信息。
    本系统设置后台管理员,管理员可以对自己负责类别板块的所有商品进行编辑修改,还可以查看所有商家的具体信息。
    从上面三点可以看出,本系统具有用户、商家和管理员三种角色。三种角色分别可以使用不同的方式进行登陆,并且分属不同的角色组,具有不同的操作权限。因而本系统具有权限管理的功能,能在一定程度上保证系统的安全性。
    2.1.2 用户信息管理用户在完成登陆之后可以查看并完善自己的个人信息,此外,个人信息将会统计该用户在本网站的总支出为多少。此外,用户在进行登陆之后还可以进行历史订单的查询,查看自己曾经购买过的商品。
    2.1.3 商家后台管理功能商家在完成登陆之后,可以选择添加新的商品。在添加商品的时候,需要提供商品的名称、价格、原价、类别、库存等信息,还可以提供商品的描述和图片。如果商品的库存不足将无法继续出售,此时商家可以选择删除该商品或者修改商品信息中的库存属性以恢复销售。此外,登陆后的商家还可以进行商户信息的修改。
    2.1.4 商品检索与购买功能用户在进行登陆之后可以进行商品的购买。购买商品需要首先进入商品界面,然后选择需要购买的数量,最后点击下单。完成下单之后,系统将会生成一个订单,订单中包含购买商品的数量以及商品的信息。用户可以在个人信息界面看到自己曾经下过的订单,并得到自己过去的总支出。
    此外,本网站还支持商品检索功能。用户可以选择自己想要选择商品的信息或者输入商品的关键字,以实现商品的查询功能。
    2.1.5 评价反馈功能用户在完成商品购买之后可以进行商品评价。用户的评分将会直接展示在商品页面和商家的页面上。商品的评分由该商品的所有评价的平均值决定,商家的评分由该商家的所有商品决定。
    2.2 系统功能结构设计图
    三、基本表定义根据之前的设计文档以及规范化原则,本系统采用共采用了 8 个基本表,它们分别为:管理员信息表(admin),用户信息表(users),商户信息表(shop),商品信息表(item),类别信息表(class),团购订单表(orders),订单详情表(iteminorder)和评论信息表(comment)。各基本表定义如下,其中的所有关系均已规范化到 3NF 以上。
    管理员信息表(admin)

    用户信息表(users)

    商户信息表(shop)

    商品信息表(item)

    类别信息表(class)

    团购订单表(orders)

    评论信息表(comment)

    订单详情表(iteminorder)

    四、安全性设计本系统能够满足较为基本的安全性设置,此章将从前端和后端两个方面说明本系统的安全性设计。
    4.1 前端前后端通信主要使用 POST 方法而不是 GET 方法,能有效防止其他人对网站信息的恶意截取与利用。用户使用用户名和密码登录的过程中,会首先检索用户名和密码填写的完整性,保证了在登录界面不会被渗透。
    4.2 后端后端的安全性设计主要采用了不同用户登陆的形式。商家、管理员、用户分别以三种不同的用户身份登陆到数据库中,不同角色之间的操作权限各不相同,这可以在很大程度上保证数据库的结构不被破坏。
    此外,在 php 连接后台的函数中,对于特定数据类型的数据加以验证,防止了 SQL 注入所造成的危险。例如,验证某个变量是否为整型,之后再进行相应操作
    $type = intval($type);
    或者是将需要检索的变量名使用引号括起来:
    $result = mysql_query("select * from users where user_email = '$name' and user_password = '$pw'",$conn);
    五、存储过程、触发器和函数5.1 存储过程本平台后端共采用了三个存储过程,下面我将分别对其进行介绍。
    5.1.1 商品数量设置存储过程(setrest)过程输入:商品 id id,需要设置的商品数量 itemnum
    过程输出:操作成功返回 0,否则返回 1
    作用:将对应 id 的商品库存数量设置为 itemnum。由于商品的库存不可能为负值,因此首先判断 itemnum 是否大于等于 0,如果是,那么进行赋值操作,输出 0;否则操作不合法,返回 1
    实现代码:

    5.1.2 商品评价统计存储过程(itemcomment)过程输入:商品 id itemid
    过程输出:1 星评价的占比 n1 , 2 星评价的占比 n2,3 星评价的占比 n3,4 星评价的占比 n4,5星评价的占比 n5,该商品的评价总数 total
    作用:统计 itemid 对应商品的所有评价的数量,并得到各等级评价所占的比例,以方便前端建立报表图形。
    实现代码:

    5.1.3 订单管理存储过程(orderManage)过程输入:用户名 username, 商品 id iid, 购买数量 num
    过程输出:运行结果 res 0 为执行成功,否则为 1
    作用:在用户进行购物操作的时候,根据不同的场景进行不同的订单操作。如果该用户不存在未结算的订单,那么系统将会为此用户新创建一个未结算的订单,之后将用户购买的操作插入到该订单的订单详情之中;如果该用户名下存在未结算的订单,那么系统将会直接在此订单的详情中插入用户现在购买的商品的信息
    实现代码:

    5.2 触发器5.2.1 商品评分更新触发器(item_star_update)作用对象:商品信息表(item)
    触发条件:comment 表插入操作后
    功能:在 comment 表中插入了一条新的评论之后,也就是用户对某商品进行评价之后,根据此评价重新计算该商品的评分(item_stars)
    实现代码:

    5.2.2 商家评分更新触发器(shop_star_update)作用对象:商家信息表(shop)
    触发条件:商品新表做出修改后
    功能:根据商品评分的修改更新商品的评分
    实现代码:

    5.2.3 用户年龄检查触发器(check_user_age)作用对象:用户信息表(users)
    触发条件:在用户表进行更新之前
    功能:首先判断输入的年龄是否合法,如果合法,更新用户年龄,否则终止操作
    实现代码:

    5.2.4 商品详情检查触发器(check_item_num)作用对象:订单详情表(iteminorder)
    触发条件:在订单详情表进行更新之前
    功能:判断用户购买的商品数量是否合法,如果合法,更新订单详情,否则终止操作
    实现代码:

    5.2.5 订单价格更新触发器(order_price_update)作用对象:订单详情表(iteminorder)
    触发条件:在订单详情表插入新条目之后
    功能:根据更新的商品的 id 和数量更新当前商品的价格
    实现代码:

    5.2.6 用户支出统计触发器(user_outcome_adapter)作用对象:订单信息表(orders)
    触发条件:订单信息表进行更新之后
    作用:根据更新订单的用户名,更新该用户的总支出情况
    实现代码:

    六、实现技术6.1 前端技术整个前端使用了很少 bootstrap 的 css 和 js 样式,基本的样式都是通过 html + css + javascript 手工实现,也运用了少许的 jquery 内容。前后端通信通过 ajax 进行,传递信息格式是 json。
    6.2 后端技术后端未使用现有的成熟框架,采用 PHP + MySQL 手工搭建。每当前端发来请求的时候,后端会首先解析前端所发来的 JSON 数据,之后进行相应的数据库查询,最终将查询的结果重新打包成 json格式返还给前端。大致的实现流程如下:

    七、运行实例7.1 主页页面主页页面采用 HTML + CSS + JS 实现动态加载,各个分栏中的内容都来源于数据库,可以跟随数据库的变化而变化。未进行登陆以及登陆后的页面会有所不同。

    7.2 登陆界面在主页点击登陆按钮之后将会弹出对应的登陆弹窗,在弹窗内输入注册邮箱和密码,然后点击立刻登陆。此时前端将会连接后端的服务器,进行身份校验,在确定输入的密码和登陆的用户一致后方能完成登陆操作。

    登陆后的主页右上角将会显示用户的用户名或者商家的商家 ID。

    7.3 商品页面商品页面可以通过点击主页的商品图片或者通过点击搜索后的结果到达,展示了商品的各个属性和评价。用户可以在此购买商品,也可以对商品进行评价。
    如下图所示,页面最上方显示商品的名称和描述,左侧有商品的配图,右侧有商品的优惠价、原价以及库存等信息。下方显示此商品的消费评价等信息,用户也可以通过点击提交评价按钮进行评价的提交。提交后的评价将会显示在页面的最下方。

    7.4 商家管理页面商家登陆后即可进入到商品管理页面,商家可以在这里修改自身的信息,也可以在此对商品做出添加,删除,修改操作。

    7.5 搜索页面搜索功能分为按类别搜索以及按照关键字搜索两类,点击页面上方的页面即可跳转到商品的类别搜索界面

    7.6 购物订单购物订单上半部分显示未支付的商品,按下显示金额的按钮可以进行支付,一次支付的所有内容算作一个订单。下半部分显示已支付的订单,每个订单用一圈黑色包裹,里面展示了订单具体内容。

    7.7 管理员登陆及管理接口
    八、源程序简要说明8.1 前端程序前端内容基本以 html 的格式放在根目录下,而每个 html 文件另配备一个 javascript 文件来实现动态加载以及通信。因为 css 优先级的问题,所以大部分 css 样式都嵌在 html 内部来重写 bootstrap代码。

    index.html:主页面,顶端摆放大型横幅,来宣传推广商品。下面左侧有个移动导航条,分类导航到各个栏目的商品,栏目共分五类:美食、酒店、电影、KTV、生活服务。每类各展示几个商品,点击更多会跳转到类别目录下
    mer.html:商品信息界面,上半部分展示商品具体信息,用户提交订单。下半部分展示商品评价并且用户可以自己填写评价
    merchant.html:列出商户具体信息,提供按钮进行编辑
    merchantEdit.html:商户编辑信息界面,可编辑名称、邮箱、电话、地址
    merEdit.html:商品信息编辑界面,可编辑名称、描述、原价、价格、图片、库存、类型
    user.html:用户个人信息界面,显示用户年龄、邮箱、电话、地址、头像、总支出
    userEdit.html:用户个人信息编辑界面,可编辑用户年龄、邮箱、电话、地址、头像
    register.html:登录界面
    result.html:顶部搜索结果界面,同时也是分类显示界面,界面每一排显示一个符合结果的商品
    paylist.html:订单界面,上面是未支付的订单,按下方按钮可以进行支付。下面是已支付订单,每一个订单会展示在一起。每个订单可以有多个不同的商品

    8.2 后端程序后端连接数据库所用的函数全部保存在 htdocs/php 文件夹下面,各文件的具体功能如下:

    addComment.php:创建商品评价的函数
    addItem.php:供商户添加商品所用的函数
    adminlogin.php:管理员登陆功能的函数
    createorder.php:创建用户清单的函数
    deleteitem.php:删除物品的函数
    itemcommnet.php:显示某商品评价的函数
    loadindex.php:加载主页内容的函数
    loadinfo.php:读取个人信息的函数
    loadmer.php:mer 页面的动态填写函数
    loadshop.php:读取商家信息的函数
    login.php:用户/商家登陆用函数
    mysql.php:连接数据库所用的函数
    pay.php:处理订单结算的函数
    register.php:实现登陆功能的函数
    search.php:实现搜索功能的函数
    shopitem.php:实现商户页面商品填充的函数
    test.php:单纯的测试用函数
    updateitem.php:更新商品信息的函数
    updateshop.php:更新商户信息的函数
    updateuser.php:更新用户信息的函数
    userorder.php:读取用户所有订单的函数

    九、总结通过这次数据库课设的实践,我们对于课上所讲授的数据库知识有了更进一步的了解。不仅亲手从模型开始进行了完整的数据库设计流程,还亲自实现了事务处理、触发器、索引、存储过程等机制,对于课上学习的内容进行了进一步的巩固和加强,同时也深刻地体会到了“实践”对于计算机科学这个学科的重要性。纸上得来终觉浅,绝知此事要躬行。由于刘瑞老师课上讲授的 SQL 语句多为 T-SQL,而且之前的题目大多都是用笔写 SQL,这导致我们的数据库编程经验时期是非常短缺的。对于手写 SQL 语句中的小错误,我们常常会归结于自己的马虎,但是等真正敲起代码来的时候,一个小小的错误都会导致 SQL 语句无法执行。不能熟练使用的结果就是提笔就报错,然后进入漫长的 debug 阶段,在经过比较漫长的熟悉过程之后,开发的效率才慢慢提高了起来。在数据库编程面前,细心,才是保证开发效率的唯一捷径。
    2 评论 7 下载 2019-12-29 14:43:50 下载需要15点积分
  • 基于ASP.NET和SQL SERVER数据库的招聘网站设计与实现

    摘 要本课题是基于互联网与数据库技术的网上招聘网站,是先进的计算机科学技术和现代招聘理念相结合的产物,通过使用以ASP.NET技术为基础,基本实现网上招聘网站的基本功能,满足了求职者和招聘企业的需求,实现了招聘单位和求职者的双向选择,对于求职者查看岗位和简历投递进度有很大的帮助,本网站收集大量招聘单位的招聘任息,它使网上招聘从盲目的网络职位搜索到有序大量的职位投递,从混乱变为规范,而成功率较高。
    本人负责求职者简历投递的流程和公司处理求职者的全部操作,在功能上已经满足基本需求,但细节上仍然有瑕疵。比如数据的更新,网站本应使用Ajax技术实现异步刷新,但由于技术难度和风险,选择了整体刷新,在视觉效果上仍然可以有改进的余地。
    关键词: 招聘网站;职位投递;双向需求
    1 系统功能概述1.1 公司权限
    公司权限登录:进入公司权限,可选择公司的功能菜单
    公司信息管理:展示公司图标、名称、地址及详细信息,并提供编辑功能
    职位管理:提供发布删除本公司职位功能
    申请管理:查看求职者投递的简历并选择是否提档
    录用信息:查看已经被提档的用户,并发送面试时间。

    1.2 求职者权限
    求职者登录:进入求职者界面,可选择求职者的功能菜单
    简历管理:查看个人简历信息,并提供编辑简历功能
    职位市场:展示市场中的职位信息,并允许投递简历
    职位管理:查看求职者已经投档的简历
    我的信箱:查看公司反馈的投递简历信息和面试信息

    1.3 系统层次本系统的层次图如下图所示。

    2 数据库设计2.1 需求分析为了实现系统功能,本系统需要设计7个数据表,对应的实体关系图如下图所示。

    2.2 表结构设计求职者模型对应的表结构如下表所示。
    求职者表



    字段名
    数据类型
    长度
    含义
    约束




    Sid
    int
    10
    ID
    主键


    Sresume
    int
    10
    对应简历I



    Susername
    varchar
    20
    用户名



    Spassword
    varchar
    20
    密码



    简历表



    字段名
    数据类型
    长度
    含义
    约束




    Rid
    int
    10
    ID
    主键


    Rname
    varchar
    10
    姓名



    Rsex
    varchar
    2
    性别



    Rbirth
    datetime
    20
    生日



    Rtel
    varchar
    255
    手机号



    Rphoto
    varchar
    255
    照片



    Remail
    varchar
    255
    电子邮件



    Rworktime
    varchar
    255
    工作经历



    Redu
    varchar
    255
    学历



    Revaluate
    varchar
    255
    自我评价



    Rproject
    varchar
    255
    项目经验



    Rtech
    varchar
    255
    教育经历



    Rhonor
    varchar
    255
    荣誉奖项



    Rhobby
    varchar
    255
    兴趣爱好



    职位表



    字段名
    数据类型
    长度
    含义
    约束




    Jid
    int
    10
    ID
    主键


    Jname
    varchar
    20
    职位名



    Jcompany
    int
    10
    所属公司



    Jneed
    varchar
    255
    需求



    Jsalary
    varchar
    255
    工资待遇



    Jduty
    varchar
    255
    工作任务



    Jdemand
    varchar
    255
    工作内容



    Jdate
    varchar
    255
    发布时间



    3 系统各功能模块的详细设计3.1 求职者登录该模块是求职者登录的入口,首先通过用户的账号判断数据库是否存在该求职者用户,然后通过用户账号读取用户名密码进行匹配,并做出相应反馈。
    求职者登录界面如下图所示。

    登录后从数据库中读取求职者信息,并存储到Seeker对象中。然后将进入求职者权限界面,并可以选择求职者的菜单。
    求职者欢迎界面如下图所示。

    求职者登录界面模块中的主要代码如下:
    public static Seeker Login(string name, string pwd) { if (Exist(name)) { string sql = "SELECT * FROM seeker WHERE Susername = @name"; SqlParameter[] parm = new SqlParameter[] { new SqlParameter("@name",name) }; DataTable dt = DBHelper.GetDataTable(sql, parm); DBHelper.SqlClose(); if (dt.Rows[0].ItemArray[4].ToString() == pwd) { Seeker seeker = new Seeker(); seeker.Sid = (int)dt.Rows[0].ItemArray[0]; seeker.Sresume = (int)dt.Rows[0].ItemArray[2]; seeker.Sname = dt.Rows[0].ItemArray[1].ToString(); return seeker; } else { return null; } } else { return null; } }
    3.2 简历管理求职者的简历管理模块是查看编辑简历的模块。首先通过DataSource来绑定到数据库,并将FormView绑定到DataSource,更新时数据源也会跟着更新。
    简历展示界面如下图所示。

    当点击编辑键时,会展示事先设置好的编辑样式,点击更新后自动根据绑定的数据源更新到数据库。
    编辑简历界面如下图所示。

    3.3 职位市场职位市场模块提供了求职者对职位的浏览,并可以随时向中意的职位发送简历。展示界面从数据库中查询没有被投递过的职位,并用Html代码和asp脚本动态展示。动态展示的同时在复选框中存储着职位的ID,作为投递的依据。
    职位市场界面如下图所示。

    投递简历时,首先将隐藏的对话框展示出来等待用户确认,确认后将复选框选中的ID,求职者的ID和当前的时间存储到数据库。
    投递确认的对话框如下图所示

    展示职位的界面主要代码如下:
    public static List<Job> getJobsBySeeker(int sid) { string sql = "SELECT * FROM job,company Where Cid = Jcompany And Jid NOT IN (Select Jid FROM Apply where Sid = @Sid)"; SqlParameter[] parm = new SqlParameter[] { new SqlParameter("@Sid",sid) }; DataTable dt = DBHelper.GetDataTable(sql, parm); DBHelper.SqlClose(); List<Job> jobs = new List<Job>(); foreach (DataRow row in dt.Rows) { Job job = new Job(); job.Jid = (int)row.ItemArray[0]; job.Jname = row.ItemArray[1].ToString(); job.Jcompany = (int)row.ItemArray[2]; job.Jneed = row.ItemArray[3].ToString(); job.Jsalary = row.ItemArray[4].ToString(); job.Jduty = row.ItemArray[5].ToString(); job.Jdemand = row.ItemArray[6].ToString(); job.Jdate = row.ItemArray[7].ToString().Split(' ')[0]; job.Cname = row.ItemArray[9].ToString(); jobs.Add(job); } return jobs; }
    投递简历的主要代码如下:
    protected void Button1_Click(object sender, EventArgs e) { string[] jids = Request.Form["push"].ToString().Split(','); foreach (string jid in jids) { if (!DJob.PushResume(Convert.ToInt32(jid), Convert.ToInt32(Session["uid"]))) { Response.Write("<script>alert('投递失败!');</script>"); return; } } Response.Write("<script>alert('投递成功!');</script>"); }
    3.4 公司登录该模块是公司职员登录界面,首先通过用户的账号判断数据库是否存在该公司用户,然后通过用户账号读取用户名密码进行匹配,账号及密码匹配则进入公司主页,否则予以提示。
    公司登录界面如下图所示。

    成功登录后从数据库中读取公司信息,并存储到Company对象中,然后将进入公司权限界面,并可以查看公司的菜单。
    成功登录界面如下图所示。

    公司登录界面模块中的主要代码如下:
    public static Company Login(string name, string pwd) { if (Exist(name)) { string sql = "SELECT * FROM company WHERE Cusername = @name"; SqlParameter[] parm = new SqlParameter[] { new SqlParameter("@name",name) }; DataTable dt = DBHelper.GetDataTable(sql, parm); DBHelper.SqlClose(); if (dt.Rows[0].ItemArray[4].ToString() == pwd) { Company company = new Company(); company.Cid = (int)dt.Rows[0].ItemArray[0]; company.Cname = dt.Rows[0].ItemArray[1].ToString(); company.Cdetails = dt.Rows[0].ItemArray[2].ToString(); company.Caddress = dt.Rows[0].ItemArray[5].ToString(); return company; } else { return null; } else { return null; } }
    3.5 公司信息管理公司信息管理模块是提供查看和编辑公司信息的模块。首先将对象Company中存储的信息用p标签展示到前端。
    公司信息展示界面如下图所示。

    当点击编辑键时,展示修改的对话框。确定后先将改动的数据存储到Company对象,然后用公司的方法封装类DCompany的方法将新的Company对象更新到数据库。
    编辑公司详情界面如下图所示。

    公司修改详细信息的按钮事件代码如下:
    protected void Button1_Click(object sender, EventArgs e) { String s1 = Request.Form["company_info"]; com.Cdetails = s1; if (DCompany.UpdateCompany(com)) Response.Write("<script>alert('修改完成!');</script>"); else Response.Write("<script>alert('修改失败!');</script>"); }
    工具类DCompany更新数据库的主要代码如下:
    public static bool UpdateCompany(Company company) { string sql = "UPDATE [company] SET [Cname] = @Cname, [Cdetails] = @Cdetails, [Caddress] = @Caddress WHERE [Cid] = @Cid"; SqlParameter[] parm = new SqlParameter[] { new SqlParameter("@Cname",company.Cname), new SqlParameter("@Cdetails",company.Cdetails), new SqlParameter("@Caddress",company.Caddress), new SqlParameter("@Cid",company.Cid) }; int line = DBHelper.ExecuteNonQuery(sql, parm); DBHelper.SqlClose(); return line>0; }
    3.6 公司职位管理公司职位管理模块提供了公司对职位的发布和删除功能。展示界面根据Company对象的公司ID从数据中读取公司的职位,存储到集合中,在前端页面用ASP脚本遍历集合,并用Html代码和asp脚本动态展示数据。
    职位展示界面如下图所示。

    新建职位信息时,首先将隐藏的新建对话框展示出来等待用户输入信息后点击确认按钮,确认后通过职位的工具类DJob插入到数据库中。
    新建职位界面如下图所示。

    公司职位展示界面主要代码如下:
    public static List<Job> getJobsByCompany(int cid) { string sql = "SELECT * FROM job,company WHERE Jcompany = @cid AND Cid = Jcompany"; SqlParameter[] parm = new SqlParameter[] { new SqlParameter("@cid",cid) }; DataTable dt = DBHelper.GetDataTable(sql, parm); DBHelper.SqlClose(); List<Job> jobs = new List<Job>(); foreach(DataRow row in dt.Rows) { Job job = new Job(); job.Jid = (int)row.ItemArray[0]; job.Jname = row.ItemArray[1].ToString(); job.Jcompany = (int)row.ItemArray[2]; job.Jneed = row.ItemArray[3].ToString(); job.Jdate = row.ItemArray[7].ToString().Split(' ')[0]; job.Cname = row.ItemArray[9].ToString(); jobs.Add(job); } return jobs; }
    公司发布岗位的主要代码如下:
    public static bool InsertJob(Job job) { string sql = "INSERT INTO [job] ([Jname], [Jcompany], [Jneed], [Jsalary], [Jduty], [Jdemand], [Jdate]) VALUES (@Jname, @Jcompany, @Jneed, @Jsalary, @Jduty, @Jdemand, @Jdate)"; SqlParameter[] parm = new SqlParameter[] { new SqlParameter("@Jname",job.Jname), new SqlParameter("@Jcompany",job.Jcompany), new SqlParameter("@Jneed",job.Jneed), new SqlParameter("@Jduty",job.Jduty), new SqlParameter("@Jdemand",job.Jdemand), new SqlParameter("@Jdate",DateTime.Now.Date), }; int line = DBHelper.ExecuteNonQuery(sql, parm); DBHelper.SqlClose(); return line > 0; }
    3.7 公司申请管理模块申请管理模块是求职者向公司发送简历后,公司对发送过来的简历处理的过程。首先通过公司的ID从数据库中读取多表匹配的结果,将结果作为数据源绑定在DataGridView上,以表格的形式展示。
    申请界面如下图所示。

    选择一个求职者后,跳转到简历界面,查看选中的求职者的简历。有同意和驳回两个选项。并且会分别给对方发送通知。
    查看简历界面如下图所示。

    发送同意信息的按钮中的主要代码如下:
    protected void Button1_Click(object sender, EventArgs e) { int aid = Convert.ToInt32(Request.QueryString["apply"]); if (DNews.SentSuccessNews(aid)) { Response.Redirect("~/Com/apply.aspx",false); Response.Write("<script>alert('提档成功!');</script>"); } else { Response.Write("<script>alert('提档失败!');</script>"); } }
    获取申请的主要代码如下:
    public static Apply GetApply(int aid) { string sql = "SELECT Apply.Aid, job.Jname, company.Cname,seeker.Sid, seeker.Sname, Apply.Adatetime,job.Jid FROM job INNER JOIN Apply ON job.Jid = Apply.Jid INNER JOIN seeker ON Apply.Sid = seeker.Sid CROSS JOIN company WHERE (Apply.Aid = @Aid)"; SqlParameter[] parm = new SqlParameter[] { new SqlParameter("@Aid",aid) }; DataTable dt = DBHelper.GetDataTable(sql, parm); DBHelper.SqlClose(); Apply apply = new Apply(); apply.Aid = Convert.ToInt32(dt.Rows[0].ItemArray[0]); apply.Jname = dt.Rows[0].ItemArray[1].ToString(); apply.Cname = dt.Rows[0].ItemArray[2].ToString(); apply.Sid = Convert.ToInt32(dt.Rows[0].ItemArray[3]); apply.Sname = dt.Rows[0].ItemArray[4].ToString(); apply.Adatetime = dt.Rows[0].ItemArray[5].ToString(); apply.Jid = Convert.ToInt32(dt.Rows[0].ItemArray[6]); return apply; }
    操作类DNews发送消息的函数主要代码如下:
    public static bool SentSuccessNews(int aid) { string sql = "INSERT INTO [news] ([Sid], [Ntitle], [Ncontent], [Ntime]) VALUES (@Sid, @Ntitle, @Ncontent, @Ntime)"; Apply apply = DNews.GetApply(aid); string title = apply.Cname + "-" + apply.Jname + "-提档通知"; string content = "亲爱的" + apply.Sname + "同学:\n\t您在" + apply.Adatetime + "的时候向" + apply.Cname + "公司" + apply.Jname + "职位投递的简历已被提档,请等待面试通知.\n\t祝您面试顺利!"; SqlParameter[] parm = new SqlParameter[] { new SqlParameter("@Sid",apply.Sid), new SqlParameter("@Ntitle",title), new SqlParameter("@Ncontent",content), new SqlParameter("@Ntime",DateTime.Now), }; int line = DBHelper.ExecuteNonQuery(sql, parm); DBHelper.SqlClose(); if (line > 0) { if (DEmploy.Employ(apply.Jid, apply.Sid)) return true; } return false; }
    3.8 录用管理与面试录用管理模块主要展示被提档的人员的录用信息,以及向录用人员发送面试通知。首先通过公司的ID从数据库中读取被录用的人员信息存储在集合中,在前端遍历集合,通过html和asp脚本动态展示。
    查看录用人员的界面如下图所示。

    发送面试通知的界面如下图所示。

    展示录用人员的主要代码如下:
    public static List<Employ> getEmploys(int cid) { string sql = "SELECT resume.Rname, resume.Remail, resume.Rtel, employ.Eid, employ.Jid, employ.Sid, employ.Edate, job.Jname, job.Jcompany FROM resume INNER JOIN job INNER JOIN employ ON job.Jid = employ.Jid INNER JOIN seeker ON employ.Sid = seeker.Sid ON resume.Rid = seeker.Sresume WHERE(job.Jcompany = @Jcompany)"; SqlParameter[] parm = new SqlParameter[] { new SqlParameter("@Jcompany",cid) }; DataTable dt = DBHelper.GetDataTable(sql, parm); DBHelper.SqlClose(); List<Employ> employs = new List<Employ>(); foreach (DataRow row in dt.Rows) { Employ employ = new Employ(); employ.Eid = (int)row.ItemArray[3]; employ.Jid = (int)row.ItemArray[4]; employ.Sid = (int)row.ItemArray[5]; employ.Edate = row.ItemArray[6].ToString(); employ.Jname = row.ItemArray[7].ToString(); employ.Sname = row.ItemArray[0].ToString(); employ.Stel = row.ItemArray[2].ToString(); employ.Smail = row.ItemArray[1].ToString(); employs.Add(employ); } return employs; }
    发送面试通知的主要代码如下:
    public static bool SentInterviewNews(int eid,string date) { string sql = "INSERT INTO [news] ([Sid], [Ntitle], [Ncontent], [Ntime]) VALUES (@Sid, @Ntitle, @Ncontent, @Ntime)"; Employ employ = DEmploy.getEmploy(eid); string title = employ.Jname + "-面试通知"; string content = "亲爱的" + employ.Sname + "同学:\n\t 请您在 " + date + " 的时候参加 "+ employ.Jname+" 岗位的面试."; SqlParameter[] parm = new SqlParameter[] { new SqlParameter("@Sid",employ.Sid), new SqlParameter("@Ntitle",title), new SqlParameter("@Ncontent",content), new SqlParameter("@Ntime",DateTime.Now), }; int line = DBHelper.ExecuteNonQuery(sql, parm); DBHelper.SqlClose(); return line > 0; }
    3.9 求职者信箱信箱模块可以让求职者快速查看简历投递的进度和面试通知。系统根据求职者的ID从数据库中读取求职者的信息集合,然后在前端遍历集合,通过html和asp脚本动态展示。
    信箱界面如下图所示。

    展示信息的主要代码如下:
    public static List<News> getNewsBySeeker(int sid) { string sql = "SELECT * FROM [news] WHERE ([Sid] = @Sid)"; SqlParameter[] parm = new SqlParameter[] { new SqlParameter("@Sid",sid) }; DataTable dt = DBHelper.GetDataTable(sql, parm); DBHelper.SqlClose(); List<News> newsList = new List<News>(); foreach (DataRow row in dt.Rows) { News news = new News(); news.Nid = Convert.ToInt32(row.ItemArray[0]); news.Ntime = row.ItemArray[4].ToString(); news.Ntitle = row.ItemArray[2].ToString(); news.Ncontent = row.ItemArray[3].ToString(); newsList.Add(news); } return newsList; }
    4 结论由于本次课设在期末考试之后,经过一次又一次细致的复习,此次课设对我来说也不是完全的一头雾水,本次题目是网上招聘网站的设计与开发,当我带着疑问查询问题时,也对asp这门课有着更深层上的理解。这次课程设计我学到很多很多的东西,不仅巩固了以前所学过的知识,而且学到了很多在书本上所没有学到过的知识,掌握了一种系统的研究方法。通过这次课程设计使我懂得了理论与实际相结合是很重要的,只有理论知识是远远不够的。同时在设计的过程中发现了自己的不足之处不过。这次课程设计通过自己和同组同学的努力,还有老师的辛勤指导下,最终顺利完成了。
    在开发的过程中,一个标点符号就有可能让你烦不胜烦,其次在开发的过程中,要先对开发的项目进行分析,设计,先分析清楚项目的主要业务,主要流程,弄清楚实体与实体之间的关系,要尽可能的考虑周到,要细到具体的每一个功能,每一个实体所包含的属性、字段,然后在进行数据库的设计,之后再着手代码的编写。
    有的功能看起来貌似很简单,总感觉自己没必要花时间去做这么简单的东西,当你真正动手去做的时候,你就会知道它其实并不简单,要把它做好的话,也是有一定的困难的。例如这次项目中的detailsview界面不太美观,而且还不能套用css,于是我们放弃了使用detailsview而选择使用了formview,这样就完美地解决了此类问题,也为我以后的程序设计打下了基础。还有一点就是要多注意代码的书写规范,它能方便我们队代码的查找与修改,同时也能缩短我们的开发时间。
    本系统基本实现网上招聘网站的基本功能,满足了求职者和招聘企业的需求,实现了招聘单位和求职者的双向选择,对于求职者查看岗位和简历投递进度有很大的帮助。
    由于项目实训的时间紧迫,仍然具有很多问题和缺点,比如公司对于某条岗位信息的修改,因为暂时无法获取鼠标点击的div所代表的数据ID而暂时搁置。虽然可以通过后台的JavaScript获取点击时间,但仍然无法很好的解决。再比如数据的更新,本应使用Ajax技术实现异步刷新,但由于技术难度和风险,选择了整体刷新,在视觉效果上没有尽善尽美。
    总之,本系统已基本满足招聘网站的需求,可以发布并测试,根据运行中的反馈和测试进行进一步的修改与完善。
    参考文献[1] 丁允超 汪忆 张浩然著.ASP.NET WEB程序设计.北京.清华大学出版社.2017
    [2] 黄锐军著..NET WEB企业应用开发实战.北京.清华大学出版社.2017
    [3] 魏菊霞著.ASP.NET实践教程.北京.清华大学出版社.2017
    [4] 尚展垒,唐思均著.ASP.NET程序设计.北京.人民邮电出版社.2017
    [5] 软件开发技术联盟著.ASP.NET开发实例大全.北京.清华大学出版社.2016
    3 评论 52 下载 2018-11-15 09:07:57 下载需要8点积分
  • 基于C++的通讯录系统

    一、实验的内容通讯录系统可帮助使用者管理归纳通讯录名单,达到添加,删除,修改,保存等需求。要求使用学习过的C/C++程序设计的知识完成通讯录系统的设计与实现。
    二、实验课题分析2.1 通讯录系统的主要功能通讯录主要功能为:添加通讯录成员,修改成员,删除成员,按需求搜索查看成员,保存为文档。

    系统各模块的功能具体描述为:
    添加成员模块
    提供界面让使用者输入希望加入的通讯录成员的各类信息(姓名,电话,住址,QQ,邮箱等),并检查格式是否有误。若格式无误,则将该通讯录信息通过二进制文件方式储存在./contact文件目录下。
    修改成员模块
    使用者可以重写已有的通讯录成员,增加或删除除姓名以外的各个信息。一条通讯录成员可以拥有多个电话号码或QQ。
    删除成员模块
    使用者可以选择某个不希望继续使用的通讯录成员并删除他们。
    搜索查看成员模块
    使用者通过各种方式查询已添加的通讯录成员,并决定是否修改或删除它们。提供的方法有:精准查询,模糊查询,按分类查询等。
    2.2 系统分析及设计系统开发使用Template Method设计模式和Strategy Patten 两种设计模式,较好的封装所需函数,使得主程序入口开发环节只需关注Contact.h头文件即可实现。具体类之间的耦合关系见下图:

    2.3 系统的实现2.3.1 类的编写系统工程名为:contacts。包含类:Person, Contact, ContactInterface, ContactInit等具体类结构声明如下:
    Person类
    class Person {Public: char name[MAXNAME]; char sex; char tel[MAXTEL]; char addr[MAXADDR]; char zip[MAXZIP]; char mail[MAXMAIL]; char qq[MAXQQ]; char category[MAXCTGY]; Person(); ~Person();};
    ContactInterface类
    class CheckInterface{public: bool check(Person&, const bool _check_repe) const; bool check_exact(const Person&, const string) const;virtual ~CheckInterface(){};private: vector<string> part_tq(const Person&, const char* const) const;};
    ContactInit类
    class ContactInit{public: virtual int refresh() const = 0; virtual void welcome() const= 0; virtual ~ContactInit(){};};
    Contact类
    class Contact : public ContactInterface, public ContactInit{private: MainStrategy* setMainStrategy(int); public: Contact(); ~Contact(); int refresh() const; void welcome() const;};
    MainStrategy类
    class MainStrategy : public CheckInterface{public: MainStrategy(); virtual ~MainStrategy(); virtual int doMainStrategy() = 0;protected: void printAll() const; void print_prsn(const Person&, const string, bool) const; bool delete_prsn(Person&) const; int modify_prsn(Person&) const; //Way to modify a spefic Person member, with 0->success, -1->fail};
    MainViewMenuInterface类
    class MainVewMenuInterface : public MainStrategy{public:private: ViewStrategy* viewStrategy; virtual ViewStrategy* setViewStrategy(int) = 0; virtual int view(Person* v_Person) const; public: MainVewMenuInterface(); virtual ~MainVewMenuInterface(); virtual int doMainStrategy();};
    类的关系设计

    各类的具体功能和说明如下:

    class Person; 提供基本的数据存储结构。class ContactInterface; 提供主函数菜单,策略选择方法。是Contact类的一个接口,MainStrategy的调用者。class ContactInit; 提供初始化程序所需函数。同样是Contact类的一个接口。class Contact; 具体实现了两个接口的方法。MainStrategy的决策者。同时面向调用者(main.cpp)。但注意Contact不提供任何public方法。需要通过两个接口调用。class CheckInterface; 提供检查函数。class MainStrategy; Strategy Patten设计模式。同时包含子类公用的方法。class MainNewMenu; class MainDelMenu; class MainMdfMenu; 分别override doMainStrategy()函数,实现新建,删除,修改功能。class MainVewMenuInterface; override doMainStrategy()函数,ViewStrategy的调用者。class MainVewMenu; ViewStrategy的决策者。class ViewStrategy; Strategy Patten。class ViewAllMenu; class ViewExactMenu;class ViewFuzzyMenu; class ViewCategoryMenu; 分别override doViewStrategy()函数,实现所有查找,精确查找,模糊查找,按类查找功能。

    通讯录实现流程图

    2.3.2 交互界面以及登录菜单的实现系统运行开始的界面如图所示

    主要通过选择结构和循环结构实现界面的前进和后退。例如,第一个登录界面出现5个选择:1.新建,2.删除,3.修改,4.浏览,5.退出
    三、实验调试、测试、运行记录及分析系统在调试测试过程中遇到若干问题,不过经过仔细反复的检查已经消除各种漏洞。
    主要的测试经过如下:

    在开始界面输入“1”即添加新的成员:

    若显示 Information Entry Success! 则录入数据成功。若显示Information Error! 则录入数据失败。如图则因为在电话(TEL)中出现了中文字符。随后将返回主界面。

    在主界面输入2可删除成员:

    如我们希望删除(2)数据,则键入2:

    就可以得到(2)的详细数据。输入y/n即可选择是否删除该成员。随后程序将返回主界面。
    在主界面下输入3可以修改已有的成员:我们希望修改刚刚加入的成员(1)的电话号码,同时加入他所有常用的QQ号码:

    键入1, 并按照需求修改信息:

    确认无误后即可修改信息。随后返回主界面
    输入4即可按需求分类查看搜索成员:

    键入1进行精准匹配,该模式下只匹配名字:如输入“严恩伟”后匹配信息如图:

    随后可以根据自身需求选择1-3选项。此处不再演示。
    在view模式下键入2进行模糊匹配,该模式匹配名字,电话,地址。只要出现匹配字符即给与显示。如输入“181”后显示:

    选择1-3即可进入其详细页面。此处不再演示。
    在view模式下键入3进行分类(category)匹配。该模式会列出所有的分类,并可根据用户选择的分类进行罗列(其中未设置的分类被标记为Unset):

    根据提示选择1-2即可进入相应页面。选择0即可退出。
    在view模式下键入4进行全局匹配。该模式会列出所有的成员:

    在view模式下键入5退出view模式。
    在主菜单中键入5退出程序。
    2 评论 28 下载 2019-02-01 17:54:46 下载需要10点积分
  • 基于C#和Sql Server的图书管理系统

    1 课程设计意义与目标1.1 课程设计的意义《可视化编程技术课程设计》是在学生学习完《可视化编程技术》以后进行的设计性集中实践课程,通过课程集中实践,目的是使学生能加深对理论教学内容的理解,学会可视化编程技术的综合应用,培养学生分析问题的解决问题的能力。
    1.2 课程设计的目标通过课程集中实践,要求学生加深对讲授内容的理解,累积经验、学会独立上机调试程序;并且逐步达到综合运用封装、继承和多态等C#难点知识,更深地理解面向对象程序设计的基本概念与方法,从而学会利用C#语言解决一般应用问题,能设利用可视化编程技术开发复杂和综合性的计算机管理信息系统,并为后续专业课程的学习奠定程序设计基础。
    2 课程设计的题目2.1 设计题目概述
    图书管理系统
    2.2 开发环境搭建
    开发语言:C#
    开发工具:Visual Studio 2010
    数据库管理工具:SQL Server 2008

    3 系统的设计与实现3.1 物理数据模型设计




    3.2 主要界面设计
    界面中用了textbox,label,combobox,textbox用于获取数据输入,combobox用于数据选取,button用于单击事件。在用户类别可以选择用户类型,管理员。

    界面中用了textbox,label,combobox,dataGridView,textbox用于获取数据输入,combobox用于数据选取,button用于单击事件,dataGridView用于数据显示。
    功能:可以增加,修改,删除图书。

    界面中用了textbox,label,combobox,dataGridView,textbox用于获取数据输入,combobox用于数据选取,button用于单击事件,dataGridView用于数据显示,用户借书,管理员可以查看用户的借书记录。

    密码修改:可以更改当前用户登录的密码,旧密码符合条件,新密码和旧密码不能相同,新密码和确认密码的相同的条件。

    界面中用了textbox,label,combobox,dataGridView,textbox用于获取数据输入,combobox用于数据选取,button用于单击事件,dataGridView用于数据显示,用户借书,管理员可以查看用户的借书记录。模糊查询在下面有具体介绍。

    管理员信息管理:可以对管理员进行增添改查。

    书库管理:对书库进行增删改查。

    书库查询:按地区分类和按书库编号分类,第二个combobox会自动加载所有记录的值供你选择。

    书库管理:用来增加书库,删除,修改。

    用户管理:对用户的增删改查。

    用户登录之后的界面。

    管理员登录之后的界面。
    4 调试过程中出现的问题及解决办法4.1 数组索引超出界限解决方法:利用断点调试,重新赋值。

    4.2 从试图获取数据时,应添加新的字段
    5 个人体会及建议在这次课设中,基本都是在学习新知识的过程,从powerdesigner到动软生成软件,让我知道了这个工具的强大之处,渐渐开始会用一点,在第一天晚上想重做一遍学生信息管理系统,不料,动软生成的路径没改,直接给覆盖了,所以只能重头再来,在这时就有想法想做个不一样的系统,上学期用C++做了个图书管理系统,有点印象,就选择做这个,一开始一直模范着你给的day1,day2,day3,看不懂BLL,DAL,MODEL之间的关系,经过思考,理解了他们之间的关系,BLL负责储存方法相当于API,DAL负责储存数据,MODEL负责各个对象的类,后面理解了就开始自己写,用户负责借书,用户的增删改,添加用户,管理员负责查询书库,书库的增删改查,查询图书,图书的增删改查,借书记录的增删改查,在这个过程中不仅了解了动软生成软件的机制,而且可熟练的利用这个工具,在这个工具的基础之上,我写出了更多好用的函数供自己使用。在windows应用开发上了解更多控件和控件属性的使用和结合,可以做出功能和界面相对完整的程序,总之,在这次课设中受益匪浅。
    3 评论 209 下载 2019-03-10 21:27:29 下载需要8点积分
  • Python实现的基于词典方法和机器学习方法的中文情感倾向分析

    1 项目介绍1.1 项目背景文本情感分析又称意见挖掘, 是对包含用户观点、 喜好、 情感等主观性文本进行挖掘、 分析及判别它是一个多学科交叉的研究领域, 涉及概率论、数据统计分析、计算机语言学、自然语言处理、机器学习、信息检索、本体学(Ontology) 等多个学科及其相关技术 。
    鉴于其在用户意见挖掘、 垃圾邮件过滤及舆情分析等 多个领域具有重要的应用价值, 文本情感分析正受到国内外众多研究机构和学者的重视。
    情感分析可归纳为情感信息抽取、 情感信息分类及情感信息的检索与归纳三项层层递进的研究任务 。本文研究的重点是情感信息分类, 旨在将文本情感分为褒义、 贬义两类或者更细致的情感类别。
    按照分析文本的粒度不同, 情感信息分类可分为词语级 、短语级 、句子级 、篇章级 等几个研究层次。
    目前, 情感分类大致涌现出两种研究思路:基于情感知识和基于特征。前者主要是基于已有的情感词典或情感知识库 对文本中带有情感或极性的词( 或词语单元) 进行加权求和, 而后者主要是对文本提取具有类别表征意义的 特征, 再基于这些特征使用机器学习算法进行分类。
    1.2 项目目标
    使用机器学习 和 情感词典 这两种方法 分别对中文新闻类文本进行情感极性分析
    输入一段新闻文本能够得到文本的情感极性

    1.3 目标人群
    需要对已有中文文本数据进行情感分析的企业及用户
    APP中需要集成中文文本情感分析功能的开发人员
    从事中文文本情感分析与挖掘的研究人员

    2 项目需求分析2.1 系统KAOS图
    2.2 用例分析2.2.1 概述本系统有2个参与者,包括用户和管理员,用户为本系统所提供服务的使用者。用户可以直接使用系统所暴露的情感分析接口向后台传递一个中文字符串,从而获得一个文本的正负向情感结果。
    用户也可以对系统返回的结果进行判断并反馈错误,具体操作如下:

    在文本框输入一段已知情感正负向的中文文本并且提供这段文本的正负向情感信息, 来获取结果。
    用户判断系统返回的情感分析结果有误, 点击文本框下方的反馈错误按钮, 反馈错误样例
    系统会自动的将用户所提供的错误样例的信息插入到“待审核样例数据库” 中, 并通知管理员去审核。
    管理员在审核错误样例, 系统得到每条待审核错误样例的审核结果
    如果错误样例的审核结果是 : 有效, 那么这条错误样例将被记录到错误样例数据库中
    被管理员审核过的待审核测试样例从待审核样例数据库中删除

    管理员为本系统的管理人员。

    管理员也可以通过情感词典管理模块来对情感词典进行修改等操作
    管理员还可以审核用户反馈的错误样例,和直接访问控制系统样例数据数据库, 对系统的样例进行管理,以方便开发人员修复系统的漏洞, 进一步提高系统提供的服务质量

    2.3 系统用例总图
    3 概要设计3.1 原型设计
    3.2 业务架构
    3.3 技术架构
    3.4 部署结构
    4 详细设计目前,情感倾向分析的方法主要分为两类:一种是基于情感词典的方法;一种是基于机器学习的方法,如基于大规模语料库的机器学习。前者需要用到标注好的情感词典,英文的词典有很多,中文主要有知网整理的情感词典Hownet和台湾大学整理发布的NTUSD两个情感词典,还有哈工大信息检索研究室开源的《同义词词林》可以用于情感词典的扩充。基于机器学习的方法则需要大量的人工标注的语料作为训练集,通过提取文本特征,构建分类器来实现情感的分类。
    文本情感分析的分析粒度可以是词语、句子也可以是段落或篇章。段落篇章级情感分析主要是针对某个主题或事件进行倾向性判断,一般需要构建对应事件的情感词典,如电影评论的分析,需要构建电影行业自己的情感词典效果会比通用情感词典效果更好;也可以通过人工标注大量电影评论来构建分类器。句子级的情感分析大多事通过计算句子里包含的所有情感词的平均值来得到。
    篇章级的情感分析,也可以通过聚合篇章中所有的句子的情感倾向来计算得出。因此,针对句子级的情感倾向分析,既能解决较短文本的情感分析,同时也可以是篇章级文本情感分析的基础。
    4.1 过程流设计使用情感词典方法的过程流

    使用机器学习方法的过程流

    5 算法设计5.1 机器学习算法设计机器学习的方法精确度更高,因为词典匹配会由于语义表达的丰富性而出现很大误差,而机器学习方法不会。而且它可使用的场景更多样。无论是主客观分类还是正负面情感分类,机器学习都可以完成任务。而无需像词典匹配那样要深入到词语、句子、语法这些层面。
    而词典方法适用的语料范围更广,无论是手机、电脑这些商品,还是书评、影评这些语料,都可以适用。但机器学习则极度依赖语料,把手机语料训练出来的的分类器拿去给书评分类,那是注定要失败的。
    使用机器学习进行情感分析,可以换一个相同意思的说法,就是用有监督的(需要人工标注类别)机器学习方法来对文本进行分类。
    这点与词典匹配有着本质的区别。词典匹配是直接计算文本中的情感词,得出它们的情感倾向分值。而机器学习方法的思路是先选出一部分表达积极情感的文本和一部分表达消极情感的文本,用机器学习方法进行训练,获得一个情感分类器。再通过这个情感分类器对所有文本进行积极和消极的二分分类。最终的分类可以为文本给出0或1这样的类别,也可以给出一个概率值,比如”这个文本的积极概率是90%,消极概率是10%“。
    Python 有良好的程序包可以进行情感分类,那就是Python 自然语言处理包,Natural Language Toolkit,简称NLTK 。同时Python 也有良好的程序包可以进行对中文文本进行分析,如jiaba。
    5.1.1 算法框架
    5.1.2 数据集先以带有正向标签和负向标签的各1500条真实的中文酒店评论语料作为训练集用以训练分类器,剩余带有正向标签和负向标签的各500条真实的中文酒店评论语料作为测试集测试不同分类算法、不同特征提取方法、不同维度的准确度。
    最后选择准确度最高的方案,将上述带有正向标签和负向标签的各2000条真实的中文酒店评论语料作为训练集训练最终存储的分类器。
    保存用户输入的语句和反馈,定期挑拣出新增的训练数据以优化分类器。
    5.1.3 维度和权重不同分类方法、不同征选取方法、不同维度得到的测试准确率如下表:
    以所有词为特征提取方法



    分类算法
    准确率




    BernoulliNB
    0.704


    MultinomiaNB
    0.864


    LogisticRegression
    0.836


    SVC
    0.555


    LinearSVC
    0.821


    NuSVC
    0.843



    以所有双词搭配为特征提取方法



    分类算法
    准确率




    BernoulliNB
    0.56


    MultinomiaNB
    0.854


    LogisticRegression
    0.827


    SVC
    0.513


    LinearSVC
    0.814


    NuSVC
    0.781



    以所有词和所有双词搭配为特征提取取方法



    分类算法
    准确率




    BernoulliNB
    0.64


    MultinomiaNB
    0.876


    LogisticRegression
    0.843


    SVC
    0.536


    LinearSVC
    0.846


    NuSVC
    0.842



    以信息量丰富的所有词为特征提取取方法



    分类算法\维度
    500
    1000
    1500
    2000
    2500
    3000
    3500
    4000
    4500
    5000
    5500
    6000
    6500
    7000
    7500
    8000
    8500
    9000




    BernoulliNB
    0.801
    0.778
    0.773
    0.771
    0.767
    0.772
    0.785
    0.779
    0.774
    0.756
    0.745
    0.745
    0.745
    0.745
    0.753
    0.75
    0.749
    0.748


    MultinomiaNB
    0.857
    0.863
    0.864
    0.869
    0.866
    0.869
    0.87
    0.877
    0.872
    0.875
    0.877
    0.877
    0.877
    0.877
    0.877
    0.877
    0.874
    0.874


    LogisticRegression
    0.827
    0.832
    0.825
    0.836
    0.832
    0.834
    0.838
    0.84
    0.837
    0.839
    0.837
    0.837
    0.837
    0.837
    0.84
    0.837
    0.838
    0.838


    SVC
    0.806
    0.742
    0.714
    0.688
    0.674
    0.664
    0.639
    0.61
    0.591
    0.584
    0.571
    0.571
    0.571
    0.571
    0.57
    0.565
    0.565
    0.565


    LinearSVC
    0.826
    0.821
    0.815
    0.808
    0.814
    0.821
    0.818
    0.814
    0.813
    0.816
    0.815
    0.815
    0.815
    0.815
    0.813
    0.815
    0.822
    0.822


    NuSVC
    0.831
    0.835
    0.837
    0.845
    0.844
    0.844
    0.843
    0.846
    0.844
    0.844
    0.847
    0.847
    0.847
    0.847
    0.845
    0.843
    0.845
    0.843



    以信息量丰富的所有词和所有双词搭配为特征选取方法



    分类算法\维度
    500
    1000
    1500
    2000
    2500
    3000
    3500
    4000
    4500
    5000
    5500
    6000
    6500
    7000
    7500
    8000
    8500
    9000




    BernoulliNB
    0.798
    0.773
    0.775
    0.769
    0.772
    0.777
    0.784
    0.776
    0.767
    0.76
    0.747
    0.747
    0.747
    0.747
    0.75
    0.759
    0.759
    0.759


    MultinomiaNB
    0.854
    0.863
    0.867
    0.871
    0.871
    0.874
    0.874
    0.874
    0.874
    0.876
    0.876
    0.876
    0.876
    0.876
    0.877
    0.873
    0.873
    0.873


    LogisticRegression
    0.828
    0.831
    0.829
    0.834
    0.834
    0.832
    0.834
    0.834
    0.834
    0.839
    0.837
    0.837
    0.837
    0.837
    0.836
    0.835
    0.835
    0.835


    SVC
    0.806
    0.739
    0.712
    0.688
    0.671
    0.659
    0.633
    0.604
    0.595
    0.583
    0.565
    0.565
    0.565
    0.565
    0.565
    0.565
    0.565
    0.565


    LinearSVC
    0.824
    0.807
    0.817
    0.812
    0.81
    0.818
    0.811
    0.809
    0.815
    0.813
    0.814
    0.814
    0.814
    0.814
    0.811
    0.811
    0.811
    0.811


    NuSVC
    0.829
    0.84
    0.835
    0.84
    0.844
    0.844
    0.846
    0.846
    0.845
    0.849
    0.852
    0.852
    0.852
    0.852
    0.853
    0.855
    0.855
    0.855



    5.1.4 关键步骤
    利用python程序包中文分词工具python对语料进行分词。
    for file_name in FileNames: full_file_name = os.path.join(FindPath, file_name) if 'utf8' in full_file_name: with open(full_file_name, 'r', encoding='utf-8') as pos_f: pos_text = pos_f.read() pos_text = ''.join(pos_text.split()) # pos_text = re.sub(string.punctuation, "", pos_text) pos_text = re.sub("[\s+\.\!\/_,$%^*(+\"\']+|[+——!,。?、~@#¥%……&*()~-]+", "", pos_text) pos_list = jieba.cut(pos_text, cut_all=False) pos_words.append(list(pos_list))
    特征提取方法

    把所有词作为特征
    def bag_of_words(words): return dict([(word, True) for word in words])
    把双词搭配作为特征
    def bigram(words, score_fn=BigramAssocMeasures.chi_sq, n=1000): bigram_finder = BigramCollocationFinder.from_words(words) # 把文本变成双词搭配的形式 bigrams = bigram_finder.nbest(score_fn, n) # 使用了卡方统计的方法,选择排名前1000的双词 return bag_of_words(bigrams)
    把所有词和双词搭配一起作为特征
    def bigram_words(words, score_fn=BigramAssocMeasures.chi_sq, n=1000): tuple_words = [] for i in words: temp = (i,) tuple_words.append(temp) bigram_finder = BigramCollocationFinder.from_words(words) bigrams = bigram_finder.nbest(score_fn, n) # 使用了卡方统计的方法,选择排名前n的双 return bag_of_words(tuple_words + bigrams) # 所有词和(信息量大的)双词搭配一起作为特征

    特征选择方法

    计算整个语料里面所有的信息量
    def create_word_scores(): posWords = pickle.load(open(pos_f, 'rb')) negWords = pickle.load(open(neg_f, 'rb')) posWords = list(itertools.chain(*posWords)) # 把多维数组解链成一维数组 negWords = list(itertools.chain(*negWords)) # 同理 word_fd = FreqDist() # 可统计所有词的词频 cond_word_fd = ConditionalFreqDist() # 可统计积极文本中的词频和消极文本中的词 for word in posWords: word_fd[word] += 1 cond_word_fd["pos"][word] += 1 for word in negWords: word_fd[word] += 1 cond_word_fd["neg"][word] += 1 pos_word_count = cond_word_fd['pos'].N() # 积极词的数量 neg_word_count = cond_word_fd['neg'].N() # 消极词的数量 total_word_count = pos_word_count + neg_word_count word_scores = {} for word, freq in word_fd.items(): pos_score = BigramAssocMeasures.chi_sq(cond_word_fd['pos'][word], (freq, pos_word_count), total_word_count) # 计算积极词的卡方统计量,这里也可以计算互信息等其它统计量 neg_score = BigramAssocMeasures.chi_sq(cond_word_fd['neg'][word], (freq, neg_word_count), total_word_count) # 同理 word_scores[word] = pos_score + neg_score # 一个词的信息量等于积极卡方统计量加上消极卡方统计量 return word_scores # 包括了每个词和这个词的信息量
    计算整个语料里面所有词和双词搭配的信息量
    def create_word_bigram_scores(): posdata = pickle.load(open(pos_f, 'rb')) negdata = pickle.load(open(neg_f, 'rb')) posWords = list(itertools.chain(*posdata)) negWords = list(itertools.chain(*negdata)) bigram_finder = BigramCollocationFinder.from_words(posWords) posBigrams = bigram_finder.nbest(BigramAssocMeasures.chi_sq, 5000) bigram_finder = BigramCollocationFinder.from_words(negWords) negBigrams = bigram_finder.nbest(BigramAssocMeasures.chi_sq, 5000) pos = posWords + posBigrams # 词和双词搭配 neg = negWords + negBigrams word_fd = FreqDist() cond_word_fd = ConditionalFreqDist() for word in pos: word_fd[word] += 1 cond_word_fd["pos"][word] += 1 for word in neg: word_fd[word] += 1 cond_word_fd["neg"][word] += 1 pos_word_count = cond_word_fd['pos'].N() neg_word_count = cond_word_fd['neg'].N() total_word_count = pos_word_count + neg_word_count word_scores = {} for word, freq in word_fd.items(): pos_score = BigramAssocMeasures.chi_sq(cond_word_fd['pos'][word], (freq, pos_word_count), total_word_count) # 计算积极词的卡方统计量,这里也可以计算互信息等其它统计量 neg_score = BigramAssocMeasures.chi_sq(cond_word_fd['neg'][word], (freq, neg_word_count), total_word_count) word_scores[word] = pos_score + neg_score return word_scores
    根据信息量进行倒序排序,选择排名靠前的信息量的词
    def find_best_words(word_scores, number): best_vals = sorted(word_scores.items(), key=lambda w_s: w_s[1], reverse=True)[:number] # 把词按信息量倒序排序。number是特征的维度,是可以不断调整直至最优的 best_words = set([w for w, s in best_vals]) return best_words
    把选出的这些词作为特征(这就是选择了信息量丰富的特征)
    def best_word_features(words): # load_data() # word_scores = create_word_bigram_scores() global best_words # best_words = find_best_words(word_scores, 7500) return dict([(word, True) for word in words if word in best_words])

    分割数据及赋予类标签
    # 积极def pos_features(feature_extraction_method): posFeatures = [] for i in pos_review: posWords = [feature_extraction_method(i), 'pos'] # 为积极文本赋予"pos" posFeatures.append(posWords) return posFeatures
    # 消极def neg_features(feature_extraction_method): negFeatures = [] for j in neg_review: negWords = [feature_extraction_method(j), 'neg'] # 为消极文本赋予"neg" negFeatures.append(negWords) return negFeatures
    使用训练集用不同的分类算法训练分类器,用分类器对开发测试集里面的数据进行分类,给出分类预测的标签,对比分类标签和人工标注的差异,计算出准确度
    def score(classifier): classifier = nltk.SklearnClassifier(classifier) # 在nltk 中使用scikit-learn的接口 classifier.train(train) #训练分类器 pred = classifier.classify_many(dev) # 对开发测试集的数据进行分类,给出预测的标签 return accuracy_score(tag_dev, pred) # 对比分类预测结果和人工标注的正确结果,给出分类器准确度
    def try\_diffirent\_classifiers(): results = list() results.append(score(BernoulliNB())) results.append(score(MultinomialNB())) results.append(score(LogisticRegression())) results.append(score(SVC())) results.append(score(LinearSVC())) results.append(score(NuSVC())) return results
    选择准确度最高的分类算法、特征提取方法、维度得到的分类器并存储
    def store_classifier(): load_data() word_scores = create_word_bigram_scores() global best_words best_words = find_best_words(word_scores, 7500) posFeatures = pos_features(best_word_features) negFeatures = neg_features(best_word_features) trainSet = posFeatures + negFeatures MultinomialNB_classifier = SklearnClassifier(MultinomialNB()) MultinomialNB_classifier.train(trainSet) pickle.dump(MultinomialNB_classifier, open('../out/classifier.pkl', 'wb'))

    5.2 情感词典算法我们已知:

    中文文本以字符串的方式传进系统段落与段落之间使用换行符来划分句子之间以 “ 。,?!” 来划分短句子,短句子之间以 “ , ” 来划分
    5.2.1 基本思想
    一个中文文本的情感值由构成它的所有的段落的情感值所决定一个段落的情感值由构成它的所有的长句子决定一个长句子的情感值由构成它的所有短句子的情感值决定一个短句子的情感值由构成它的所有词语的情感值决定将组成一个短句子的所有词语的情感值查找出来, 记录于一个列表中, 将一定的算法施加于这个列表, 得到短句子的情感值将组成一个长句子的所有短句子的情感值记录于一个列表中, 将一定的算法施加于这个列表, 得到长句子的情感值将组成一个段落的所有长句子的情感值记录于一个列表中, 将一定的算法施加于这个列表, 得到段落的情感值将组成一个篇章的所有段落的情感值记录于一个列表中, 将一定的算法施加于这个列表, 得到篇章的情感值
    5.2.3 算法流程
    将一个中文文本转换为一个有短句子字符串组成列表对每一个短句子字符串进行如下操作:
    使用 jieba 分词系统将一个短句子转换成 词语, 词性对 的列表使用词语的词性筛选出潜在的情感词语, 在已有的情感词典中查找这些潜在的情感词语所查找的情感词语的情感词语分类 以及 它的情感值组合成一个词典并记录到一个列表中使用相应的算法处理这个列表, 得出这个小句子的情感极值
    将一个中文文本中的所有短句子的情感极值记录在一个列表中使用相应的算法处理这个列表, 得出整个中文文本的情感极值

    算法特性
    算法准确率基于分词的准确率, 以及情感词典的准确率对句式简单的句子的识别准确率高对复杂句子的分析依赖于复杂的文本处理算法
    关键代码
    加载情感词典
    #-----------------------------------------------------------------------# 初始化词典 , 加载自定义jieba词典# para_in : dic_kind 词典类型# 1. 知网# 2. 大连理工# 3. NTUSD# 4. 清华XXX# 5. 清华情感词极值词典# else None# para_out: 无#-----------------------------------------------------------------------def __init_dic__(dic_kind): global ext_dic jieba.load_userdict(dic_root_path + 'create_by_huzehao/jieba_dic.txt') __path__ = dic_root_path + "zhiwang\\" ext_dic = Modules.load_dic.load_ext_dic(__path__, "extent_Lv_") # 初始化程度副词词典 __init__no_word_list() if dic_kind == 1: #知网 __init_zhiwang_dic__() elif dic_kind == 2: #大连理工 __init_dllg_dic__() elif dic_kind == 3: #NTUSD __init_ntusd_dic__() elif dic_kind == 4: #清华大学 李建军 __init_tsinghua_dic__() elif dic_kind == 5: #情感极值词典 __init_extreme_dic__() else: return None
    得出词语详细信息
    #-----------------------------------------------------------------------#给出一个中文词语, 及其词性, 返回一个字典, 里面包含 词语 词性 分数 情感词类型# eg: {'n': '恶劣', 'k': 'a', 's': -1, 'p': 'neg'}# n:词语名 k: 词性 s:分数 p: 属性# para_in : word 词语# para_in : kind 词性# para_in : dic_kind 字典类型# para_in : All 是否为全模式(默认为False)# True :对所有传入的词语均返回一个详细信息# False :只对词性在 sense_word_kind_set 里的词返回一个详细信息# para_out: dic 描述一个词语的详细信息#-----------------------------------------------------------------------def find_word_info(word, kind, dic_kind, All = True): dic = {} def __setdic__(k, s, p = None): dic['n'] = word #word dic['k'] = k #kind dic['s'] = s #score dic['p'] = p #property def __common__(pos_dic, neg_dic): #如果词语是正面情感词或负面情感词的时候, 进行该操作 score = __getScore__(pos_dic, word) if score != 0: __setdic__(kind, score, 'pos') else: score = __getScore__(neg_dic, word) if score != 0: __setdic__(kind, score, 'neg') else: # 有意义的词被遗漏了 __setdic__(kind, score) ignoredWordList.write("{} {} {}\n".format(word, kind, score)) if word in no_word_set: __setdic__('no', None, None) elif kind in sense_word_kind_set: score = __getScore__(ext_dic, word) if score != 0: __setdic__(kind, score, 'ext') else: if dic_kind == 1: #知网 __common__(zhiwang_pos_sen_dic, zhiwang_neg_sen_dic) elif dic_kind == 2: #大连理工 pass elif dic_kind == 3: #ntusd __common__(ntusd_pos_dic,ntusd_neg_dic) elif dic_kind == 4: #清华 李建军 __common__(tsinghua_pos_dic, tsinghua_neg_dic) elif dic_kind == 5: #情感极值词典 score = __getScore__(word_extreme_dic, word) if score > 0: __setdic__(kind, score, "pos") elif score < 0: __setdic__(kind, score, "neg") else: #情感词语遗漏了 ignoredWordList.write("{} {} {}\n".format(word, kind, score)) if len(dic) > 0: return dic elif All: __setdic__(kind, 0, None) return dic else: return None
    计算短句子情感值
    #-----------------------------------------------------------------------# para_in :tiny_sentence# para_in :# para_in : # para_out: score#-----------------------------------------------------------------------def _get_group_score(tiny_sentence,group = [{}], stream = None): if len(group) > 0: stack = [] score = None score_item = None pair = getCommentPair(tiny_sentence,group) if pair != None: score_item = conn.execute('select s from polysemy where a = ? and n = ?', pair).fetchone() if score_item != None: score = score_item[0] stack.append(score) for item in group: if item.get('k') == 'no': stack.append(-1) elif item.get('k') == 'ext': stack.append(item.get('s')) return __CaculateScoreOfGroup__(stack, False), stack, pair #else: score, stack = get_group_score(group) return score, stack, pair return 0, None
    获取整个中文文本的情感值
    #-----------------------------------------------------------------------# 暴露接口# para_in : text 文件内容 字符串# para_in : dic_kind 词典类型 整数值# 1: 知网# 2: 大连理工# 3: ntusd# 4:XXX# 5:极值词典# para_out: score 文本情感值#-----------------------------------------------------------------------def getScoreFromString(text, dic_kind): __init_dic__(dic_kind) pgen = get_paragraph(text) ggen = get_group(pgen) _score_sum_ = 0 if dic_kind == 4: _score_sum_ = _text_processing_(text) else: for group in ggen: wordList = splict_group_into_list(group, dic_kind) score, stack = _get_group_score(wordList) _score_sum_ += score #print(group, score, stack, wordList) return _score_sum_
    5.3 类设计
    3 评论 73 下载 2018-11-05 11:03:06 下载需要11点积分
  • 基于C++的模拟Unix文件系统

    一、实验介绍本次实验的内容是完成一个 UNIX文件系统的子集的模拟实现。
    实验要求:

    完成文件卷结构设计
    I节点结构设计
    目录结构
    用户及组结构
    文件树结构
    实现功能如下命令:
    Ls 显示文件目录Chmod 改变文件权限Chown 改变文件拥有者Chgrp 改变文件所属组Pwd 显示当前目录Cd 改变当前目录Mkdir 创建子目录Rmdir 删除子目录Umask 文件创建屏蔽码Mv 改变文件名Cp 文件拷贝Rm 文件删除Ln 建立文件联接Cat 连接显示文件内容Passwd 修改用户口令
    二、实验环境
    操作系统:windows 10 64位
    开发工具:visual studio 2017
    程序类型:win32 控制台应用程序
    引用库:
    #include <iostream>#include <conio.h>#include <fstream>#include <time.h>#include <stack>#include <stdio.h>#include <windows.h>#include "dataStruct.h"#include "error.h"

    三、实验设计3.1 系统流程图
    程序开始执行后,程序会先将资源文件读取,并以一文件指针保存在全局变量virtualDisk中。
    读取文件成功以后,通过文件指针访问超级块所在盘块,读取内容保存在全局变量super中。
    加载超级块以后,程序通过iget操作获取到root节点,保存在全局变量root中。
    接下来,进入登录界面,用户输入密码和口令,程序根据用户的输入,读取用户文件,判断是否登录成功,成功以后进入程序的主界面。
    3.2 文件卷设计
    本程序的模拟磁盘的大小是8MB,每个盘块的大小设定为1KB,共8192个盘块。其中0号盘块在本程序中没有使用,但保留了下来。1号盘块保存超级块的信息。2-911盘块保存的是finode节点的信息。从912盘块开始,都是存储文件内容的盘块,使用成组链接法来管理,每组的盘块数是20。关于结点等数据结构会在下面部分详细介绍。
    3.3 实现命令操作本程序主要实现了以下的操作:
    Ls 显示文件目录Chmod 改变文件权限Chown 改变文件拥有者Chgrp 改变文件所属组Pwd 显示当前目录Cd 改变当前目录Mkdir 创建子目录Rmdir 删除子目录Mv 改变文件名Cp 文件拷贝Cat 连接显示文件内容Passwd 修改用户口令此外本程序还额外完成了以下指令:
    touch 新建一个文件>> 向文件里面追加内容指令的详细介绍和解释会在第五章模块详解中具体介绍。
    四、数据结构4.1超级快数据结构1#块为超级块(superblock)。磁盘的回收和索引结点的分配与回收,将涉及到超级块。超级块是专门用于记录文件系统中盘块和磁盘索引节点使用情况的一个盘块,其中含有以下各个字段:

    size:文件系统的盘块数
    freeBlock:空闲盘块号栈,即用于记录当前可用的空闲盘块编号的栈
    nextFreeBlock:当前空闲盘快号数,即在空闲盘块号栈中保存的空闲盘块号的数目。他也可以被视为空闲盘块号栈的指针
    freeInode:空闲磁盘i结点编号栈,即记录了当前可用的素偶皮空闲结点编号的栈
    nextFreeInode:空闲磁盘i结点数目,指在磁盘i结点栈中保存的空闲i结点编号的数目,也可以视为当前空闲i结点栈顶的指针
    freeBlockNum:空闲盘块数,用于记录整个文件系统中未被分配的盘块个数
    freeInodeNum:空闲i结点个数,用于记录整个文件系统中未被分配的节点个数
    lastLogin:上次登录时间

    struct supblock{ unsigned int size; //the size of the disk unsigned int freeBlock[BLOCKNUM]; //the stack of the free block unsigned int nextFreeBlock; //the pointer of the next free block in the stack unsigned int freeBlockNum; //the totally number of the free block in the disk unsigned int freeInode[INODENUM]; //the stack of the free node unsigned int freeInodeNum; //the totally number of the free inode in the disk unsigned int nextFreeInode; //the next free inode in the stack unsigned int lastLogin;};
    4.2 结点数据结构关于索引结点,本程序的主要由两种结构的定义,分别是内存索引结点和磁盘索引结点。
    磁盘索引结点

    mode:文件类型及属性
    fileSize:文件大小
    fileLink:文件连接数
    owner:所属用户名
    group:所属用户组
    modifyTime:修改时间
    createTime:创建时间
    addr:盘块地址数组

    结构定义
    struct finode{ int mode; long int fileSize; int fileLink; char owner[MAXNAME]; char group[GROUPNAME]; long int modifyTime; long int createTime; int addr[6]; char black[45]; //留空,以备内容扩充时不会影响结构大小};
    内存索引结点
    内存索引结点是保存在内存中索引结点的数据结构,当文件第一次被打开时,文件的索引结点从模拟磁盘上读出,并保存在内存中,方便下一次文件的打开。

    finode:磁盘索引结点结构,保存从磁盘读出的索引结点信息
    parent:父级内存索引结点指针
    inodeID:索引结点号
    userCount:用户打开数

    结构定义
    struct inode{ struct finode finode; struct inode *parent; unsigned short int inodeID; //the node id int userCount; //the number of process using the inode};
    4.3 文件数据结构文件目录项
    文件目录项由文件名和文件索引结点号组成。

    directName:文件名或目录名
    inodeID:文件索引结点号

    结构定义
    struct direct{ char directName[DIRECTNAME]; unsigned short int inodeID;};
    4.4 目录数据结构目录结构

    dirNum:目录数目
    direct:目录项数组

    结构定义
    struct dir{ int dirNum; struct direct direct[DIRNUM];};
    对于目录类,它的内容都是以dir结构保存在磁盘中的,并以dir结构读取。
    五、模块详解5.1 文件操作关于文件,本模拟系统主要实现了以下的操作:

    Chgrp 改变文件所属组:

    命令格式
    chgrp [组] [文件]
    命令功能
    chgrp命令可采用群组名称或群组识别码的方式改变文件或目录的所属群组。使用权限是超级用户
    命令参数:

    [组]:用户组名称[文件]:文件名称
    Chown改变文件拥有者:
    命令格式
    chown [用户名][文件]
    命令功能
    通过chown改变文件的拥有者
    命令参数

    [用户名]:要更改的新的用户名[文件]:要修改的文件名
    Chmod改变文件权限:
    命令格式
    chmod [mode] [文件名]
    命令功能
    chmod命令用于改变linux系统文件或目录的访问权限。用它控制文件或目录的访问权限
    命令参数
    [mode] 文件权限码r=4,w=2,x=1若要rwx属性则4+2+1=7若要rw-属性则4+2=6;若要r-x属性则4+1=7。 [文件名] 要更改的文件名称

    Rmdir删除文件(兼顾删除子目录功能):

    命令格式
    rmdir 文件名/目录名
    命令功能
    该命令从一个目录中删除一个文件或者是目录项,若是目录项,目录项下的所有文件和目录也将被删除

    Cat连接显示文件内容:

    命令格式
    cat 文件名
    命令功能
    cat命令的用途是连接文件并打印。这个命令常用来显示文件内容

    Cp文件拷贝:

    命令格式
    cp 源目的
    命令功能
    cp命令用来复制文件或者目录,是UNIX系统中最常用的命令之一
    命令参数
    源:要复制的源文件目的:要复制的目的文件

    touch新建一个文件:

    命令格式
    touch 文件名
    命令功能
    用于创建一个新的不存在文件



    向文件里面追加内容:

    命令格式
    文件名 内容
    命令功能
    改指令用于向已尽存在的文件中追加内容,内容将会追加到问价你的末尾



    mv修改文件名

    命令格式
    mv 文件 新文件名
    命令功能
    改指令用于修改目录中已经存在文件名。


    5.2 目录操作关于目录,本模拟系统主要实现以下几个指令:

    Ls显示文件目录:

    命令格式
    ls
    命令功能
    ls命令是UNIX下最常用的命令。ls命令就是list的缩写,用来打印出当前目录的清单

    Pwd 显示当前目录:

    命令格式
    pwd
    命令功能
    pwd 命令来查看”当前工作目录“的完整路径。 简单得说,每当你在终端进行操作时,你都会有一个当前工作目录。在不太确定当前位置时,就会使用pwd来判定当前目录在文件系统内的确切位置

    Cd 改变当前目录:

    命令格式
    cd 目录名
    命令功能
    cd指令的功能是进入目录,支持相对路径和绝对路径

    Mkdir 创建子目录:

    命令格式
    Mkdir 目录名
    命令功能
    mkdir指令是用于在当前目录下创建子目录的指令

    Rmdir 删除子目录:

    命令格式
    rmdir 文件名/目录名
    命令功能
    该命令从一个目录中删除一个文件或者是目录项,若是目录项,目录项下的所有文件和目录也将被删除


    5.3 主函数主函数中,最最核心的函数就是dispatch函数,它解析用户输入的指令,并解析出参数,调用用户需要的函数。其流程图如下:

    5.4 核心函数一下函数为整一个文件系统最最核心的功能,所有的操作都是建立在以下函数的基础上进行的:
    盘块读函数
    int bread(void * _Buf,unsigned short int bno,long int offset,int size,int count=1)
    该函数将指定的盘块号内容的读取到对应的数据结构中。
    盘块写函数
    int bwrite(void * _Buf,unsigned short int bno,long int offset,int size,int count=1)
    该函数将指定数据的内容写入到指定的盘块中。
    结点分配函数
    struct inode* ialloc()
    该函数的作用是为从超级块的空闲结点栈中取出一个新的结点,并初始化该结点。其流程图如图5.4.1所示。

    盘块分配函数
    int balloc()
    该函数的主要功能是从超级快的空闲盘块栈中取出一个空闲盘块号,若栈只剩一个空闲盘块,那么采用成组链接法,读取下一组的空闲盘块栈。函数流程图如图5.4.2所示。

    盘块回收函数
    int bfree(int bno)
    该函数的主要功能是回收空闲盘块,若超级块中的空闲盘块栈未满,则回收盘块号入栈,若空闲盘块栈满,则将栈内容写到新回收的盘块上,清空栈,并将新的盘块号入栈。流程图如图5.4.3。

    六、实验演示6.1 进入文件系统
    6.2 文件相关操作创建一个文件并在目录上显示

    向文件newtest.txt写入内容并链接显示内容

    修改文件名称

    6.3 目录相关操作显示当前目录下的文件详细信息

    显示当前目录路径

    help命令

    七、实验总结本次实验对自己来说是一个很大的挑战,看了很多的资料,书上和网上的技术博客,虽说不完全是自己写的代码,但是自己有很用心去读懂它,看懂它。通过这次实验,我了解操作体统中,文件是如何进行管理和规划。知道了结点,盘块的概念,了解了文件的组织结构,这对于未来我从事计算机底层开发来说意义重大。
    2 评论 26 下载 2019-04-14 23:23:57 下载需要11点积分
  • 基于C#和SQL SERVER数据库实现的餐饮收银系统

    1 需求分析分析思维导图

    2 部分原型设计图登录

    管理员主页面

    3 实际实现界面注册界面

    登录界面

    管理员主页面

    添加食物界面

    服务员订单界面

    修改食物详情界面

    未完成订单界面

    支付成功并找零界面

    已完成订单界面

    4 程序使用说明
    管理员账号和密码:admin, admin
    服务员账号和密码: test, test

    注:可自行注册账号并登录,但是只能注册服务员账号
    5 总结这次写C#作业还是挺累的。我按照正常的软件开发流程写这个作业。借用思维导图,花了大概两个小时进行需求分析和原型设计。当然,在分析的过程中,始终无法对整个项目进行细致入微地考虑,所以总有考虑不周的地方,比如进行原型设计的时候没有考虑到实现的难度,或者说考虑地不够,导致在项目进行时对原型进行了些许微调。
    整个分析的过程是非常有效的,尤其是数据库,在实施过程中只进行了很少的微调。由于C#语言基础知识掌握不够,只花一个小时看完教科书就上手干,其中走了不少弯路。比如在连接数据库的时候总是连接不上(花了一整天连接Sql Server,可还是不行),后来才知道是Visual Studio缺少组件,后来改用比较熟悉的MySQL才将数据库问题解决。
    另外一个需要值得反思的是把大把时间花在UI上是否值得。虽然项目写出来非常好看,但是功能模块并不多,只是够用。这次作业大概花了三天的时间才做完,是远超出预期的,但是在这过程中确实学到了很多,踩了很多坑,比如把项目写残了一次,不过幸好用版本控制工具git给回滚回来了。
    期间解决了很多问题,对C#的掌握更深了,总的来说还是比较满意的。
    1 评论 45 下载 2018-11-06 10:39:08 下载需要11点积分
  • 基于C语言和TCP Socket实现的Linux环境下的邮件收发客户端程序

    一、项目题目及功能
    项目题目:设计一个邮件收发客户端程序
    功能:设置邮件账户、接收服务器和发送服务器等

    接收邮件并显示在一个列表里 编写、发送新邮件可暂不考虑邮件内容的编码/解码问题删除邮件邮件标记:已读、未读

    注:参照outlook express、foxmail、thunderbird或Linux下的mail命令等。
    二、项目整体设计框架图及说明这是一个在Linux下用命令行操作的邮件收发系统。编程语言为C语言,实现过程中利用TCP套接字来与服务器实现数据交流。邮件功能涉及到两个个邮箱协议:SMTP、IMAP4、POP3。
    其中SMTP是发送邮件协议,IMAP4跟POP3是接受邮件协议,由于POP3协议不能完成邮件是否已读功能,接受邮件实现用的IMAP4协议。程序分模块实现,先实现用户账户信息的绑定(软件目前连接163邮箱的服务器,故只能实现163邮箱用户的)。再用TCP协议连接服务器,再分别实现邮件的收发功能,邮件的删除属于收件箱里的子功能。查看收件箱的邮件内容时,因未能实现base64转gb2312编码,所以用户无法直接阅读邮件的中文内容,只能解码英文字符。
    三、项目分模块设计说明、流程图
    模块一:主函数,用户获取用户命令,根据用户命令调用相应功能

    命令 mail 查看帮助信息及功能说明。命令 email inbox进入收件箱命令 email -h查看帮助文档命令 email setuser设定用户命令 email send 目标邮箱 发送邮件
    模块二:设定用户,用于设置账户信息
    模块三:发送邮件,用户输入邮件信息,实现邮件发送功能
    模块四:收件箱查看,可查看邮件信息,已读未读,同时在该模块里嵌套删除邮件的功能

    四、项目关键数据结构及说明设定用户信息
    char name[100]; //用户名char passwd[100]; //用户密码
    发送邮件
    char server1[56]="smtp.163.com";//服务器,该程序绑定到163邮箱char Subject[512]; //邮件主题char Content[6000]={0}; //邮件内容char From[128]; //邮件发送方地址char To[128]; //邮件接收方地址
    五、项目关键函数说明及流程图主函数接受用户命令
    int main(int argc,char* argv[]){ if (argc == 1){ //如果直接输入mail的话,直接进入收件箱 watch_help(); }else{ //判断mail命令之后的参数 if (strcmp(argv[1],"setuser") == 0){ //-user setUser(); }else if (strcmp(argv[1],"send") == 0){ //发送邮件,接受参数要发送到的账号 char* to = argv[2]; if (to == NULL){ printf("地址为空!\n"); return -1; } send_mail(to); }else if (strcmp(argv[1],"-h") == 0){ watch_help(); }else if (strcmp(argv[1],"inbox") == 0){ watch_inbox(); }else{ printf("输入选项错误,请查看帮助,命令为-h\n"); return -1; } } return 0;}
    设定用户信息
    //设置用户信息void setUser(){ printf("请输入账号:"); char name[100],*passwd; scanf("%s",name); getchar(); passwd = getpass("请输入密码:"); char* base64_name = base64_encode(name); char* base64_passwd = base64_encode(passwd); FILE* fp = fopen("email.conf","w"); fprintf(fp, "%s\n%s",base64_name,base64_passwd); fclose(fp);}
    发送邮件
    void send_mail(char* to){ //获取标题 printf("Subject: "); char subject[512]; fgets(subject,512,stdin); //获取邮件内容 printf("Content: "); char c; int p = 0; char content[6000]; while ((c = getchar())!=EOF){ content[p++] = c; } content[p] = '\0'; char name[100]; char passwd[100]; getNamePasswd(name,passwd);//从配置文件里获取用户信息 char* from = base64_decode(name);//将用户信息解密 sendmail(name,passwd,from, to, subject, content);//调用发送邮件的实现函数}
    发送邮件的实现函数
    int sendmail(char* name,char* passwd,char* from,char* to,char* subject,char* content){ //1.连接主机服务器 if(connectHost(server1,IPSTR_SMTP,25)<0){ return -1; } //2.登录 if(login(name, passwd) < 0){ fprintf(stderr,"Can Not LOGIN !\n"); return -1; } //3.发送邮件 if(from =="" ||to == ""||subject == ""||content == ""){ printf("arguments error!\n"); return -1; } sprintf(From, "MAIL FROM: <%s>\r\n", from); //使用TCP套接字连接 if((ret = send(sockfd, From, strlen(From), 0)) == SOCKET_ERROR){ return -1; } if(getResponse() < 0){ return -1; } sprintf(To, "RCPT TO: <%s>\r\n", to); if((ret = send(sockfd, To, strlen(To),0)) == SOCKET_ERROR){ return -1; } if(getResponse() < 0){ return -1; } send_data = "DATA\r\n"; if((ret = send(sockfd,send_data,strlen(send_data),0)) == SOCKET_ERROR){ return -1; } if(getResponse() < 0){ return -1; } sprintf(Content,"from:%s\nto:%s\nsubject:%s\n%s\r\n.\r\n",from,to,subject,content); if((ret= send(sockfd, Content, strlen(Content), 0)) == SOCKET_ERROR){ return -1; } memset(buffer, '\0', sizeof(buffer)); if(getResponse() < 0){ return -1; } if((ret = send(sockfd,"QUIT\r\n",strlen("QUIT\r\n"), 0)) == SOCKET_ERROR){ return -1; } if(getResponse() < 0){ return -1; } printf("Send Mail Successful!\n"); return 0;}
    收件箱(删除邮件在该函数内实现)
    //连接服务器,参数,服务器地址,ip地址,端口号 if(connectHost(server2,"123.125.50.47",143)<0){ return -1; } //登录 char ch[1024]; char name[100]; char passwd[100]; getNamePasswd(name,passwd); sprintf(ch, "a001 LOGIN %s %s\r\n",base64_decode(name),base64_decode(passwd)); ret = send(sockfd, (char *)ch, strlen(ch),0); if(ret == SOCKET_ERROR){ return -1; } if(getResponse() < 0){ return -1; } sprintf(ch, "a002 SELECT INBOX\r\n"); ret = send(sockfd, (char *)ch, strlen(ch),0); if(ret == SOCKET_ERROR){ return -1; } if(getResponse() < 0){ return -1; } char* p = strtok(buffer," \n"); p = strtok(NULL," \n"); int total = atoi(p); if(total == 0){ printf("邮箱为空!\n"); return -1; } for (int i=0;i<total;++i){ sprintf(ch, "a003 fetch %d FLAGS(SEEN)\r\n",i+1); ret = send(sockfd, (char *)ch, strlen(ch),0); if (ret == SOCKET_ERROR){ return -1; } if(getResponse() < 0){ return -1; } p = strtok(buffer,"\n"); char* q = strtok(p," "); q = strtok(NULL," "); q = strtok(NULL," "); q = strtok(NULL," "); q = strtok(NULL," "); q = strtok(q,")"); printf("第%d封邮件 %s):\n", i+1,q); sprintf(ch, "a003 fetch %d BODY[HEADER.FIELDS (DATE FROM TO SUBJECT)]\r\n",i+1); ret = send(sockfd, (char *)ch, strlen(ch),0); if (ret == SOCKET_ERROR){ return -1; } if(getResponse() < 0){ return -1; } q = strtok(buffer,"\n"); q = strtok(NULL,"\n"); printf("%s\n",q); q = strtok(NULL,"\n"); printf("%s\n",q); q = strtok(NULL,"\n"); printf("%s\n",q); q = strtok(NULL,"\n"); printf("%s\n",q); sprintf(ch, "a003 fetch %d BODY[1]\r\n",i+1); ret = send(sockfd, (char *)ch, strlen(ch),0); if (ret == SOCKET_ERROR){ return -1; } if(getResponse() < 0){ return -1; } p = strtok(buffer,"\n"); p = strtok(NULL,"\n"); printf("Content:\n%s\n",p); printf("-------------------------------------\n");}
    删除邮件
    printf("是否删除邮件?[y/n]\n"); char del; scanf("%c",&del); if(del == 'y'){ printf("请输入要删除邮件的编号:"); int delete_i; scanf("%d",&delete_i); while(delete_i > total){ printf("输入有误,请重新输入。\n"); printf("请输入要删除邮件的编号:"); scanf("%d",&delete_i); } sprintf(ch, "a004 STORE %d +flags (\\Deleted)\r\n",delete_i); ret = send(sockfd, (char *)ch, strlen(ch),0); if (ret == SOCKET_ERROR){ return -1; } if(getResponse() < 0){ return -1; } printf("删除成功!\n"); }
    六、项目文件列表、文件功能说明及项目编译步骤6.1 文件列表及文件功能说明
    email:linux下可执行文件,打开即可运行程序
    email.cpp:程序的主入口,对命令行参数的分析以及处理
    tools.h:程序的工具头文件,其中包含connectHost(),连接服务器函数以及base64编解码函数
    sendmail.h:发送邮件的头文件。具体操作是通过send_mail()函数获取要发送邮件的标题和内容,再通过sendmail()函数发送过去
    receivemail.h:进入收件箱后执行的获取收件箱邮件以及删除邮件的头文件

    6.2 项目编译步骤
    在命令行打开程序所在文档
    编译程序
    gcc email.cpp -o email



    执行程序

    七、项目演示步骤
    当输入./email命令或者./email -h命令的时候会在屏幕输出帮助文档


    当输入./email setuser命令的时候会进入设置用户的状态,并且会将输入的用户名密码通过base64加密存放到email.conf中

    现在设定的测试邮箱是zerofang42@163.com,密码为zero633,163的邮箱开通smtp以及imap这些邮箱协议支持的时候会生成一个新的密码bzvxrmuheoqmvjqm。

    当输入./email send 邮箱命令的时候会进入发送邮件的状态

    此时邮件已经发送成功,进入对应的邮箱网页端可以看到:


    当输入./email inbox命令的时候会进入对应用户的收件箱

    已读未读信息显示在第n封邮件后,已读显示(\Seen),未读显示()。随后分行显示日期,发件人,主题,收件人和邮件正文。由于中文采用gb2312编码,所以以base64编码的邮件正文或标题没有找到解码的方法。但是英文字符可以利用base64转utf-8解码,效果如下:


    删除邮件

    删除邮件是在进入收件箱之后的操作,在阅读完邮件后会询问是否删除邮件,若选择是输入y,否则输入n,若输入y则再提示输入要删除的邮件编号,输入对应编号后执行删除操作,若删除成功则提示成功删除。
    2 评论 39 下载 2018-11-12 16:19:04 下载需要4点积分
  • 基于TCP和UDP Socket编程实现的网路聊天室支持文件传输

    1、环境1.1 开发平台Intel i5处理器,4GB内存,500GB机械硬盘,1GBps网卡速率;操作系统为Windows 10,开发平台为Qt creator5.9.2,除Qt的部分动态链接库以外,无其他组件。
    1.2 运行平台服务器运行平台与开发平台相同,由于需要多个用户同时在线,故其中一个用户也运行在开发平台上;其他用户运行在虚拟机上,虚拟机是Vmware workstation上安装的Windows 7虚拟机,内存2GB,磁盘空间50GB。第三方组件为Qt的动态链接库。
    2、系统功能需求基于TCP和UDP协议实现一个即时通讯工具,具体功能要求包括:

    工具包括服务器端和客户端
    具备用户注册、登录、找回密码功能(基于TCP协议)
    两个用户如果同时在线,采用点到点通信方式进行聊天,信息不需要通过服务器中转,服务器也不保存,且消息要在五秒内送达对方(基于TCP协议)
    支持离线消息,且在登录后一分钟内全部收到(基于TCP协议)
    支持点到点可靠文件传输(基于UDP协议)
    存储在服务器端的数据需要进行强加密
    支持不少于两组用户同时在线交流和传输文件
    文件传输具有良好的性能,能够充分利用网路带宽
    人机交互友好,软件易用性强

    3、系统设计3.1 系统架构由于开发环境是qt,而qt中有其独特的信号和槽机制,通过信号的触发来调用槽函数,所以系统的功能函数基本都是槽函数,通过用户的按键等操作来实现调用,所以不像一般的系统都是一个主函数调用若干个子功能函数,本系统中的众多功能函数相对比较分散、独立,各有各的触发开关,执行自己的功能。
    3.2 功能模块由于系统分为服务器和客户端,所以功能模块也需要各自讨论。
    3.2.1 服务器服务器主要功能是接受用户的注册消息、登录消息和离线消息等,所以主要功能模块如下:

    用户连接:处理用户的tcp连接请求
    注册功能:处理用户的注册消息
    找回密码:处理用户的找回密码请求
    获取IP:用于处理聊天的用户发来的获取IP的请求
    离线消息:处理用户发来的离线消息
    获取用户列表:获取所有用户,区分在线和离线用户
    保存&读取数据:保存已注册用户的所有账号信息并加密,读取用户信息并解密

    3.2.2 客户端客户端的功能远远多于服务器,大体上可以分成tcp模块和udp模块,而这两个模块下又有很多子模块。

    注册账号:新用户注册新账号
    找回密码:老用户根据密保问题找回密码
    登录:最基本的功能,是聊天和发文件的基础
    文本聊天:选择好友进行文字聊天
    文件传送:选择好友进行文件传输
    接收聊天信息:接受好友或服务器发来的聊天信息并显示
    接收文件:接受好友发来的文件并保存
    界面控制:根据不同的用户状态,在界面上显示或隐藏不同的控件
    获取好友列表:获取所有好友,并在列表中显示器账号

    3.3 应用层协议3.3.1 文本传输用Qbytearray向qtcpsocket中写入数据,为了供数据接收方识别,需要根据数据的功能加上各自的头部,比如注册信息可加上“reg”头部,普通聊天消息可加“msg”,发送方要根据当前用户所处的状态来加不同的头部,而接收方在取出头部后,需要根据头部的值来判断这是何种信息,接下来该执行何种操作,这是应用层协议的关键,双方需要协商好每一种信息使用何种头部。
    3.3.2 文件传输使用qdatastream向qudpsocket中写入从文件中读出的数据,每个包的数据长度自己定义,由于udp只用于文件传输,所以发送和接收方都不会发生混淆,不需要加消息头部标识,但是还是需要在头部添加数据包的序号。接收方采用累积确认法,接受一个包就发送该包对应序号的ack,当收到的数据包不是已确认序号的下一个时,仍发送最后一个已确认序号的ack信号。发送方采用快速重传法,并且使用tcp reno算法控制拥塞窗口大小。当收到三个冗余的ack时,重新发送冗余ack后的所有分组,窗口长度减半,当发生超时,拥塞窗口减为1,再用慢启动恢复窗口长度。
    4、系统实现4.1 服务器保存&读取数据:这是服务器一切功能的基础。服务器启动时,从内存中导入已注册用户的信息,形成一个用户链表,表中的每个结点分别对应一个用户,其中存放了用户的账号、密码等个人信息,此外还有是否在线的标记,以及与服务器建立连接的tcp套接字。
    在读取文件恢复链表时, 只需要恢复账号密码信息即可,其他在线信息暂且不用管。在保存数据时,也只需要保存账号信息。需要注意的是,对于密码,需要进行加密然后再写入文件,读出文件时使用与之配套的解密算法进行解密。
    从内存读取数据并恢复用户链表的流程图如下图所示,保存数据的过程与之类似,但顺序相反。

    用户连接:当有用户发送连接请求时,服务器要用一个套接字来与之连接。与服务器进行连接的用户有多种情况:注册、找回密码和登录,对于前两种情况,该套接字在操作完成后就失效了,但对于登录的用户,则需要长久保持连接。因此,我们先创建一个套接字建立这一连接,等到确认该用户是登录用户时,就将该套接字挂载到用户链表中的对应节点上,并将链表中用户是否在线的标志变量置为1,并获取登录方的IP地址,也存到用户结点中。若用户是进行注册和找回密码,则需要在一定时间以后销毁该套接字,以免占用内存。
    客户端接受用户各类连接信息的流程图如下图所示。

    注册功能:用户与服务器建立连接,发送注册的账号和密码,服务器到用户链表中查找是否已经存在该用户,假如不存在,则新建一个用户结点,把该用户添加到用户链表中;否则,发送注册失败的消息,告诉用户,该账号已经被注册了。
    注册功能是服务器处理各类连接中的一类情况,其操作流程已经包含在服务器处理连接的流程图中。
    找回密码:用户与服务器建立连接,发送密保问题和答案,并附带新密码。需要注意的是,我们生活中常用的软件附带的找回密码功能,实际上都是让用户设置新密码,而并不是直接把旧的密码告诉用户,因为这样更安全,所以此处也借鉴了这一做法。服务器根据用户账号去用户链表中找到该用户,判断用户的密保问题是否回答正确,若是,则将新密码替换旧密码,并反馈给用户修改成功的信息;否则,告知用户问题回答错误,无法找回密码。
    找回密码功能是服务器处理各类连接中的一类情况,其操作流程已经包含在服务器处理连接的流程图中。
    获取IP:系统要求用户点对点聊天时,消息不经过服务器,那么,用户就必须知道聊天对方的IP地址,才能与之交流,所以用户必须向服务器索要IP。服务器收到获取IP的请求后,根据用户所请求的账号,去用户链表中查找该用户是否在线,若是,则把用户的IP发回请求方,否则,告知请求方,你所请求的用户不在线,假如要跟它聊天,请直接把离线消息发给服务器。
    离线消息:对于不在线的用户,与之聊天时只能把消息发给服务器。发送方要在消息头部注明该消息是发给谁的,服务器收到以后,为该用户建立一个文件,把离线消息全都存起来。当离线的用户上线时,服务器立马检索是否有该用户的离线消息,若有,则打开文件,把消息读出,并发给用户,然后还要记得销毁文件,以免占用内存。
    获取用户列表:为了方便服务器管理用户,在要求服务器端显示所有用户的账号,对于在线的用户,还需要显示IP地址。可以设置一个获取用户的按键,按键触发时,遍历用户的链表,判断该用户是否在线,假如在线,则将其昵称和IP地址显示到在线用户列表,否则,将账号显示到离线用户列表。
    获取IP、发送离线消息和获取用户列表三者都是某用户在线条件下向服务器发送消息的功能,所以三者是同一处理程序的不同分支,其流程图如下图所示。

    4.2 客户端注册账号:获取用户输入的账号信息,包括账号、密码、密保问题、问题答案,将这些信息打包发给服务器,加上“reg”首部表示是注册信息,等待服务器响应,若服务器返回确认信息,则注册成功,可以使用注册的账号登录,否则,注册账号失败,说明该账号已存在。
    注册账号的流程图如下图所示。

    找回密码:用户输入已注册的账号,该账号的密保问题和答案,以及新的密码,将这三项信息加上头部“psd”,打包发给服务器,若服务器返回确认信息,说明找回密码成功,可以使用新密码登录;否则,说明密保问题回答错误,找回密码失败。找回密码的流程与注册账号相似,流程图结构也相似。
    登录:将账号和密码,以及头部“login”发给服务器,等待服务器响应,若返回确认信息,则登录成功,否则登录失败,账号与密码不匹配。登陆流程与注册和找回密码相似,流程图结构也类似。
    获取好友列表:点击获取用户按键,向服务器发送一个请求信息,请求服务器将当前所有已经注册的用户反馈过来,这样,我就可以选择其中的用户进行聊天。收到服务器的反馈以后,建立一个用户链表,每个结点代表一个用户,并将每个人的账号显示在列表中。当需要与某人聊天时,双击即可。获取用户列表的流程图如下图所示。

    文本聊天:在已经获取了好友列表以后,在好友列表中点击一位好友,客户端将向服务器发送获取IP地址的请求,请求服务器把我选中的用户的IP地址进行反馈,以便我与他取得联系。服务器可能有两种响应:返回用户IP,或者返回错误信息。正常返回用户IP时,客户端向该IP地址发送tcp连接请求,连接成功后,在文本框内输入消息,点击发送,就可以使用已经建立的tcp连接把消息发送过去。假如服务器返回了错误信息,说明该用户当前不在线,所以也就不存在IP地址一说,故不能直接和他聊天,而应该以离线消息的形式发给服务器。在用户看来,对方在线或者不在线是没有区别的,消息都成功发送出去了,但是在内部实现时,要根据对方是否在线,决定将消息发向对方或者是发向服务器。
    文本聊天的程序流程图如下图所示。

    接收聊天信息:接收好友或者服务器发来的消息。对于好友发来的消息,在消息框内进行显示,并在界面左上角显示当前联系人,也就是发消息过来的人,这些消息都是包含在对方发来的报文中的,直接提取即可。对于服务器发来的离线消息,处理程序也类似,只是在消息显示时,注明是离线消息,以便让用户知道这不是当前发的消息。
    文件传送:点击获取文件,弹出文件选择框,选择待发送的文件,获取其文件名,将文件名发送给好友,并向其发送udp端口请求,请求对方提供他的udp端口号,以便进行udp文件传输。对方收到文件名以后,创建文件,并反馈端口号,即刻,文件传输开始。一次从文件中读取1400个字节的数据,再加上一个从1开始的序号,用udp发送给对方。虽然是udp文件传输,但是在拥塞控制上,使用了tcp的reno算法,拥塞窗口长度一开始为1,然后在每次收到正确的ack以后长度翻倍。当收到三个冗余ack时,窗口减半;当发生超时,窗口减为1。发送结束后,关闭套接字和文件。文件发送的流程图如下图所示,由于发送过程的处理逻辑过于复杂,全部用流程图展出难度太大,所以对有些过程进行了缩减,只画出了大致流程。

    接收文件:根据好友发送的文件名,在本地创建文件,并将收到的数据写入文件。若收到的序号是递增的,则将数据写入文件,并返回递增的ack序号。若收到的数据包序号不是当前想要的,就发送重复的ack,直到收到正确的数据包。文件接受完毕后,关闭文件。接收文件的逻辑没有发送文件那么复杂,其流程图如下图所示。

    界面控制:在不同的用户操作状态下,界面上要显示不同的组件,可以设立一个状态变量,界面控制函数里有一个状态机,它会根据当前所处的状态进行控件的隐藏和显示。这个不涉及到系统的核心功能,没有太多技术含量,只是为了让界面尽可能友好,调用的主要就是控件的show、hide、settext函数。
    5、系统测试及结果说明测试系统为本机+虚拟机,本机配置为:Intel i5处理器,4GB内存,500GB机械硬盘,1GBps网卡速率,Windows10操作系统; 虚拟机为:Vmware workstation上安装的Windows 7虚拟机,内存2GB,磁盘空间50GB。
    5.1 注册选择注册功能,填写要注册的账号信息。这里注册账号为胖虎,密码为panghu,密保问题是我的真实姓名,答案是胖虎的真实姓名“冈田武”的拼音。点击确认,注册成功。

    5.2 登录输入账号密码。

    登录成功,进入用户界面,右侧显示本机ip地址和分配的tcp以及udp端口号。点击获取用户列表,获取到当前已注册的所有用户。

    5.3 点对点聊天现在想要与小明聊天,故双击小明,右侧显示“connected”表示成功与小明建立连接(此时小明也成功登录了)。向小明发送消息,与他问好,小明成功收到了消息。

    小明收到消息以后,应该回复消息,两人可以按照任意顺序,随意进行聊天。

    5.4 多组用户聊天当胖虎与小明聊天时,其他在线用户可能向小明和胖虎发送消息,此时他们要能够正常接收其他用户发来的消息。现假定小红也在线,她向正在聊天的小明和胖虎分别发去了消息。

    此时小明和胖虎都收到了小红发的消息。

    5.5 离线消息当前共有三人在线,而小强不在线,小明和胖虎想叫小强出去打篮球,所以他们都向小强发了消息。

    此时,两人发送的消息在服务器端存了起来,等到小强上线时才会发送给他。几分钟后,小强上线了,他立马就收到了来自服务器的消息。

    5.6 密码找回假设小刚想要登录,但是发现自己忘记了密码,他应该选择找回密码功能,填写账号和密保问题,并设置新密码。

    点击确认,找回密码成功。再用新密码进行登录,登录成功。


    5.7 文件传输小刚向胖虎发送视频文件狐狸爸爸fox.mkv,用时23秒,如下图所示。在文件夹中可以看到保存成功的文件。

    文件传输过程中拥塞窗口长度变化的部分图像。

    图中横坐标为时间,单位是毫秒,纵坐标是拥塞窗口的长度。由于传输过程达到了上千毫秒,所以只能选取部分过程进行分析,只要该部分中包含有传输中可能出现的各种情况,都是可取的。这里选取了开始的半秒钟,可见在半秒钟时间内,窗口长度就发生了多次剧变。一开始窗口长度以指数型方式增长,超过阈值,然后线性增长;当网络轻度拥塞,产生三个冗余ack,拥塞窗口减半再加三,以线性增长方式恢复;当网络重度拥塞,窗口长度减为1,然后再以指数型方式恢复。这与我们的网络传输协议的设计是相符的,也就相当于tcp拥塞控制协议中的reno算法,只不过是用在了udp传输中而已。
    2 评论 40 下载 2019-03-22 09:08:19 下载需要15点积分
显示 30 到 45 ,共 15 条
eject