一、前言
所谓增量比较,就是说在每天探测的情况下,新文件和之前的存储文件之间的区别比较,如果采用遍历存储比较的方法显然非常复杂,这里主要提供笔者学习学长以及讨论、个人考虑之后的一种思路。
二、主要思路
1、存储文件的存储形式
在存储文件中,可能需要保存同一对象的很多状态,如果每次更新都采用全量存储,势必占用许多空间。经过学习和讨论,为了实现节省存储空间的目的,我们可以采取只存储变化数据的方式在文件最后追加存储。
这样一来,优点是显而易见的,因为大多数对象每次探测的结果可能是一样的,这些一样的部分我们将不做存储,从而减少空间占用。
2、某天的状态如何存储
这也带来了一定的问题,即当前状态是怎样的?或者某天的状态是怎样的?
这时候某天的状态存储在当天或者当天之前的最近一次的数据里。因为这天和上次存储的数据没有发生变化,所以这段时间的数据都可以用“当天之前的最近一次的数据”来代替,因此我们可以采用反向查找的方式得到这部分数据。
另外,还有两个问题,
(1)插入每条数据时候的比较非常复杂,可能需要多次逆序遍历整个表才能找到上次存储的状态。
(2)当随着时间推移这个表越来越大的时候,新建表的时候需要遍历得到之前的状态,并再次存储在新建表的起点,作为初始化的部分,这也比较复杂而且浪费时间。
3、利用存储表的“视图”来存储当前状态
这样一来,上面的问题就解决了,
(1)插入新数据的时候只需要和这个“视图“来比较即可,大大缩减了遍历的长度。
(2)新建表的时候也不必初始化,直接追加存储即可。
这个视图笔者加了引号,笔者想做的不是真的导出一个视图,而是建立一张新表,每次比较都更新这个表的状态,这在程序员方面会多做一点工作,但编写成功之后自动运行将比每次都提取视图节省许多时间和资源。
4、核心比较的代码
这个代码不用看了,笔者补充了更好的思路在后记中,之所以没有删除是不舍得删除当时写了半天的代码,好好~
(当时采用这种方式解决,时间复杂度O(n),现在有了更好的想法,时间复杂度O(1),2020.09.27修改,新增内容在后记中)
(1)如果读者只比较一组数据,并且有其他附加数据需要对应的情况,可以参考下面列表比较的代码
说明:核心思想是当对象不同或者新增时,在拼接附加数据之后更行当前状态表以及追加变化数据到存储文件。
def list_compare(new_list,new_spare,current_list,current_spare):
'''
功能:传入两大组数据,分别是新文件组和当前文件组
每组包括两个部分,是两个列表,
list中存储元组,spare中存储其他rdns数据
将新文件和当前文件比较,得到变化内容追加到store文件,并更新状态表
传回需要追加存储的数据和当前文件这两组数据(四个列表)
'''
new_store=[]
spare_to_store=[]
new_current=[]
spare_to_current=[]
#新表与当前状态表相比,得到此次发生变化的数据,存入store
l=len(new_list)
for i in range(l):
try:
#如果新元组在老元组中,则新状态表继承老数据,且store不存储
index=current_list.index(new_list[i])
new_current.append(current_list[index])
spare_to_current.append(current_spare[index])
except:
#如果新元组不在老元组中,新状态表和store对应添加新元组以及其对应数据
new_store.append(new_list[i])
spare_to_store.append(new_spare[i])
new_current.append(new_list[i])
spare_to_current.append(new_spare[i])
#对于原状态表的元素,判断是否已经继承,若未继承则记录其所有数据为-1
l=len(current_list)
for i in range(l):
try:
#如果已经继承,则不做处理
index=new_current.index(current_list[i])
except:
#如果没有继承,则数据全部写为-1
new_store.append(current_list[i])
spare_to_store.append(("-1","-1","-1","-1"))
new_current.append(current_list[i])
spare_to_current.append(("-1","-1","-1","-1"))
return (new_store,spare_to_store,new_current,spare_to_current)
if __name__=="__main__":
new_list,new_spare=[("a","b"),("c","a")],[("aaa","bbb"),("ccc","aaa")]
current_list,current_spare=[("1","2"),("3","4")],[("111","222"),("333","444")]
(list_to_store,spare_to_store,list_to_current,spare_to_current)=list_compare(new_list,new_spare,current_list,current_spare)
print(list_to_store,spare_to_store,list_to_current,spare_to_current,sep="\n")
(2)如果读者的数据包括对象、对象的值、以及一些其他需要对应但不包括在键值对中的数据,可以参考下面字典比较的代码,其他对象存储在列表中。
说明:这里的核心思想是在对象相同时,比较对象的值,若对象的值变化,则在拼接了其他数据之后更新当前状态表和追加变化数据到存储文件。
def dic_compare(new_dic,new_spare,current_dic,current_spare):
'''
功能:传入两大组数据,分别是新文件组和当前文件组
每组包括两个部分,分别是一个字典和一个列表,字典中存储主要数据,列表中存储时间和批次数据
将新文件字典和当前文件字典比较,得到变化内容追加到store文件
根据变化内容更新当前文件
传回需要追加存储的数据和当前文件这两组数据
'''
new_store={}
spare_to_store=[]
new_current={}
spare_to_current=[]
#新表与当前状态表相比,得到此次发生变化的数据,存入store
key_new=list(new_dic.keys())
value_new=list(new_dic.values())
key_current=list(current_dic.keys())
value_current=list(current_dic.values())
#对于新表中的每一个元素,判断是否在状态表中
l=len(new_dic)
for number in range(l):
try:
#如果这个值在当前状态表中
index=key_current.index(key_new[number])
#如果新值与之前的值不同,则更新存储文件
if value_new[number]!=value_current[index]:
new_store[key_new[number]]=value_new[number]
spare_to_store.append(new_spare[number])
#如果值不在状态表中,直接添加
except:
new_store[key_new[number]]=value_new[number]
spare_to_store.append(new_spare[number])
finally:
#无论值是否在原状态表中,在表中是否相同,新状态表都写入新值
new_current[key_new[number]]=value_new[number]
spare_to_current.append(new_spare[number])
#对于原所有数据,未保存的记为-1,表示此次没有结果
l=len(key_current)
for i in range(l):
if key_current[i] not in new_current.keys():
not_in_value=(-1)
new_current[key_current[i]]=not_in_value
spare_to_current.append(("-1"))
new_store[key_current[i]]=not_in_value
spare_to_store.append(("-1","-1"))
return (new_store,new_current,spare_to_store,spare_to_current)
三、后记
新思路补充:
仅使用字典进行比较,键值对中值的字段“有所变化但没什么价值的项”比如时间戳等可以不比较,而不是像上面一样使用列表进行追加同步、那样会引入O(n)的时间复杂度。
仅仅使用字典比较是O(1)复杂度,会比上面用列表同步快得多。
PS:如果字典较大就分成多个字典多个表,以期避免hash冲突造成的时间复杂度变化。
代码需要读者自行实现,有问题可留言~