当前位置: 首页 > news >正文

mysql电子词典_电子词典阶段性小项目

电子词典阶段性项目

项目需求介绍

登录注册

用户可以登录和注册

用户名 密码

用户名不能重复

用户信息可以长期保存

网络连接

程序分为服务端,客户端

服务端负责数据处理

启动服务端后应满足多个客户端同时操作

图形二级界面

通过基本的图形界面 print 提示客户端输入

启动后进入一级界面

一级界面功能

登录 注册 退出

登陆: 成功进入二级界面

注册: 成功返回一级界面,或者直接使用注册用户进入二级界面

退出: 退出软件

二级界面功能

查单词 历史记录 注销

查单词: 循环输入单词.得到单词解释,输入特殊符号退出查询状态

直接使用单词本查询.(文本操作)

现将单词本存入数据库查询.通过数据库查询

历史记录:  查询当前用户的查词记录 要求记录包含 name word time可以查看所有记录,或者只显示前10条

注销: 返回上级页面

单词库

特点

文本形式

每个单词占一行

单词按照从小到大顺序排列

单词和解释之间一定有空格

形式如下的一个 e_dict.txt 文件

a indef art one

abacus n.frame with beads that slide along parallel rods, usedfor teaching numbers to children, and (in some countries) forcounting

abandon v. go awayfrom (a person or thing or place) not intending to return; forsake; desert

abandonment n. abandoning

abase v.~ oneself/sb lower oneself/sb in dignity; degrade oneself/sb ;

abash to destroy the self-possession or self-confidence of:disconcert

abashed adj.~embarrassed; ashamed

abate v. makeorbecome less

abattoir n.=slaughterhouse (slaughter)

abbess n. woman whois head of a convent ornunnery

abbey n. buildingin which monksor nuns live as a community under an abbot orabbess

abbot n. man whois head of a monastery orabbey

abbreviate v.~ sth shorten (a word, phrase, etc), esp by omitting letters

项目目的

练以下的技能

socket, 协程, MySQL, pymysql,

项目结果展示

服务端启动

服务端执行启动后等待客户端连接

5a8e83233ff42ffeaab2f24ff0e70ad1.png

客户端启动

客户端连接后显示一级界面

9db589818da5a6b16d162f0c695e7f2b.png

注册功能展示

用户名输入,密码,重复密码输入,都无法展示.注册成功后返回一级界面

9dc153fb68293be0681cba514a2461e7.png

登录功能展示

登录输入验证成功后进入二级界面

258d8eb80131f63eb4da4c5a916378d1.png

查询功能展示

查询单词后返回查询结果,输入## 退出查看 返回二级目录

3d2e3cfd8fe5027140e32e648c557d07.png

06ee42a705028e33bbc57d0726120245.png

历史记录功能查看

返回当前用户最近10条历史查询单词,然后返回二级菜单

5bfedcf74f5cb2720f856fb68f9fd77f.png

注销功能

注销当前用户返回一级菜单

e08aff430b99cd3790c0c7432770a5ca.png

退出程序展示

38c69a5537f6034df76a32708dde7455.png

项目需求分析整理

大白话思路

首先想好要用哪些技术.

数据库这边选择 mysql. 然后使用 pymysql 连接数据库这定死了.

然后就是数据库表结构设计

然后就是网络部分选择 tcp_socket 分为服务端客户端两套来做.各自处理各自的逻辑也没得说

然后关于高并发问题用 多进/线/协程来处理,这里选择最简单的协程最方便

然后就是具体的逻辑代码问题了.

逻辑方面:

登录验证这块就和数据库的用户表打交道了.

查词这块是和字典表来查询.查询这块的选择方式为了知识点覆盖我们不通过数据库来查询.基于文件来查询

历史记录这块重点就是sql语句的问题了,要排序要限制取数据条数,这块为了技术覆盖面我们要基于数据库来

那整体差不多就是这样的分析了.首先设计表结构把

mysql 表结构设计

首先这里我们可以使用外键,这样设计是可以的.但是鉴于我们仅仅是练习还是特么这么简单的结构设计就懒得这么折腾了

table user-----------------------id| username | pwd |

-----------------------table user_hist--------------------------id| user_id | hist_id |

--------------------------table user_history-----------------------------id| use_id | dict_id |time-----------------------------table dict_hist---------------------------id| dict_id | hist_id |

---------------------------table e_dict.txt-----------------id| word |val-----------------

所以我们使用更简单的结构设计,直接三张表解决吧

a962bcb52ce4d4cd589cdca5d68f6ef6.png

其次还需要做一件事就是要把 e_dict.txt 文件中的 数据添加到数据库中

这里存在着优化点.

文件中有 2w条数据,本以为不算特别大居然也用了接近5分钟.这个效率不敢恭维

因此可以使用 将数据整合在列表中,然后用 excutemany 来实现批量的添加更加高效.

"""一次性的执行脚本文件

目的

实现将文件中的 数据插入在数据库中"""

importpymysql

f= open("e_dict.txt")

db= pymysql.connect("127.0.0.1", "root", "123456", "dict")

cursor=db.cursor()for line in f: #循环读取文件内容

tmp = line.split(" ")

word=tmp[0]

mean= " ".join(tmp[1:]).strip()

sql= 'insert into words (word, mean) values ("%s","%s")' %(word, mean)try:

cursor.execute(sql)

db.commit()except:

db.rollback()

f.close()

执行完毕后,数据库的相关操作就全部完成了

业务逻辑

服务端和客户端彼此的职能是完全不同的,但是需要数据的交互

业务逻辑也全部都是彼此分割

总框架

服务端

前面使用了 sys.argv, 此方法用与获取用户命令行操作的参数.这样可以实现用户不通过修改源码的方式实现自定义属性的传入

此方法在 django ,scarpy 中都有体现

服务端这需要创建tcp_socket 以及并发的相关操作.以及一些其他的善后处理.当然还有与 DB 的交互也要完成

并且定义了 相关的业务逻辑操作码.以及 基于协程的 并发模式处理

defmain():#连接数据库

db = pymysql.connect("127.0.0.1", "root", "123456", "dict")#创建套接字

s =socket()#s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 保证端口复用的

s.bind(ADDR)

s.listen(5)#僵尸进程的处理

#signal.signal(signal.SIGCHLD, signal.SIG_IGN)

#循环等待连接

whileTrue:try:

c, addr=s.accept()print("Connect from", addr)exceptKeyboardInterrupt:

s.close()

sys.exit("服务器退出")continue

#创建子进程

gevent.spawn(do_child, c, db)#如果在 linux 下直接这样也可以

#pid = os.fork()

#if pid == 0:

#s.close()

#do_child(c, db)

#sys.exit()

#else:

#c.close()

defdo_child(c, db):whileTrue:

data= c.recv(1024).decode()print(c.getpeername(), ":", data)if not data or data[0] == "E":

c.close()

sys.exit()elif data[0] == "R":

do_register(c, db, data)elif data[0] == "L":

do_login(c, db, data)elif data[0] == "Q":

do_query(c, db, data)elif data[0] == "H":

do_history(c, db, data)return

客户端

客户端要实现一个图形界面的展示以及用户的操作指引.顺便需要对用户的操作指定 操作码

比如在框架中可以提现 退出程序 的操作码为 " E "

同样和服务端一样, 客户端也需要在命令行进行定制

defmain():if len(sys.argv) < 3:print("""argv is error""")returnHOST= sys.argv[1]

PORT= int(sys.argv[2])

s=socket()try:

s.connect((HOST, PORT))exceptException as e:print(e)return

whileTrue:print("""=====================Welcome======================

-- 1. 注册 2.登录 3 退出 --

==================================================""")try:

cmd= int(input("输入选项:"))exceptException as e:print("命令错误")continue

if cmd not in [1, 2, 3]:print("请输入正确选项")continue

elif cmd == 1:

do_register(s)elif cmd == 2:

do_login(s)elif cmd == 3:

s.send(b'E')

sys.exit("谢谢使用")

登录业务逻辑

服务端

服务端对业务逻辑 为登录的时候定义 操作码为 " L ", 然后定义通过 do_login函数具体操作

do_login 函数具体取到 客户端的发送数据然后格式化处理后在数据库查询用户验证

判断基于状态返回

defdo_login(c, db, data):

l= data.split(" ")

name= l[1]

passwd= l[2]

sql= "select * from user where name = '%s' and pwd = '%s'" %(name, passwd)#查找用户

cursor =db.cursor()

cursor.execute(sql)

r=cursor.fetchone()if r ==None:

c.send(b'FAIL')else:

c.send(b"OK")return

客户端

客户端的操作主要是获取用户数据然后发送 与服务端约定的格式和操作码.然后等待服务端的回传消息

在基于回传消息进行对用户的信息展示.

如果 登录成功会进一步的 通过 login 函数进入二级界面.

defdo_login(s):

name= input("User:")

passwd=getpass.getpass()

msg= "L %s %s" %(name, passwd)

s.send(msg.encode())

data= s.recv(128).decode()print(data)if data == "OK":print("登陆成功")

login(s, name)else:print("登录失败")return

注册业务逻辑

服务端

依旧和登录类似,数据结构处理后对数据库的操作

中间有一步对用户名存在的判断

defdo_register(c, db, data):

l= data.split(" ")

name= l[1]

passwd= l[2]

cursor=db.cursor()

sql= "select * from user where name = '%s'" %name

cursor.execute(sql)

r=cursor.fetchone()if r !=None:

c.send(b"EXISTS")return

#插入用户

sql = "insert into user (name, pwd) value ('%s','%s')" %(name, passwd)try:

cursor.execute(sql)

db.commit()

c.send(b"OK")except:

db.rollback()

c.send(b"FAIL")return

客户端

客户端依旧是获取用户数据以及状态消息回传. 密码部分的显示用了 getpass 来隐藏输入显示

defdo_register(s):whileTrue:

name= input("User:")

passwd=getpass.getpass()

passwd1= getpass.getpass("Again:")if (" " in name) or (" " inpasswd):print("用户名密码不能有空格")continue

if passwd !=passwd1:print("两次密码不一致")continuemsg= "R %s %s" %(name, passwd)#发送请求

s.send(msg.encode())#等待回应

data = s.recv(128).decode()if data == "OK":print("注册成功")#login(s,name) # 注册成功进入二级界面

return

elif data == "EXISYS":print("用户已存在")else:print("注册失败")return

查询业务逻辑

服务端

服务端的查询需要拿到单词的解释以及分区是否可以查询到,

未查询到的时候要返回具体的错误消息(因为客户端不好判断,这里直接返回具体消息更方便)

因为这里使用过的是基于文件的查询,在判断对比 查询字段的时候 基于 字符串的排序,因为文件内的字段都是排序过的.直接这样对比可以更加快速

当然也需要考虑 "zzzz" 这样的单词到死也查询不到的可能性,因此需要注意判断的嵌套,最外层要又一次表示

defdo_query(c, db, data):

l= data.split(" ")

name= l[1]

word= l[2]#插入历史记录

cursor =db.cursor()

tm=time.ctime()

sql= "insert into hist (name, word, time) value ('%s','%s','%s')" %(name, word, tm)try:

cursor.execute(sql)

db.commit()except:

db.rollback()

f=open(DICT_TEXT)for line inf:

tmp= line.split(" ")[0] #获取单词

if tmp >word:break

elif tmp ==word:

c.send(line.encode())

f.close()returnc.send("没有该单词".encode())

f.close()

客户端

客户端的查询逻辑,获取用户输入.基于结束码结束

这里需要注意的是不确定是否拿到的是单词解释还是因为找不到单词而返回的结果.

但是不论是什么这里不好区分我们由后端来区分,这里直接全部展示即可

defdo_query(s, name):whileTrue:

word= input("请输出查询单词:")if word == "##":breakmsg= "Q %s %s" %(name, word)

s.send(msg.encode())#单词解释 / 找不到

data = s.recv(2048).decode()print(data)

访问历史业务逻辑

服务端

主要还是 mysql 的使用 sql 语句,要用到排序和限制.

需要注意的是数据库创建的时候图方便没使用 time 相关的字段而是用了字符串

因此这里排序不能按照时间,要按照自增的 id 倒叙来保证最近时间的数据可靠性

defdo_history(c, db, data):

name= data.split(" ")[1]

cursor=db.cursor()

sql= "select * from hist where name='%s' order by id desc limit 10" %name

cursor.execute(sql)

r=cursor.fetchall()if notr:

c.send(b"FAIL")else:

c.send(b"OK")

time.sleep(0.1)for i inr:

msg= "%s %s %s" % (i[1], i[2], i[3])

c.send(msg.encode())

time.sleep(0.1)

c.send(b"##")

客户端

客户端这块就没什么好说的了,唯一需要注意的是因为你不知道要查询的字段到底有多少数据,

因此直接选择用 死循环不停接收即可,也因为死循环,需要定义一个结束标识, 即 "##"

defdo_history(s,name):

msg= "H %s" %name

s.send(msg.encode())

data= s.recv(128).decode()if data == "OK":whileTrue:

data= s.recv(1024).decode()if data == "##":break

print(data)else:print("没有历史记录")

总结

整合了网络编程,并发编程.mysql,以及前期阶段的集成项目.非常适合练手.

项目虽然完成但是中间有大量的优化空间,

尤其是大量的冗余代码.比如 DB 操作可以整合为一个 工具类这样 服务端中的数据库操作会更加舒适

其次全程使用 函数式编程,整合成 面向对象方式更加清晰.

还有为了避免沾包问题,这里用了很蠢的 sleep 来处理也是优化空间

窝草.通篇忘了使用 pymysql 的防注入了.全程铁头自己拼字符串还行...醉了....回过头来看问题真的多

以上差不多就是这样吧,,,算了算了...心放宽...宽...

全部代码

客户端

from socket import *

importsysimportgetpass#创建网络连接

defmain():if len(sys.argv) < 3:print("""argv is error""")returnHOST= sys.argv[1]

PORT= int(sys.argv[2])

s=socket()try:

s.connect((HOST, PORT))exceptException as e:print(e)return

whileTrue:print("""=====================Welcome======================

-- 1. 注册 2.登录 3 退出 --

==================================================""")try:

cmd= int(input("输入选项:"))exceptException as e:print("命令错误")continue

if cmd not in [1, 2, 3]:print("请输入正确选项")continue

elif cmd == 1:

do_register(s)elif cmd == 2:

do_login(s)elif cmd == 3:

s.send(b'E')

sys.exit("谢谢使用")defdo_register(s):whileTrue:

name= input("User:")

passwd=getpass.getpass()

passwd1= getpass.getpass("Again:")if (" " in name) or (" " inpasswd):print("用户名密码不能有空格")continue

if passwd !=passwd1:print("两次密码不一致")continuemsg= "R %s %s" %(name, passwd)#发送请求

s.send(msg.encode())#等待回应

data = s.recv(128).decode()if data == "OK":print("注册成功")#login(s,name) # 注册成功进入二级界面

return

elif data == "EXISYS":print("用户已存在")else:print("注册失败")return

defdo_login(s):

name= input("User:")

passwd=getpass.getpass()

msg= "L %s %s" %(name, passwd)

s.send(msg.encode())

data= s.recv(128).decode()print(data)if data == "OK":print("登陆成功")

login(s, name)else:print("登录失败")return

deflogin(s, name):whileTrue:print("""=====================Welcome======================

-- 1. 查词 2.历史记录 3 注销 --

==================================================""")try:

cmd= int(input("输入选项:"))exceptException as e:print("命令错误")continue

if cmd not in [1, 2, 3]:print("请输入正确选项")continue

elif cmd == 1:

do_query(s, name)elif cmd == 2:

do_history(s,name)elif cmd == 3:return

defdo_query(s, name):whileTrue:

word= input("请输出查询单词:")if word == "##":breakmsg= "Q %s %s" %(name, word)

s.send(msg.encode())#单词解释 / 找不到

data = s.recv(2048).decode()print(data)defdo_history(s,name):

msg= "H %s" %name

s.send(msg.encode())

data= s.recv(128).decode()if data == "OK":whileTrue:

data= s.recv(1024).decode()if data == "##":break

print(data)else:print("没有历史记录")if __name__ == '__main__':

main()

服务端

from gevent importmonkey

monkey.patch_all()from socket import *

importpymysqlimportsysimporttimeimportgeventimportos#制定输入格式,提供用户输入指引

if len(sys.argv) < 3:print("""Start as:

python3 dict_server.py 0.0.0.0 8000""")

sys.exit()#定义全局变量

HOST = sys.argv[1] #sys.argv 获取命令行参数

PORT = int(sys.argv[2])

ADDR=(HOST, PORT)

DICT_TEXT= "e_dict.txt"

#搭建网络连接

defmain():#l连接数据库

db = pymysql.connect("127.0.0.1", "root", "123456", "dict")#创建套接字

s =socket()#s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 保证端口复用的

s.bind(ADDR)

s.listen(5)#僵尸进程的处理

#signal.signal(signal.SIGCHLD, signal.SIG_IGN)

#循环等待连接

whileTrue:try:

c, addr=s.accept()print("Connect from", addr)exceptKeyboardInterrupt:

s.close()

sys.exit("服务器退出")continue

#创建子进程

gevent.spawn(do_child, c, db)#如果在 linux 下直接这样也可以

#pid = os.fork()

#if pid == 0:

#s.close()

#do_child(c, db)

#sys.exit()

#else:

#c.close()

#处理客户端请求

defdo_child(c, db):whileTrue:

data= c.recv(1024).decode()print(c.getpeername(), ":", data)if not data or data[0] == "E":

c.close()

sys.exit()elif data[0] == "R":

do_register(c, db, data)elif data[0] == "L":

do_login(c, db, data)elif data[0] == "Q":

do_query(c, db, data)elif data[0] == "H":

do_history(c, db, data)return

defdo_register(c, db, data):

l= data.split(" ")

name= l[1]

passwd= l[2]

cursor=db.cursor()

sql= "select * from user where name = '%s'" %name

cursor.execute(sql)

r=cursor.fetchone()if r !=None:

c.send(b"EXISTS")return

#插入用户

sql = "insert into user (name, pwd) value ('%s','%s')" %(name, passwd)try:

cursor.execute(sql)

db.commit()

c.send(b"OK")except:

db.rollback()

c.send(b"FAIL")return

defdo_login(c, db, data):

l= data.split(" ")

name= l[1]

passwd= l[2]

sql= "select * from user where name = '%s' and pwd = '%s'" %(name, passwd)#查找用户

cursor =db.cursor()

cursor.execute(sql)

r=cursor.fetchone()if r ==None:

c.send(b'FAIL')else:

c.send(b"OK")return

defdo_query(c, db, data):

l= data.split(" ")

name= l[1]

word= l[2]#插入历史记录

cursor =db.cursor()

tm=time.ctime()

sql= "insert into hist (name, word, time) value ('%s','%s','%s')" %(name, word, tm)try:

cursor.execute(sql)

db.commit()except:

db.rollback()

f=open(DICT_TEXT)for line inf:

tmp= line.split(" ")[0] #获取单词

if tmp >word:break

elif tmp ==word:

c.send(line.encode())

f.close()returnc.send("没有该单词".encode())

f.close()defdo_history(c, db, data):

name= data.split(" ")[1]

cursor=db.cursor()

sql= "select * from hist where name='%s' order by id desc limit 10" %name

cursor.execute(sql)

r=cursor.fetchall()if notr:

c.send(b"FAIL")else:

c.send(b"OK")

time.sleep(0.1)for i inr:

msg= "%s %s %s" % (i[1], i[2], i[3])

c.send(msg.encode())

time.sleep(0.1)

c.send(b"##")if __name__ == '__main__':

main()

相关文章:

  • 找不到类com.mysql_java – 在webservice 中找不到类异常Class.forName(“com.mysql.jdbc.Driver”)...
  • mysql服务器管理员_配置MySQL服务器时,需要设置一个管理员账号,其名称是( )。...
  • linux基础
  • mysql zookeeper 切换_利用ZOOKEEPER技术 MYSQL数据库容灾切换
  • conda入门到精通
  • python class tynu()_调用百度地图API与语音API实现简易地图语音导航
  • 小d课堂mysql_mysql之explain详解(分析索引的最佳使用)
  • LINUX 批量删除文件的几种方法
  • mysql 模糊 不包括_Oracle 模糊查询中不包括某字符串的实现方式
  • star 序列比对2020-12-25
  • java线程模式_Java多线程基础(十一)——Future模式
  • RNA-seq实战--2小时
  • java什么是数组_JAVA中数组是什么?
  • ChIP-seq数据处理流程(附赠长达5小时的视频指导)
  • java录制视频_java 屏幕录制
  • [译]CSS 居中(Center)方法大合集
  • 【翻译】Mashape是如何管理15000个API和微服务的(三)
  • 2019年如何成为全栈工程师?
  • CSS相对定位
  • JavaScript DOM 10 - 滚动
  • JavaScript HTML DOM
  • 规范化安全开发 KOA 手脚架
  • 聚类分析——Kmeans
  • 开源SQL-on-Hadoop系统一览
  • 腾讯大梁:DevOps最后一棒,有效构建海量运营的持续反馈能力
  • 微信支付JSAPI,实测!终极方案
  • 原生 js 实现移动端 Touch 滑动反弹
  • LIGO、Virgo第三轮探测告捷,同时探测到一对黑洞合并产生的引力波事件 ...
  • 长三角G60科创走廊智能驾驶产业联盟揭牌成立,近80家企业助力智能驾驶行业发展 ...
  • 国内开源镜像站点
  • #100天计划# 2013年9月29日
  • #我与Java虚拟机的故事#连载17:我的Java技术水平有了一个本质的提升
  • (1)(1.9) MSP (version 4.2)
  • (14)学习笔记:动手深度学习(Pytorch神经网络基础)
  • (2020)Java后端开发----(面试题和笔试题)
  • (Python第六天)文件处理
  • (论文阅读26/100)Weakly-supervised learning with convolutional neural networks
  • .bat批处理(一):@echo off
  • .htaccess 强制https 单独排除某个目录
  • .NET 5种线程安全集合
  • .NET Core使用NPOI导出复杂,美观的Excel详解
  • .NET 解决重复提交问题
  • .NET/C# 的字符串暂存池
  • .NET中GET与SET的用法
  • @DataRedisTest测试redis从未如此丝滑
  • @media screen 针对不同移动设备
  • @RequestMapping-占位符映射
  • [ CTF ]【天格】战队WriteUp- 2022年第三届“网鼎杯”网络安全大赛(青龙组)
  • [20171102]视图v$session中process字段含义
  • [AutoSar]BSW_OS 02 Autosar OS_STACK
  • [BZOJ2281][SDOI2011]黑白棋(K-Nim博弈)
  • [c]统计数字
  • [C++]AVL树怎么转
  • [linux]linux命令学习-netstat
  • [MAT]使用MAT比較多个heap dump文件