PYTHON用字典保存各种程序资源的文本或二进制数据(支持保存和导入字典数据),发布程序可不必再跟随图标等资源文件了

    相对上一篇文章用字典来保存文件数据的方式,又重新编写了一更详细的字典内存数据类Res,可对用字典来保存程序运行过程中可能要用到的各种图标ICO,光标CUR,各种类型的图象,视频,声音等等数据,在程序开始时就加载全部资源到字典变量中,程序在需要使用时,直接从字典中得到此文件资源对应的二进制或文本类型数据即可使用,避免频繁读取硬盘上的文件,简化编程时对大量文件名称的管理(转化为用字典中的类型+KEY关键字名称来管理文件内容),提高程序运行速度。

    当前字典数据可以保存到一个外部文件中,需要时也可以导入此字典文件到内存来恢复字典数据,这样就可以将程序依赖的字典中已导入的各种其他文件全部删除,不必再跟到应用程序一并发布了,只需跟随一字典数据保存后的文件即可。

 字典数据支持保存或加载的文件格式有PICKLE格式,JSON格式,CSV格式。

模块Res.Py中除Res类外,还提供了其对应的轻量级MemFile类来将字典同内存和外部文件进行数据操作的类,供参考。

示例主窗口代码如下( 代码中要用到的1.ico等文件自行准备COPY到程序目录下即可)def.txt和def1.txt测试用纯文本文件,可自行用记事本随意输入几行文本即可

测试用类表格数据文件table02.txt中的内容可参考下面输入(即行列数据,逗号分隔)

TABLE02,01,02,03
10,11,12,13
20,21,22,23
30,31,32,33

#主窗口模块DicMemRes_Test.py
#
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5 import QtCore
from PyQt5.QtGui import QMovie
from PyQt5.QtCore import QByteArray

from res import *                #导入自定义的要测试的res.py模块(包含两个类Res,MemFiles)
##################################################################
#测试res.py中的Res类(字典内存文件)和MemFiles类(轻量版字典内存文件)QT主窗体
class MyWidget(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('测试res类:将各种资源加载到内存字典中')
        self.setGeometry(0, 0, 1920, 900)
        self.statusbar = self.statusBar()
        self.statusbar.showMessage('准备')
        self.setStatusTip('准备...')  
        self.qimage = QImage('1.jpg')   #底图
        self.initUI()
    def initUI(self):
       
        self.btn_Test1=QPushButton('1测试代码1',self)
        self.btn_Test1.setGeometry(1600,50,150,50)
        self.btn_Test1.clicked.connect(self.test1)
        self.btn_Test2=QPushButton('2测试代码',self)
        self.btn_Test2.setGeometry(1600,150,150,50)
        self.btn_Test2.clicked.connect(self.test2)
        self.btn_Test3=QPushButton('3测试代码',self)
        self.btn_Test3.setGeometry(1600,250,150,50)
        self.btn_Test3.clicked.connect(self.test3)

        self.Lab01=QLabel(self)
        self.Lab01.setGeometry(50,50,200,200)
        self.Lab01.setText('测试标签1')
        #self.Lab01.raise_()

        self.Lab02=QLabel(self)
        self.Lab02.setGeometry(50,300,200,200)
        self.Lab02.setText('测试标签2')


########################################################################################
    def test1(self):   
        res=Res()
        lstico=['1.ico','2.ico','3.ico']
        lsticokey=['ico01','ico02','ico03']
        res.addRes('ICO',lstico,lsticokey)   #批量增加
        lstimg=['1.png','2.png','3.png','4.png']
        lstimgkey=['png1','png2','png3','png4']
        res.addRes('IMG',lstimg,lstimgkey)   #批量增加
        res.addRes('IMG','5g.jpg','jpg-5g')  #单文件增加
        res.addRes('GIF','1.gif','gif01')  #单文件增加
        res.addRes('TXT','abc123','txt01')   #单字串增加
        res.addRes('TXTDATA','def.txt','txtData01')    #单文本增加

        lstTable=[[0,'a',2,'d'],
                [1,'b',3,'e'],
                [2,'c',4,'f'],
                ]
        res.addRes('TABLEDATA',lstTable,'TABLE01')   #单表数据增加

        binImg,dataType= res.getRes('img','png2')   #调用返回的第一个值为二进制序列数
        binImg=QImage.fromData(binImg)              #将得到字典中的二进制图象数据转换成PYQT的图像对象格式
        pixmap = QPixmap.fromImage(binImg)
        self.Lab01.setPixmap(pixmap)                #将从字典中得到的图象数据在标签控件上显示出来

        binIco,dataType= res.getRes('ico','ico02')   #调用返回的第一个值为二进制序列数
        binIco=QImage.fromData(binIco)              #将得到字典中的二进制图象数据转换成PYQT的图像对象格式
        pixmap = QPixmap.fromImage(binIco)
        self.btn_Test1.setIcon(QIcon(pixmap)) 

        binGif,dataType= res.getRes('GIF','gif01')   #调用返回的第一个值为二进制序列数
        with open('temp.gif', 'wb') as file:     #因对GIF类内存二进制,暂没有找至直接加载到标签控件上进行播放的方式,只能先写成一临时文件加载播放,当然这是多此一举,不知有没有直接加载的方式,探索中....
            file.write(binGif)
        self.movie = QMovie('temp.gif')   
        self.Lab02.setScaledContents(True)  
        self.Lab02.setMovie(self.movie)
        self.movie.start()
        #os.remove('temp.gif')  # 删除临时文件,对GIF文件不能被删除,因上面的控件还是使用,GIF文件真没办法用内存文件播放吗?

        lsttable,retype=res.getRes('TABLEDATA','TABLE01')  #从表单类型的字典中得到值测试
        print(lsttable)


    def test2(self):   
        #更新一个文本文件到字典数据中去
        print('\n\n##########以下为test2的测试值情况############')
        res2=Res()      #因字典数据在类Res中采用的是全局变量,所有Res类实例化对象对这部份全局变量的访问是公用的,即每个实例化成员都可得到当前最新的类字典对象中的数据,且可以对其进行后序操作
        file1='def1.txt'
        allTxt=''
        lindS=''
        lstData=['',[]]  #其值为两个成员[0]=全文件文本,[1]=每行的文本内容
        lstRowData=[]
        rf = open(file1,'r',encoding='utf-8')
        for lineS in rf.readlines():
            s=lineS.strip()
            allTxt = allTxt+s+'\n'
            s=s.replace("\n","")  #将从文件中读出的\n删除,此语句可能会报异常
            s=s.replace("\r","")  #将从文件中读出的\r删除,此语句可能会报异常
            lstRowData.append(s)
        rf.close
        lstData[0]=allTxt
        lstData[1]=copy.deepcopy(lstRowData) 
        res2.updateRes('TXTDATA','txtData01',lstData)

        txt,dataType=res2.getRes('TXTDATA','txtData01')   #调用返回的第一个值为一列表,0序号为一文本,1序号为各文本按行计处的列对象
        txt0=txt[0]
        print(txt,dataType)
        print(txt0)

    #测试PICKL,JSON,CSV格式的字典保存和加载
    def test3(self):   
        print('\n\n##########以下为test3的测试值情况############')
        res3=Res()   #因字典数据在类Res中采用的是全局变量,所有Res类实例化对象对这部份全局变量的访问是公用的,即每个实例化成员都可得到当前最新的类字典对象中的数据,且可以对其进行后序操作
        if(res3.saveDicData('dic_data.pickle')): #将当前已经初始化好的字典中的全部数据保存到文件dic_data.pickle中
            print('PICKLE方式保存字典数据成功!')
        if(res3.loadDicData('dic_data.pickle')):   #从字典数据文件中导入全部数据
            print('PICKLE方式导入字典数据成功!')
            txt,dataType=res3.getRes('TXTDATA','txtData01')   #测试导入字典文件后的一些字典值
            txt0=txt[0]
            print(txt,dataType)
            print(txt0)

        res3.addResTable('TABLE02.txt','TABLE02')  #从表格类型的文件中加载数据到KEY='TABLE02'
        lst03=[['Table03','41','42','43'],
             ['50','51','52','53'],
             ['60','61','62','63'],
             ]

        res3.addResTable(lst03,'TABLE03')     #直接赋值数据
        if(res3.saveDicToJsonFile('dic_data.json')): #将当前已经初始化好的字典中的全部数据保存到文件dic_data.json中
            print('JSON方式保存字典数据成功!')
        lsttabledata,dataType=res3.getRes('TABLEDATA','TABLE03')
        print(lsttabledata)
        print('\n\n打开刚保存的JSON文件,看还原字典后的数据如下:')
        res3.loadDicFromJsonFile('dic_data.json','TABLE03')  #再从刚保存的JSON文件中导入数据看有变化没有
        lsttabledata,dataType=res3.getRes('TABLEDATA','TABLE03')
        print(f'重新从外部SJON文件导入后得到的数据{lsttabledata}')

        lstHead=['0列','1列','2列','3列']
        res3.saveDicToCsvFile('dic_data.crv','TABLE03',lstHead)    #将当前已经初始化好的字典中的全部数据保存到文件dic_data.crv中
        print('\n\n打开刚保存的CRV文件,看还原字典后的数据如下:')
        res3.loadDicFromCrvFile('dic_data.crv','TABLE03')
        print(f'重新从外部CSV文件导入后得到的数据{res3.dic_TableData}')


        lstico=['1.ico','2.ico','3.ico']
        memf=MemFiles(lstico,'1.ico')
        memf.saveDicData('memfile.mem')
        if(memf.loadDicData('memfile.mem')):
            print('导入基于MemFiles类的内存文件memfile.mem成功')



########################################################################################
if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MyWidget()
    w.show()
    sys.exit(app.exec())

自定义的字典内存数据类Res.py模块代码如下:

#res.py:定义一字典来保存各种资源文件并加载到内存中去,支持文本,二进制,表格类数据
#
import os,sys       
import time
import math
import copy 
import random
import pickle   #支持保存和导入字典数据的模块(PICKLE方式:仅PTHON支持),为默认,可以保存和加载不受限制的任意数据
import json     #支持保存和导入字典数据的模块(JSON方式:多种语言支持)
import csv      #支持保存和导入字典数据的模块(CSV方式:多种语言支持)

"""本类暂没有使用QT5的类型,暂无需导入库
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5 import QtCore
from PyQt5.QtGui import QMovie
from PyQt5.QtCore import QByteArray
"""
#定义将的所有程序要用到各种资源文件预加载到内存中,使用时从此内存中的数据直接使用
class Res():
    dic_txtRes={}   #单行文本
    dic_icoRes={}   #二进制
    dic_curRes={}   #二进制
    dic_imgRes={}    #二进制
    dic_gifRes={}    #二进制:对QT暂没有找到从内存中的GIF直接加载到控件上的方式,需要再转回临时文件的方式加载到标签控件,真没有办法了吗?
    dic_voiceRes={}  #二进制
    dic_videoRes={}   #二进制
    dic_htmlRes={}    #多行文本
    dic_codeRes={}    #多行文本
    dic_binData={}   #其他任何二进制文件资源
    dic_txtData={}   #其他任何文本文件资源,字典对象为文本列表['所有行的全部文本内容......',[1行文本,2行文本,......]]
    dic_TableData={}   #为支持CSV和JSON格式的字典存储,此字典格式{key:[列表元素1,列表元素2......]},对应类型"TABLEDATA"

    dic_Res={
            'TXT':dic_txtRes,  #字符串资源  
            'ICO':dic_icoRes,  #ICO类图标资源     
            'CUR':dic_curRes,  #CUR光标资源
            'IMG':dic_imgRes,  #其他各种图象资源
            'GIF':dic_gifRes,  #各种GIF动画图象资源
            'VOICE':dic_voiceRes,  #各种语音资源
            'VIDEO':dic_videoRes,  #各种视频资源
            'HTML':dic_htmlRes,  #各种网页资源
            'CODE':dic_codeRes,  #各种代码资源
            'BINDTAT':dic_binData,  #其他未知的各种二进制文件资源
            'TXTDATA':dic_txtData,  #其他未知的各种文本文件资源:此字典会同时保存文本的各行内容到一列表中 
            'TABLEDATA':dic_TableData #类似表格的行列数据字典,只此字典支持CSV和JSON格式
            }  
    
   

    #init初始化函数前定义的变量(以上各行)可以被类的所有实例化对象共同使用和访问
    def __init__(self):  
        self.fileCount=0        #当前字典中文件的数量(含KEY=0的默认文件数量)
        
    #增加资源文件到字典库中
    def addRes(self,resType,resDatas,resKey):
        resType=resType.upper()
        vartype=type(resDatas)
        if(isinstance(resDatas,str)):    #是单一字符串,即传入了一个字符串或一个文件,注意,对应的resKey也应是字符串不能是列表
            print(f'参数为字符串,即传入了一个单一资源"{resDatas}",对应此资源的key="{resKey}"')
            resKey=resKey.upper()
            if(resType=='TXT'):                          #注:从PYTHON3.1版开始,支持math 值:   case 值1:。。。。。。case _:其他。。。的匹配结构了,本类未采用此结构代码
                if(isinstance(resDatas,str)):
                    Res.dic_txtRes[resKey]=resDatas
                else:
                    print('对字符串类型资源,传入函数参数必须是单个字符串和列表字符串两种类型')
            elif(resType=='ICO'):
                if(os.path.exists(resDatas)):
                    with open(resDatas, 'rb') as f:  #对ICO类型,以二进制方式读入文件
                        Res.dic_icoRes[resKey] = f.read()
            elif(resType=='CUR'):
                if(os.path.exists(resDatas)):
                    with open(resDatas, 'rb') as f:  #对CUR类型,以二进制方式读入文件
                        Res.dic_curRes[resKey] = f.read()
            elif(resType=='IMG'):
                if(os.path.exists(resDatas)):
                    with open(resDatas, 'rb') as f:  #对各种图片类型,以二进制方式读入文件
                        Res.dic_imgRes[resKey] = f.read()
            elif(resType=='GIF'):
                if(os.path.exists(resDatas)):
                    with open(resDatas, 'rb') as f:  #对GIF动图类型,以二进制方式读入文件
                        Res.dic_gifRes[resKey] = f.read()
            elif(resType=='VOICE'):
                if(os.path.exists(resDatas)):
                    with open(resDatas, 'rb') as f:  #对语音类型,以二进制方式读入文件
                        Res.dic_voiceRes[resKey] = f.read()
            elif(resType=='VIDEO'):
                if(os.path.exists(resDatas)):
                    with open(resDatas, 'rb') as f:  #对视频类型,以二进制方式读入文件
                        Res.dic_videoRes[resKey] = f.read()
            elif(resType=='BINDATA'):
                if(os.path.exists(resDatas)):
                    with open(resDatas, 'rb') as f:  #对其他二进制类型,以二进制方式读入文件
                        Res.dic_binData[resKey] = f.read()
            elif(resType=='TXTDATA' or resType=='HTML' or resType=='CODE'):
                lstData=['',[]]  #其值为两个成员[0]=全文件文本,[1]=每行的文本内容
                if(os.path.exists(resDatas)):
                    rf = open(resDatas,'r',encoding='utf-8')
                    allTxt=''
                    lindS=''
                    lstRowData=[]
                    for lineS in rf.readlines():
                        #print(lineS)
                        s=lineS.strip()
                        allTxt = allTxt+s+'\n'
                        s=s.replace("\n","")  #将从文件中读出的\n删除,此语句可能会报异常
                        s=s.replace("\r","")  #将从文件中读出的\r删除,此语句可能会报异常
                        lstRowData.append(s)
                    rf.close
                    lstData[0]=allTxt
                    lstData[1]=copy.deepcopy(lstRowData) 
                Res.dic_txtData[resKey] = copy.deepcopy(lstData) 
            elif(resType=='TABLEDATA'): 
                self.addResTable(resDatas,resKey)
            else:
                print(f'导入的资源类型{resType}本程序暂不支持,请扩展完善代码。。。。')

        elif(isinstance(resDatas,list) and isinstance(resKey,list) and resType!='TABLEDATA'):    #是列表对象,即传入了多个字符串或多个文件
            print(f'参数为列表,即传入了多个资源"{resDatas}"')
            for oneData , oneKey in zip(resDatas,resKey):
                self.addRes(resType,oneData,oneKey)   #递归回调

        elif(isinstance(resDatas,list) and  resType=='TABLEDATA'):
            #对TABLEDATA类型字典,虽然传入的是列表,但此列表不能递归调用,应是一次性赋值
            Res.dic_TableData[resKey] = copy.deepcopy(resDatas) 
        else:
            print(f'传入的对象即不是字符串,也不是列表,或第二第三个参数不全是列表对象,无法增加此内容到资源字典中去')

    #对类似表格行列的数据,在本函数中来处理,字典对应于dic_TableData,如晨指定的resKey已存在,则会覆盖之前的表格数据
    #本方法适用于CSV和JSON格式的字典存储,对应于字典Res.dic_TableData
    def addResTable(self,resDatas,resKey): 
        try:
            row=len(Res.dic_TableData)   #当前字典已有行数
            lstData=[]  #其值为两个成员所传入的列表行数或文件中的行数
            lstRowData=[]
            if(isinstance(resDatas,str)): #传入的参数为字符型,表示此字符串为文件名
                if(os.path.exists(resDatas)):
                    rf = open(resDatas,'r',encoding='utf-8')
                    lindS=''
                    for lineS in rf.readlines():
                        lstRowData.clear()
                        #print(lineS)
                        s=lineS.strip()
                        s=s.replace("\n","")  #将从文件中读出的\n删除,此语句可能会报异常
                        s=s.replace("\r","")  #将从文件中读出的\r删除,此语句可能会报异常
                        lstRowData = s.split(',')
                        lstData.append(copy.deepcopy(lstRowData))
                    rf.close
                    Res.dic_TableData[resKey] = copy.deepcopy(lstData) 

            elif(isinstance(resDatas,list)): #传入的参数直接就是一表格的二维列表[[],[],............]
                if(len(resDatas)>0):
                    if(len(resDatas[0])>0):   #是多维数组
                        #for lstone in resDatas:
                            #print(lstone)
                        Res.dic_TableData[resKey] = copy.deepcopy(resDatas) 
        except Exception as e:  # 如果发生异常,执行这里的代码
            print(f'创建表格类型字典出现错误,可能是传入的列表对象不是二维列表对象,或文件内容同表格格式不相符')


    #得到资源字典中的数据
    def getRes(self,resType,resKey,bDeepCopy=True):   
        resType=resType.upper()
        resKey=resKey.upper()
        reType='BIN'
        resData=None
        try:
            if(resType=='TABLEDATA'): #是表格数据时
                reType='TABLEDATA'
                resData=Res.dic_TableData.get(resKey,None)   #是一多维列表
            else:
                if(resType=='TXT' or resType=='CODE' or resType=='HTML' or resType=='TXTDATA'): 
                    reType='TXT'   
                resData=Res.dic_Res.get(resType,None).get(resKey,None)   #是文本或二进制

            if(resData is None):print('没有从字典中得到任何值,请检查程序')
            if bDeepCopy:  #返回值时应为得到值的副本(即在调用外对返回值进行修改数据时,不会同时关联修改到本类的存在字典中的数据,如不用深度COPY,对列表等对象值在调用处改动后会关联改动到本类的字典中存储的数据
                return copy.deepcopy(resData),reType
            return resData,reType   #返回两个值,后一个值代表返回的数据是二进制还是文本
        except Exception as e:  # 如果发生异常,执行这里的代码
            print(f'导入的资源类型{resType},资源key={resKey}发生异常,返回None')
            reType='ERR'
            return None,reType
        
    #更新数据到字典中对应的KEY的数据中,如果键值不存在,则在字典中新建此数据项
    def updateRes(self,resType,resKey,resDatas,bDeepCopy=True):
        resType=resType.upper()
        resKey=resKey.upper()
        try:
            if(bDeepCopy):
                resDatas=copy.deepcopy(resDatas)
            if(resType=='TXT'):   #纯文本
                if(not isinstance(resDatas,str)):
                    print('更新"TXT"类字典的变量不是纯文本,未更新成功,请检查代码')
                    return False
                Res.dic_txtRes[resKey]=resDatas
            elif(resType=='CODE'):  #可能是多行文本
                if(not isinstance(resDatas,list) or len(resDatas)!= 2):
                    print('更新"CODE"等多行文本类字典的变量不是列表或列表格式不对,未更新成功,请检查代码')
                    return False
                Res.dic_codeRes[resKey]=resDatas
            elif(resType=='HTML' ):  #可能是多行文本
                if(not isinstance(resDatas,list) or len(resDatas)!= 2):
                    print('更新“HTML”等多行文本类字典的变量不是列表或列表格式不对,未更新成功,请检查代码')
                    return False
                Res.dic_htmlRes[resKey]=resDatas
            elif(resType=='TXTDATA' ):  #可能是多行文本
                if(not isinstance(resDatas,list) or len(resDatas)!= 2):
                    print('更新“TXTDATA”等多行文本类字典的变量不是列表或列表格式不对,未更新成功,请检查代码')
                    return False
                Res.dic_txtData[resKey]=resDatas
            elif(resType=='TABLEDATA' ):  #可能是多行文本,更新来的变量就是多行多列的列表对象
                if(not isinstance(resDatas,list)):
                    print('更新“TABLEDATA”等多行文本类字典的变量不是列表或列表格式不对,未更新成功,请检查代码')
                    return False
                if(len(resDatas)>0):   #数据行数大于0
                    if(resDatas[0]>0): #第0行数据列数大于0
                        Res.dic_TableData[resKey]=resDatas
                else:
                    print('更新“TABLEDATA”等多行文本类字典的变量不是列表或列表格式不对,未更新成功,请检查代码')
                    return False
            elif(resType=='ICO'):
                Res.dic_icoRes[resKey] = resDatas
            elif(resType=='CUR'):
                Res.dic_curRes[resKey] = resDatas
            elif(resType=='IMG'):
                Res.dic_imgRes[resKey] = resDatas
            elif(resType=='GIF'):
                Res.dic_gifRes[resKey] = resDatas
            elif(resType=='VOICE'):
                Res.dic_voiceRes[resKey] = resDatas
            elif(resType=='VIDEO'):
                Res.dic_videoRes[resKey] = resDatas
            elif(resType=='BINDATA'):
                Res.dic_binData[resKey] = resDatas
            else:
                print('出现不支持的数据类型,请检查调用处代码')
                return False
            return True
        except Exception as e:  # 如果发生异常,执行这里的代码
            print(f'更新的资源类型{resType},资源key={resKey}发生异常,返回None')
            return False
        
    #将当前的字典数据保存到一外部文件中,在需要时导入,保存的方式有
    # 'PICKLE'=PYTHON专用的格式,字典可作复杂的二进制数据,建议用此方式(也是默认方式)
    # 'JSON'=多种编程语言通用的一种格式,对此格式的保存和加载用saveDicToJsonFile和loadDicFromJsonFile
    # 'CSV'=带逗号分隔符的数据文件格式,对此格式的保存和加载用saveDicToCsvFile和loadDicFromCrvFile
    def saveDicData(self,saveFileName,bExistCover=True):  
        try:
            if(os.path.exists(saveFileName)):
                if(not bExistCover):return False
            binData=pickle.dumps(Res.dic_Res)
            with open(saveFileName,'wb') as wf: 
                wf.write(binData)
            return True
        except Exception as e:  # 如果发生异常,执行这里的代码
                print(f'保存字典数据内容到PICKLE格式文件{saveFileName}发生异常')
                return False
    #从外部文件加载本类用到的Res字典类的数据
    def loadDicData(self,openFileName,saveType='PICKLE'):
        if(os.path.exists(openFileName)):
            Res.dic_Res.clear()  #先清除字典
            if(saveType=='PICKLE'):
                with open(openFileName, "rb") as tf:
                    Res.dic_Res = pickle.load(tf)
            elif(saveType=='JSON'):
                tf = open(openFileName, "r")
                Res.dic_Res = json.load(tf)
            else:
                print('暂只支持"PICKLE"、"JSON"、“CSV”三种方式来从字典数据文件加载到PYTHON中')
                return False
            
            return True
    #保存为JSON格式的文件(要求字典中只能是全字符列表,故只对本类的子字典Res.dic_TableData进行操作,不能对Res.dic_Res进行操作)
    #JSON的格式同字典相类似,
    def saveDicToJsonFile(self,saveFileName,bExistCover=True):  
        try:
            item = json.dumps(Res.dic_TableData)       #先将字典对象转化为可写入文本的字符串
            with open(saveFileName, "w", encoding='utf-8') as f:
                f.write(item + "\n")
            return True
        except Exception as e:  # 如果发生异常,执行这里的代码
                print(f'保存字典数据内容到JSON格式文件{saveFileName}发生异常,字典中的数据格式不支此格式') 

    #打开JSON格式的文件(要求字典中只能是全字符列表,故只对本类的子字典Res.dic_TableData进行操作,不能对Res.dic_Res进行操作)        
    def loadDicFromJsonFile(self,openFileName,dicKeyTableName):
        if(os.path.exists(openFileName)):
            with open(openFileName, 'r', encoding='utf-8') as rf:
                #Res.dic_TableData.clear()  #如字典中存在多价目个表及对应的列表数据,就不能清空,应采用赋值方式
                Res.dic_TableData[dicKeyTableName] = json.load(rf)

    #保存为CSV格式的文件(本类数据格式复杂,不适用此方法)
    def saveDicToCsvFile(self,saveFileName,resKey,lstHeads='',bAddData=False):
            try:
                wtype="w"
                if bAddData: wtype="a"
                # 第一行写入表头
                #if  os.path.exists(saveFileName):
                with open(saveFileName, wtype, newline='', encoding='utf-8') as csvfile:  # newline='' 去除空白行
                    #writer = csv.DictWriter(csvfile, fieldnames = lstHeads)  # 写字典的方法,有问题舍弃不用
                    #writer.writeheader()  # 写表头的方法
                    #writer = csv.DictWriter(csvfile, fieldnames = lstrowdata)
                    writer = csv.writer(csvfile)
                    lstRowDatas,retype=self.getRes('TABLEDATA',resKey)
                    for lstrowdata in lstRowDatas:
                        writer.writerow(lstrowdata)        # 按行写入数据
                    return True
            except Exception as e:
                print("写SCV文件时出现错误", e)
                return False
    #打开CRV格式的文件(要求字典中只能是全字符列表,故只对本类的子字典Res.dic_TableData进行操作,不能对Res.dic_Res进行操作)    
    #导入文件后     
    def loadDicFromCrvFile(self,openFileName,dicKeyTableName):
        if(os.path.exists(openFileName)):
                with open(openFileName, newline='') as csvfile:
                    #Res.dic_TableData.clear()  #因此字典可能有多个表KEY对应的列表数据,故不清除
                    csvreader = csv.reader(csvfile, delimiter=',')
                    lsttabdata=[]
                    for row in csvreader:
                        lsttabdata.append(row)
                        print(row)
                    Res.dic_TableData[dicKeyTableName]=lsttabdata  #将导入的数据更新指定的字典的KEY表名

##########################################################################
#定义应用程序公用的通用内存二进制文件类:相当于Res类的轻量化版本,此版本字典的KEY值为连续的整数值,Res的KEY值为自定义的字符串值
class MemFiles():
    #构造初始化函数:指定的文件生成内存文件,参数1:初始化的内存文件要传入的文件或文件列表(一个或多个文件)
    def __init__(self,fileNames,defFile):  
        self.dicMemFiles={} #将指定列表中的全部文件读入内存,并保存在此字典中,通过KEY来访问,默认为KEY=0时,留用给没有从字典中得到文件数据时的默认值
        self.lstMemFileName=[]  #对应字典读入的内存文件(文件应为绝对路径或模块可识别的路径不含路径的文件名),第0索引文件为默认文件留用,本变量仅用于调试用
        self.dicFile={"FILENAME":self.lstMemFileName,"FILEDATA":self.dicMemFiles}
        self.fileCount=0        #当前字典中文件的数量(含KEY=0的默认文件数量)
        self.MakeMemFile(fileNames,defFile,True)  #初始化时,第三个参数应为True
        
    #根据传入的文件名初始化内存文件(bNew=True时表示先清空原有的再重新创建,bNew=False时表是增加):本函数不允许传入的文件不能正确导入到内存文件中,因每个KEY对应其他代码的调用,如果KEY不一一对应,程序运行结果将不可预见
    #不论传入的文件不论是否创建成功,均会为内存字典分配一个KEY,只是对应的KEY的文件读入内存的二进制数据为None
    def MakeMemFile(self,fileNames,defFile='',bNew=False):
        if bNew:   #如果新建内存文件,会用defFile文件作为KEY=0的内存文件数据
            self.fileCount=0
            self.dicMemFiles.clear()
            self.lstMemFileName.clear()
            self.lstMemFileName.append(defFile)
            if(defFile==''):
                print('新建内存文件时,必须要指定一个默认文件为作字典的默认内存数据,现将字典KEY=0的默认值为None')
                self.dicMemFiles[0]=None
            self.fileCount=1    
            
        #处理参数表传入的默认文件名
        if(defFile==''):  #表示无需对默认文件进行任何操作
            pass
        else:  #如有定义默认文件名,则更新字典中的KEY=0的默认文件数据
            self.lstMemFileName[0]=defFile   #默认文件无论是否正确加载到内存文件中,列表中的值均为传入的内存文件名称
            if(os.path.exists(defFile)):
                with open(defFile, 'rb') as f:
                    binData = f.read()
                    self.dicMemFiles[0]=binData
                    print(f'当前默认文件数据对应的默认文件名="{defFile}"')
            else:
                print(f'指定默认文件"{defFile}"不存在,字典KEY=0的默认内存文件暂为None,请检查文件')
                self.dicMemFiles[0]=None

        #判断传入的fileNames是一列表文件组,还是单一的一个字符串文件
        vartype=type(fileNames)
        if(isinstance(fileNames,str)): #是单一字符串,即传入了一个单文件
            print(f'参数为字符串,即传入了一个单一文件"{fileNames}"')
            if(os.path.exists(fileNames)):
                with open(fileNames, 'rb') as f:
                    binData = f.read()
                    self.dicMemFiles[self.fileCount]=binData    
                    #f.close()
            else:
                print(f'指定文件"{fileNames}"不存在,向字典中增加一个内存文件出现错误!!,暂将此序号key={self.fileCount}文件同key=0的默认文件内容相同')
                self.dicMemFiles[self.fileCount]=self.dicMemFiles[0]  
            self.lstMemFileName.append(fileNames) 
            self.fileCount+=1    
        elif(isinstance(fileNames,list)): #是一列表变量,即传入了多个文件
            print(f'参数为列表,即传入了多个文件{fileNames}')
            for filename in fileNames:
                if(os.path.exists(filename)):
                    with open(filename, 'rb') as f:
                        rData = f.read()
                        self.dicMemFiles[self.fileCount]=rData      #内存文件增加一个        
                else:
                    print(f'指定文件"{filename}"不存在,向字典中增加一个内存文件出现错误!!,暂将此序号key={self.fileCount}文件同key=0的默认文件内容相同')
                    self.dicMemFiles[self.fileCount]=self.dicMemFiles[0]  
                self.lstMemFileName.append(filename) 
                self.fileCount+=1


    #得到内存文件中的二进制数据:参数1=要得到字典内存文件的KEY值,
    # bDeepCopy=True时,返回值时会用深度COPY一副本来使用,这样避免调用端对字典内存文件改动后影响其他同样在使用这个内存文件的
    # defData不为空时:如果没有从字典中得到相对应的,用defData作为返回值,为None时,以字典的key=0 的内容作为出错返回值
    def getMemFileData(self,dicKeyID,bDeepCopy=False,defData=None,bUseDicDefValue=True):
        data=None
        if(len(self.dicMemFiles)>0):
            if(defData==None):  #参数未再指定从字典中得到内存文件数据出错时的默认数据时,且bUseDicDefValue==Ture时得到字典中的KEY=0的默认值
                if(bUseDicDefValue):
                    data= self.dicMemFiles.get(dicKeyID, self.dicMemFiles[0]) #用get函数来得到对应字典key的值,如果调用函数defData值不为空,则用defData值,否则用g_lstMemFileName[0]作为默认值
                else:
                    data= self.dicMemFiles.get(dicKeyID, None)  #如没有正确得到字典中的值,返回None,而不是key=0的默认值
            else:
                data= self.dicMemFiles.get(dicKeyID, defData) #用get函数来得到对应字典key的值,如果调用函数defData值不为空,则用defData值,否则用g_lstMemFileName[0]作为默认值
            #如指定了defData的数据,但还是得到失败,且又指定了bUseDicDefValue=True,失败时采用KEY=0的默认数据时
            if(data==None and bUseDicDefValue):
                data= self.dicMemFiles.get(0,None)  #再次失败还是会得到一个None
            if(bDeepCopy):
                return copy.deepcopy(data)
        return data
        
    #将当前数组中的数据采用PICKLE方式保存到外部文件中
    # 'PICKLE'=PYTHON专用的格式,字典可作复杂的二进制数据,建议用此方式(也是默认方式)
    # 'JSON'=多种编程语言通用的一种格式,本类不支持这和格式
    # 'CSV'=带逗号分隔符的数据文件格式,本类不支持这和格式
    def saveDicData(self,saveFileName,bExistCover=True):  
        try:
            if(os.path.exists(saveFileName)):
                if(not bExistCover):return False
            binData=pickle.dumps(self.dicFile)
            with open(saveFileName,'wb') as wf: 
                wf.write(binData)
            return True
        except Exception as e:  # 如果发生异常,执行这里的代码
                print(f'保存字典数据内容到PICKLE格式文件{saveFileName}发生异常')
                return False
    #从外部文件加载本类用到的MemFile类中的字典类的数据
    def loadDicData(self,openFileName):
        try:
            if(os.path.exists(openFileName)):
                self.dicFile.clear()  #先清除字典
                with open(openFileName, "rb") as tf:
                    self.dicFile = pickle.load(tf)
                    self.lstMemFileName=self.dicFile.get("FILENAME",None)   #回原到原字典中
                    self.dicMemFiles=self.dicFile.get("FILEDATA",None)      #回原到原字典中
                    return True 
            else:
                return False
        except Exception as e:  # 如果发生异常,执行这里的代码
                print(f'导入PICKLE格式文件{openFileName}发生异常')
                return False

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mr_LuoWei2009

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值