• python模拟SQL语句操作文件


     1.需求

    在文本界面输入SQL语句,查询相应的数据,例如输入下面的语句

    print('''
            支持大小写的SQL语句查询,大写或者小写都可以
            1. select * from db1.emp
            2. select * from db1.emp limit 4
            3. select * from db1.emp where id > 24
            4. select * from db1.emp where name like 李
            5. select * from db1.emp where id > 10 and id <14 or name like 李
            6. SELECT * from db1.emp where not id > 24
            7. INSERT into db1.emp values 张国辉,30,18676575678,运维,2007-8-1
            8. delete from db1.emp where id > 25
            9. update db1.emp set name='sb' where id=24
            10.update db1.emp set name='alex' where name like sb
            ''')
    #db1.emp是一个数据库,名字随便取,只要能找到这个值就行

     开始分析整个需求

      1.根据输入的语句实现数据库中的增删改查,四个操作

        ①.语句解析 select, insert ,update,delete

        ②.四种语句解析做的工作类似,抽取一个函数专门解析

    def handle_sql_parse(sql_l,sql_dic){}
    

              解析完成之后返回解析的语句,在分发给对应的函数 

        ③.拿到解析的SQL语句,调起相对应的函数(insert,delete,update,select),查询语句

        ④.返回查询到的结果,输出打印。

    2.具体业务实现

        1.入口函数

    if __name__ == "__main__":
        while True:
            print('''
            支持大小写的SQL语句查询,大写或者小写都可以
            1. select * from db1.emp
            2. select * from db1.emp limit 4
            3. select * from db1.emp where id > 24
            4. select * from db1.emp where name like 李
            5. select * from db1.emp where id > 10 and id <14 or name like 李
            6. select * from db1.emp where not id > 24
            7. insert into db1.emp values 张国辉,30,18676575678,运维,2007-8-1
            8. delete from db1.emp where id > 25
            9. update db1.emp set name='sb' where id=24
            10.update db1.emp set name='alex' where name like sb
            ''')
            sql = input('sql>>:').strip()
            if sql == 'exit' or sql == 'q':
                break
            if len(sql) == 0:continue
    
            #开始解析sql语句
            sql_dic = sql_parse(sql)
            print(sql_dic)
            # 拿到处理好的sql语句开始查询
            result = sql_action(sql_dic)
            print('*'*30 + '查询结果' + '*'*30)
            for value in result[-1]:
                print(value)
            print('*'*30 + '查询完毕' + '*'*30)

          2.根据select,insert,update,delete,四种语句进行分发,分别处理语句

    def sql_parse(sql):
        '''
        输入sql语句,根据关键字拿到处理的函数,传入sql语句解析
        :param sql:
        :return:
        '''
    
        func_dict = {
            'insert':insert_sql_parse,
            'delete':delete_sql_parse,
            'update':update_sql_parse,
            'select':selete_sql_parse
        }
        #将sql语句以空格为界转化为一个列表
        sql_l = sql.split(' ')
        #获取列表中的第一个值即为字典对应的key,命令可能为大写,全部转化为小写去执行
        sql_order = sql_l[0].lower()
        sql_result = ''
        if sql_order in func_dict:
            sql_result = func_dict[sql_order](sql_l)
        return sql_result

         3.具体的select,insert,update,delete四种方式对应的函数

    def insert_sql_parse(sql_l):
        '''
        insert语句,定义语法规则字典传入给语句解析函数
        :param sql_l: 
        :return: 
        '''
        # insert_action 定义的处理insert SQL语句的函数,
        sql_dic = {
            'func': insert_action,
            'insert': [],
            'into': [],
            'values': [],
        }
        return handle_sql_parse(sql_l, sql_dic)
    
    def delete_sql_parse(sql_l):
        '''
        delete语句,定义语法规则字典传入给语句解析函数
        :param sql_l: 
        :return: 
        '''
        sql_dic = {
            'func': delete_action,
            'delete': [],
            'from': [],
            'where': [],
        }
        return handle_sql_parse(sql_l, sql_dic)
    
    def update_sql_parse(sql_l):
        '''
        update语句,定义语法规则字典传入给语句解析函数
        :param sql_l: 
        :return: 
        '''
        sql_dic = {
            'func': update_action,
            'update': [],
            'set': [],
            'where': [],
        }
        return handle_sql_parse(sql_l, sql_dic)
    
    def select_sql_parse(sql_l):
        '''
        select语句,定义语法规则字典传入给语句解析函数
        :param sql_l: 
        :return: 
        '''
        sql_dic = {
            'func':select_action,
            'select': [],
            'from': [],
            'where': [],
            'limit': [],
        }
        return handle_sql_parse(sql_l,sql_dic)

         4.四种语句有共同性,用一个handle函数统一处理返回处理后的sql语句,而且考虑到where后面的运算符处理,我们把逻辑处理的部分在拿出来封装一个函数去处理

    def handle_sql_parse(sql_l,sql_dic):
        '''
        执行sql解析操作,返回sql_dic
        :param sql:
        :param sql_dic:
        :return:
        参考字典
        sql_dic = {
            'func':select_action,
            'select': [],
            'from': [],
            'where': [],
            'limit': [],
        }
        '''
        tag = False
        for item in sql_l:
            #对应的key都转换为小写
            item = item.lower()
            #如果列表中的值和key对应,并且标志位也是true ,就把标志位置反
            if tag and item in sql_dic:
                tag = False
            # 如果列表中的值和key对应,并且标志位也是false ,就把标志位置反,把对用的item赋值给key
            if not tag and item in sql_dic:
                tag = True
                key = item
                continue
            if tag:
                # 标识为true 就把对应的item放到上次循环中对应的key中
                # 根据上一步的item 在字典中取出对应的列表添加元素
                # 以select为例,输入select * from db1.emp where id > 24
                # 返回{'func': <function select_action at 0x10343f598>, 'select': ['*'], 'from': ['db1.emp'], 'where': ['id', '>', '24'], 'limit': []}
                sql_dic[key].append(item)
       
        if sql_dic.get('where'):
                # 单独处理where,返回之后的字典,就是把where中的比较字符每一个条件处理成了一个数组
                #{'func': < function select_action at 0x102ecf598 >, 'select': ['*'], 'from': ['db1.emp'], 'where': [['id', '>', '24']], 'limit': []}
                sql_dic['where'] = sql_where_parse(sql_dic.get('where'))
        return sql_dic

       5.单独对where语句和其中的逻辑运算语句的处理

    def sql_where_parse(sql_where_l):
        '''
        处理where语句 <class 'list'>: ['id', '>', '10', 'and', 'id', '<14', 'or', 'name', 'like', '李']
        :param sql_where_l:
        :return:
        '''
        result = []
        key = ['and','or','not']
        char = '' #拼接字符显示
        for value in sql_where_l:
            if len(value) == 0:continue
            # 如果是逻辑运算
            if value in key:
                if len(char) != 0:
                    char = logic_parse(char)
                    result.append(char)
                    result.append(value)
                    char = '' #每次都清空
            else:
                # 先处理 and or not 这个三个特殊的逻辑运算符,例如上面的例子中,先把id>10作为一个整体放入char中,遍历到and的时候再对char处理
                # 调用logic_parse()函数进行处理,将id>10处理为一个列表['id','>','10'],将列表添加到外围的列表中,同时添加逻辑运算符,清空char
                # 再次运行
                char+=value
        else:
            # 处理最后一个char字符,变为列表
            char = logic_parse(char)
            result.append(char)
        return result

        6.逻辑运算符的解析

    def logic_parse(logic_str):
        '''
        对where中的逻辑运算做处理,返回一个字典,将一个小的条件过滤
        例如 age>10  转换成 ['age','>','10']
        :param login_str:
        :return:
        '''
        key = ['>','<','=']
        result = []
        char = '' #拼接运算符
        flag = False #添加的标识位
        option = '' #记录是否是运算符
        for value in logic_str:
            # age>10,类似于这样的字符串,逐个处理,如果不是运算符,就拼接字符,直到是运算符为止,
            # 将字符添加到数组中,再次判断是否是运算符,拼接运算符,如果不是运算符 就将运算符也添加到数组中,依次循环
            if value in key:
                flag = True
                if len(char) != 0:
                    result.append(char)
                    char = ''
                option+= value
    
            if not flag:
                char+=value
    
            if flag and value not in key:
                flag = False
                result.append(option)
                option = ''
                char+=value
        else:
            result.append(char)
    
        # 添加对like的解析
        if len(result) == 1:
            # 以like为分割线,转化为数组,
            result = result[0].split('like')
            # 在中间位置添加like
            result.insert(1,'like')
    
        return result

       7.在入口函数中拿到解析后的sql语句,开始执行对应的select,update,delete,insert语句,对这个语句做一个分发的操作

    def sql_action(sql_dic):
        '''
        字典中提取命令 分发给对应的具体指令去执行
        :param sql_dic:
        :return:
        根据传入的字典,调用对用的方法,
        sql_dic = {
            'func':select_action,
            'select': [],
            'from': [],
            'where': [],
            'limit': [],
        }
        '''
        return sql_dic.get('func')(sql_dic)

       8.编写对应的insert,select,update,insert等语句开始执行

    def select_action(sql_dic):
        '''
        执行select 语句
        :param sql_dic:
        :return:
        '''
        # 解析路径
        if '.' in sql_dic['from'][0]:
            file_path = sql_dic['from'][0].replace('.','/')
        else:
            file_path = sql_dic['from'][0]
    
        # print(path,table)
    
        filehandle = open(file_path,'r',encoding='utf-8')
        #分别去解析where limit 还要对比显示文字
        where_resutl = where_action(filehandle,sql_dic['where'])
        filehandle.close()
        # print(where_resutl)
        #添加limit操作
        limit_result = limit_action(where_resutl,sql_dic['limit'])
        #根据搜索添加结果
        search_result = search_action(limit_result,sql_dic['select'])
        return search_result

       9.根据解析后的sql语句 还需要对相应的where字符做处理,添加对应的where和limit,最后加上select的处理

    def where_action(filehandler,whele_sql):
        '''
        解析where语句,显示where语句中的执行条件
        :param filehandler:  文件句柄
        :param whele_sql:
        :return:
        '''
        result = []
        titles = 'id,name,age,phone,dept,enroll_data'.split(',')
        if len(whele_sql) != 0:
            for line in filehandler:
                # zip 转化为一个元祖,如果两个队列长度不一致,以短的返回一个元祖
                #tem_dict={'id': '1', 'name': '姬建明', 'age': '25', 'phone': '15201541043', 'dept': '运维', 'enroll_data': '2013-11-01
    '}
                tem_dic = dict(zip(titles,line.split(',')))
                # 处理逻辑运算符中的数据
                logic_res = logic_action(tem_dic,whele_sql)
                # 返回为true的时候,添加数据到列表中
                if logic_res:
                    result.append(line.split(','))
        else:
            return filehandler.readlines()
    
        return result
    
    def logic_action(dic,where_sql):
        '''
        根据字典和sql语句进行匹配,匹配成功返回true  匹配失败返回false
        :param dic:
        :param where_sql:
        :return:
        '''
        # [['id', '>', '10'], 'and', ['id', '<', '14'], 'or', ['name', 'like', '李']]
        result = []
        for item in where_sql:
            # 过滤掉and or not 等逻辑运算符
            if type(item) is list:
                # print(item,dic)
                prop,option,value = item
                # 如果option为= 在后面在拼接一个=
                if option == '=':
                    option+='='
                # 判断是否是数字
                if dic[prop].isdigit():
                    dic_v = int(dic[prop])
                    value = int(value)
                else:
                    dic_v = '%s' %dic[prop]
    
                if option != 'like':
                    # 将字符串str当成有效的表达式来求值并返回计算结果
                    flag = str(eval("%s%s%s" %(dic_v,option,value)))
                else:
                    if value in dic_v:
                        flag = 'True'
                    else:
                        flag = 'False'
            # 拼接每一个匹配的值,
            result.append(flag)
            # 添加and进行连接
            result.append('and')
        #删除最后一个and
        result.pop()
        # 计算数组中的表达式的值
        res = eval(' '.join(result))
    
        # 将result中的bool值进行处理,并计算所有bool值的最后结果
        return res

       10.添加limit处理

    def limit_action(where_res,limit_l):
        '''
        根据用户输入的limit 返回limit的个数的数组
        :param where_res:
        :param limit_l:
        :return:
        '''
        # 根据传入的limit 做一个切片处理
        if len(limit_l) != 0:
            return where_res[0:int(limit_l[0])]
        else:
            return where_res

    11.处理select中除了*之外的其他字符 select id name IT from*****. 语句只返回id  name  IT这个三个属性

    def search_action(limit_res,select_l):
        '''
        根据用户的输入,返回特定的数组
        :param limit_res:
        :param select_l:
        :return:
        '''
        result = []
        fileds_l = []
        titles = 'id,name,age,phone,dept,enroll_data'.split(',')
        if select_l[0] == "*":
            fileds_l = titles
            result = limit_res
        else:
            # 循环匹配where和limit语句的执行结果
            for item in limit_res:
                # zip 转化为一个元祖,如果两个队列长度不一致,以短的返回一个元祖
                #{'id': '25', 'name': '张国辉', 'age': '30', 'phone': '18500841678', 'dept': '运维', 'enroll_data': '2007-8-1
    '}
                temp_dict = dict(zip(titles,item))
                condition_l = []
    
                fileds_l = select_l
                for i in fileds_l:
                    condition_l.append(temp_dict[i].strip())
                result.append(condition_l)
        #只返回搜索的字段名
        return (fileds_l,result)

       12.删除操作delete

    def delete_action(sql_dic):
        '''
        删除表中的元素,返回删除成功标识
        :param sql_dic:
        :return:
        '''
        # 'func': delete_action,
        # 'delete': [],
        # 'from': [],
        # 'where': [],
        if '.' in sql_dic['from'][0]:
            file_path = sql_dic['from'][0].replace('.','/')
        else:
            file_path = sql_dic['from'][0]
        titles = "id,name,age,phone,dept,enroll_date".split(',')
        # 创建一个临时文件复制指定文件的内容,完成之后,删除源文件,修改临时文件的名称
        with open(file_path,'r',encoding='utf-8') as r_obj,
                open("%s.txt" %file_path,'w',encoding='utf-8') as w_obj:
            for item in r_obj:
                tem_dic = dict(zip(titles,item.split(',')))
                flag = logic_action(tem_dic,sql_dic['where'])
                if not flag:
                    w_obj.write(item)
            w_obj.flush()#立刻从内存刷到硬盘上
    
        os.remove(file_path)
        os.rename("%s.txt" %file_path,file_path)
        return [['删除成功']]

     13.更新操作update

    def update_action(sql_dic):
        '''
        更新对应的数据
        'func': update_action,
            'update': [],
            'set': [],
            'where': [],
        :param sql_dic:
        :return:
        '''
        if '.' in sql_dic['update'][0]:
            file_path = sql_dic['update'][0].replace('.','/')
        else:
            file_path = sql_dic['update'][0]
    
        set_sql = sql_dic['set'][0].split(',')
        sets = []
        for value in set_sql:
            sets.append(value.split('='))
    
        titles = "id,name,age,phone,dept,enroll_date".split(',')
        with open(file_path,'r',encoding='utf-8') as r_obj,
                open('%s.txt' %file_path,'w',encoding='utf-8') as w_obj:
            # 遍历文件内容
            for item in r_obj:
                changeline = []
                # 将每组数据格式化为一个字典
                temp_dic = dict(zip(titles,item.split(',')))
                # 匹配字典中的值是否和where语句中的值相同
                flag = logic_action(temp_dic,sql_dic['where'])
                if flag:
                    #在数据库中存在这个数据,拿到数据修改为要设置的值
                    for set in sets:
                        k = set[0]
                        v = set[-1].strip("'")
                        temp_dic[k] = v
                    for prop in titles:
                        changeline.append(temp_dic[prop])
                # 数组转化为字符串,保存到文件中
                w_obj.write(','.join(changeline))
            w_obj.flush()
        os.remove(file_path)
        os.rename("%s.txt" % file_path, file_path)
        return [['修改成功']]

       14.insert插入操作

    def insert_action(sql_dic):
        '''
        'func': insert_action,
            'insert': [],
            'into': [],
            'values': [],
        插入新的元素,先要获取最后一行的数据,获取到他的ID,自增长ID
        :param sql_dic:
        :return:
        '''
        if '.' in sql_dic['into'][0]:
            file_path = sql_dic['into'][0].replace('.','/')
        else:
            file_path = sql_dic['into'][0]
        print(file_path)
        titles = "id,name,age,phone,dept,enroll_date".split(',')
        with open(file_path,'ab+') as f_obj:
            # 查询文件的最后一行
            offset = -50
            while True:
                # seek(offset,可选值) 可选值默认为0 从文件开头算 1 从当前位置开始算 2从文件末尾算
                f_obj.seek(offset,2)
                lines = f_obj.readlines()
                if len(lines) > 1:
                    last_line = lines[-1]
                    break
                offset *= 2
            # 以二进制打开的文件,要对字符串解码
            last_line = last_line.decode(encoding='utf-8')
    
            #取出最后一行的ID
            temp_dict = dict(zip(titles,last_line.split(',')))
            last_id = int(temp_dict['id'])
            #id加一 添加到数组中
            add_info = sql_dic['values']
            add_info.insert(0,str(last_id+1))
            # 数组转化为字符串
            new_line = ','.join(add_info) + '
    '
            f_obj.write(new_line.encode('utf-8'))
            f_obj.flush()
        return [['添加成功']]

     因为添加的数据库格式固定了,还可以添加不知道字段名的数据库进行操作,全局修改titles这个数组来匹配所有的字段名,也可以手动输入,在进行解析,实现一个多个字段可用的SQL查询小工具

  • 相关阅读:
    【POJ 3525】Most Distant Point from the Sea(直线平移、半平面交)
    【HDU 4940】Destroy Transportation system(无源无汇带上下界可行流)
    codevs 5962 [SDOI2017]数字表格
    【NOIP2016】天天爱跑步
    [2011WorldFinal]Chips Challenge[流量平衡]
    [Ahoi2014]支线剧情[无源汇有下界最小费用可行流]
    [NOI2008] 志愿者招募[流量平衡]
    [Wc2007]剪刀石头布[补集转化+拆边]
    poj3281 Dining[最大流]
    1458: 士兵占领[最大流]
  • 原文地址:https://www.cnblogs.com/zj901203/p/9295970.html
Copyright © 2020-2023  润新知