python3实现金融文件比对(实现FFReader基本功能的基础上,增加比对功能)

本文介绍了一种使用Python3实现金融文件比对的方法,基于FFReader的config文件,对比两文件的基本信息并按强控、普通、可忽略条件分组。通过HTML界面展示比对结果,展示不同情况的例子,并指出后续改进计划,包括Java版实现和增强功能。
摘要由CSDN通过智能技术生成

python3实现金融文件比对(实现简化版FFReader的基础上,增加比对功能)

需求

  1. 使用FFReader的config文件,解析传入文件的内容(包括Dictionary和TipDictionary内容)。
  2. 对比传入两文件除路径外的基本信息,基本信息包括[‘文件路径’, ‘发送方代码’, ‘发送方信息’, ‘接收方代码’, ‘接收方信息’, ‘使用的配置’, ‘文件类型’, ‘文件描述’, ‘文件传递日期’, ‘解析器配置’],不是全部文件都包含上述所有的基本信息。
  3. 将文件内容的列分组,分为[‘强控条件’, ‘普通条件’, ‘可忽略条件’],将传入的两个文件的数据行进行匹对:强控条件若不同,一票否决;强控条件系统,普通条件不同最少的匹配为一对;当其他条件完全相同时,再考虑可忽略条件。
  4. 提供类似Beyond Compare的界面显示比对结果(我对前端不感兴趣,这里使用HTML显示)。

代码

# coding:utf8

import os
import re
import time
import webbrowser

INI_PATH = r'F:\FFReader\config\\'  # ffreader的配置文件路径
OFD_CodeInfo = 'OFD_CodeInfo.ini'  # 发送方、接收方代码
BREAK_BASIC_CONDITION = ['文件类型']  # 文件基本信息强控项,若此项不同,不必继续
PRIORITY_CONDITION_LIST = []  # 优先匹配的行信息,只有这些列完全相同才能相等,可一票否决,慎设
IGNORE_CONDITION_LIST = ['序号', '*流水号*', '交易发生时间']  # 符合这些字段的列,出现不同,仅警告


class interface_difference:
    interface_info1 = interface_info2 = None
    basic_msg_diff = {
   }  # 存储不同之处{不同的基本信息名: [文件1的值, 文件2的值], ....}
    col_names_level = [[],[],[]]  # 分级好的所有列名的列表[[强控],[普通],[可忽略]]
    data_diff = {
   }  # 两个文件对应上的行的不同之处,{(id1行, id2行):[[强控列号(在col_names_level[0]中的列号)],[普通],[可忽略]], ...}
    data_no_match = [[], []]  # 两个文件不匹配的行,[[id1中无法匹配的行号], [id2中无法匹配的行号]]
    col_map = [[[], [], []], [[], [], []]]  # 两个文件的列与col_names_level的对应关系,方便查找值
    # __col_map数据结构:[[[文件1强控列1在原文件列号, ...], [文件1普通列1在原文件列号,...], []],[...]]
    __is_init_diff_data__ = False  # 懒加载


    def __init__(self, id1, id2):
        self.interface_info1, self.interface_info2 = id1, id2

    def __init_diff_data__(self):  # 核心匹配方法,懒加载调用
        if self.__is_init_diff_data__:
            return
        id1, id2 = self.interface_info1, self.interface_info2
        if id1.basic_msg_dict['文件路径'].split('\\')[-1] != id2.basic_msg_dict['文件路径'].split('\\')[-1]:  # 比较文件名
            # ret_str += "文件名不同,文件1的文件名:" + id1.basic_msg_dict['文件路径'].split('\\')[-1] + ',文件2的文件名:' \
            #            + id1.basic_msg_dict['文件路径'].split('\\')[-1] + '\n'
            self.basic_msg_diff['文件路径'] = [id1.basic_msg_dict['文件路径'].split('\\')[-1],
                                           id1.basic_msg_dict['文件路径'].split('\\')[-1]]
        # 比较除路径外的基本信息
        for k in list(id1.basic_msg_dict.keys())[1:]:
            if id1.basic_msg_dict[k] != id2.basic_msg_dict[k]:
                self.basic_msg_diff[k] = [id1.basic_msg_dict[k], id2.basic_msg_dict[k]]
                if k in BREAK_BASIC_CONDITION:
                    self.data_no_match = [[l for l in range(len(id1.data_dict))], [l for l in range(len(id1.data_dict))]]
                    return

        col_names = set(id1.col_names)
        col_names.update(id2.col_names)  # 集合所有列名
        col_names = list(col_names)  # list方便调用
        # fixme 这里应该自己实现算法生成col_names,否则顺序会打乱
        col_names_level = [[], [], []]  # col_names_dict实现列的分级比对
        self.col_names_level = col_names_level
        id1_data, id2_data = [[[] for i in range(3)] for j in range(len(id1.data_dict))], \
                             [[[] for i in range(3)] for j in range(len(id2.data_dict))]  # 数据信息分级存储数据结构如下:
        # [[[1行1级列1, 1行1级列2, ...], [1行2级列1, ...], [...]], [[2行1级列1,...],[...],[...]], ...]

        def append_id_date(n, idn, id_data, col_map):  # 数据分级
            cns = idn.col_names
            if c in cns:  # 有c对应列,将每行对应值加入该行数据对应级别的list
                col_index = cns.index(c)
                col_map[n-1][c_flag].append(col_index)  # 尾追入列号
                for i_d_i in range(len(idn.data_dict)):
                    id_data[i_d_i][c_flag].append(idn.data_dict[i_d_i][col_index])
            else:  # 文件无此列,数据置None
                col_map[n - 1][c_flag].append(None)  # 否则会错位
                for i_d_i in range(len(idn.data_dict)):
                    id_data[i_d_i][c_flag].append(None)

        for c in col_names:  # 把信息分类排序,分为PRIORITY_CONDITION_LIST、IGNORE_CONDITION_LIST、其他
            c_flag = 0 if is_in_condition(PRIORITY_CONDITION_LIST, c) else \
                (2 if is_in_condition(IGNORE_CONDITION_LIST, c) else 1)
            col_names_level[c_flag].append(c)  # 列名分级,将c放到col_names对应列表中去
            append_id_date(1, id1, id1_data, self.col_map)
            append_id_date(2, id2, id2_data, self.col_map)
        matching_col_matrix = [[[[] for k in range(3)] for j in range(len(id2_data))] for i in
                               range(len(id1_data))]  # 列匹配矩阵,将两两不匹配的列名全部保存,
        # 数据结构:[[[[id1第1行与id2第1行所有强控列不同的列号列表],[id1第1行与id2第2行所有强控列不同的列号],[id1第1行与id2第2行所有忽略列不同的列号]],[...],...],[...],...]

        # 两个文件的行匹配算法,核心算法
        for d_i in range(len(id1_data)):  # 生成匹配度矩阵
            for d_j in range(len(id2_data)):
                for l in range(3):  # 分级
                    for c_i in range(len(col_names_level[l])):  # 每列
                        if id1_data[d_i][l][c_i] != id2_data[d_j][l][c_i]:
                            matching_col_matrix[d_i][d_j][l].append(c_i)  # 将有不同的列序号(col_names_level[l]的序号)加进列表

        # 处理匹配矩阵
        match_degree = []  # 匹配度,[[(普通列不同数, 可忽略列不同),id1行号,id2行号], ...],获取后对其进行排序
        # 计算出所有匹配值,顺位匹配,若匹配值相同以id1顺序先到先得
        for i in range(len(id1_data)):
            for j in range(len(id2_data)):
                if matching_col_matrix[i][j][0]:  # 强控条件,一旦不同,两行不再考虑匹配
                    pass
                elif matching_col_matrix[i][j] is not None:  # 应该都是非空,保险起见
                    match_degree.append([(len(matching_col_matrix[i][j][1]), len(matching_col_matrix[i][j][2])), i, j])
        match_degree.sort()  # 升序排序
        match_results = [[], []]  # 匹配结果,储存两两匹配的行数,第一个列表为id1行号,第二个列表对应位置为id2行号,使用list为了提高查找效率
        not_match_lines = [[i for i in range(len(id1_data))], [i for i in range(len(id2_data))]]  # 未匹对的行,先纳入全部,再删
        for i in match_degree:
            if i[1] in match_results[0
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值