python完全拷贝源EXCEL的内容和格式到目标EXCEL的尾部

import openpyxl as op
import copy
import re
from collections import OrderedDict
from openpyxl.utils import get_column_letter, column_index_from_string
from openpyxl.utils.cell import coordinate_from_string

def fullcopy_sheet_s2t(s_wb, t_wb, s_ws=None, t_ws=None):
    """完全拷贝源EXCEL的内容和格式到目标EXCEL的尾部

    Args:
        s_wb (_type_): 源工作簿
        t_wb (_type_): 目标工作簿
        s_ws (_type_, optional): 源工作表. Defaults to None.
        t_ws (_type_, optional): 目标工作表. Defaults to None.
    """

    def get_effective_data_range(wb, sheet_name=None):
        """获取工作簿(第一个或指定)工作表的数据范围

        Args:
            wb (_type_): 工作表
            sheet_name (_type_, optional): 工作表名. Defaults to None.

        Returns:
            _type_: 开始CELL坐标,结束CELL坐标,有效数据行数
        """
        # 获取指定工作表
        if sheet_name is None:
            ws = wb[wb.sheetnames[0]]
        else:
            ws = wb[sheet_name]

        cells = [cell for row in ws.rows for cell in row]
        # print("🚀 ~ file: copyxlsx240517.py:199 ~ _list:", cells)
        if cells:
            start = cells[0]
            # print("🚀 ~ file: copyxlsx240517.py:201 ~ cells[0]:", cells[0])
            end = cells[-1]
            # print("🚀 ~ file: copyxlsx240517.py:203 ~ cells[-1]:", cells[-1])
            start_coordinate = start.coordinate
            end_coordinate = end.coordinate
            # 计算有效数据行数(不包括空行)
            data_row_count = end.row - start.row + 1
        else:
            # 空表格
            start_coordinate = None
            end_coordinate = None
            data_row_count = 0

        # 返回结果
        return (start_coordinate, end_coordinate, data_row_count)

    def get_newarea(start_coordinate, end_coordinate, current_row, newrows):
        """根据原始坐标, 开始行, 新增行数,生成新增数据的目标区域

        Args:
            start_coordinate (_type_): 开始坐标
            end_coordinate (_type_): 结束坐标
            current_row (_type_): 开始行
            newrows (_type_): 新增行数

        Returns:
            _type_: _description_
        """
        start_col, start_row = coordinate_from_string(start_coordinate)
        end_col, end_row = coordinate_from_string(end_coordinate)
        new_start_row = start_row + current_row
        new_end_row = end_row + current_row
        new_start_col = start_col
        new_end_col = end_col
        # print(new_start_col + str(new_start_row), new_end_col + str(new_end_row))
        return new_start_col + str(new_start_row), new_end_col + str(new_end_row)

    if s_ws is None:
        s_ws = s_wb.sheetnames[0]
    if t_ws is None:
        t_ws = s_ws

    ws_from = s_wb[s_ws]
    if t_ws not in t_wb.sheetnames:
        ws_to = t_wb.create_sheet(t_ws)
    else:
        ws_to = t_wb[t_ws]

    # 删除默认创建的空sheet
    if s_ws != "Sheet" and "Sheet" in t_wb.sheetnames:
        del t_wb["Sheet"]

    _, _, lastrow = get_effective_data_range(t_wb)

    start_coordinate, end_coordinate, data_row_count = get_effective_data_range(s_wb)

    s_range = f"{start_coordinate}:{end_coordinate}"

    targe_start_coordinate, targe_end_coordinate = get_newarea(
        start_coordinate, end_coordinate, lastrow, data_row_count
    )

    t_range = f"{targe_start_coordinate}:{targe_end_coordinate}"

    ws_to.sheet_properties.tabColor = ws_from.sheet_properties.tabColor
    if s_range is not None:
        source_area = ws_from[s_range]
    else:
        source_area = ws_from

    merge_cell_dict = OrderedDict()
    merged_ranges = ws_from.merged_cells.ranges
    for source_row in source_area:
        for source_cell in source_row:
            sc_str = str(source_cell)
            point_time = sc_str.count(".")
            sc_str = sc_str.replace(".", "", point_time - 1)
            start = sc_str.find(".")
            sc_str = sc_str[start + 1 : -1]
            for merged_range in merged_ranges:
                if source_cell.coordinate in merged_range:
                    _cell_value = ws_from.cell(
                        row=merged_range.min_row, column=merged_range.min_col
                    )
                    merge_cell_dict[sc_str] = (
                        merged_range.min_row,
                        merged_range.min_col,
                        _cell_value,
                    )
                    continue
    range_li = []
    for val in set(merge_cell_dict.values()):
        tmp = []
        for x, y in merge_cell_dict.items():
            if y == val:
                tmp.append(x)
        if len(tmp):
            range_li.append(min(tmp) + ":" + max(tmp))

    for i in range_li:
        # print(i)
        if s_range is not None:
            base_point_letter = s_range.split(":")[0]
            base_point = ws_from[base_point_letter]
            base_row = base_point.row
            base_col = base_point.column
        else:
            base_point_letter = i.split(":")[0]
            base_point = ws_from[base_point_letter]
            base_row = base_point.row
            base_col = base_point.column
        s = i.split(":")[0]
        e = i.split(":")[1]
        # 模板区间第一个点相对顶点距离
        base_delta_row = ws_from[s].row - base_row
        base_delta_col = ws_from[s].column - base_col
        # 模板区间两个端点距离
        delta_row = ws_from[e].row - ws_from[s].row
        delta_col = ws_from[e].column - ws_from[s].column
        # print(base_delta_row, base_delta_col, delta_row, delta_col)
        if t_range is not None:
            tar_s = t_range.split(":")[0]
            tar_s_letter = re.findall(r"([A-Za-z]+)", tar_s)[0]
            tar_base_col_idx = column_index_from_string(tar_s_letter)
            tar_base_row_idx = int(re.findall(r"(\d+)", tar_s)[0])
        else:
            tar_s = s
            tar_s_letter = re.findall(r"([A-Za-z]+)", tar_s)[0]
            tar_base_col_idx = column_index_from_string(tar_s_letter)
            tar_base_row_idx = int(re.findall(r"(\d+)", tar_s)[0])
        # print(tar_base_row_idx, tar_base_col_idx)
        tar_range_s_col = get_column_letter(tar_base_col_idx + base_delta_col)
        tar_range_s_idx = tar_base_row_idx + base_delta_row
        tar_range_e_col = get_column_letter(
            tar_base_col_idx + base_delta_col + delta_col
        )
        tar_range_e_idx = tar_base_row_idx + base_delta_row + delta_row
        tar_merge = (
            tar_range_s_col
            + str(tar_range_s_idx)
            + ":"
            + tar_range_e_col
            + str(tar_range_e_idx)
        )
        # print('tar merge:', tar_merge)
        ws_to.merge_cells(tar_merge)

    if s_range is not None and t_range is not None:
        source_point_letter = s_range.split(":")[0]
        source_point = ws_from[source_point_letter]
        source_row = source_point.row
        source_col = source_point.column

        tar_point_letter = t_range.split(":")[0]
        tar_point = ws_from[tar_point_letter]
        tar_row = tar_point.row
        tar_col = tar_point.column

        delta_row = tar_row - source_row
        delta_col = tar_col - source_col
        print("ROW:", tar_row, source_row)
        print("COL:", tar_col, source_col)
    else:
        delta_row = 0
        delta_col = 0
    # print("DELTA ROW COL:", delta_row, delta_col)

    for source_row in source_area:
        update_row_h = False
        for source_cell in source_row:
            source_x = source_cell.row
            new_x = source_x + delta_row
            source_y = source_cell.column
            new_y = source_y + delta_col

            if not update_row_h:
                ws_to.row_dimensions[new_x].height = ws_from.row_dimensions[
                    source_x
                ].height
                update_row_h = True

            ws_to.column_dimensions[
                get_column_letter(new_y)
            ].width = ws_from.column_dimensions[get_column_letter(source_y)].width

            ws_to.cell(row=new_x, column=new_y, value=source_cell.value)

            # 设置单元格格式
            target_cell = ws_to.cell(new_x, new_y)
            target_cell.fill = copy.copy(source_cell.fill)
            if source_cell.has_style:
                target_cell._style = copy.copy(source_cell._style)
                target_cell.font = copy.copy(source_cell.font)
                target_cell.border = copy.copy(source_cell.border)
                target_cell.fill = copy.copy(source_cell.fill)
                target_cell.number_format = copy.copy(source_cell.number_format)
                target_cell.protection = copy.copy(source_cell.protection)
                target_cell.alignment = copy.copy(source_cell.alignment)

    s_wb.close()
    t_wb.close()

targe_wb = op.Workbook()  # 新建一个EXCEL
source_wb1 = op.load_workbook("head.xlsx")
source_wb2 = op.load_workbook("data.xlsx")
source_wb3 = op.load_workbook("tail.xlsx")

fullcopy_sheet_s2t(source_wb1, targe_wb)
fullcopy_sheet_s2t(source_wb2, targe_wb)
fullcopy_sheet_s2t(source_wb3, targe_wb)

targe_wb.save(filename="all_V2.xlsx")

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

数据服务生

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

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

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

打赏作者

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

抵扣说明:

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

余额充值