ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

比较两文件差异python实现

比较两文件差异python实现 比较1比较2下面的是GUI方式俩栏颜色对比较适合前后有改动的同一文档import sysfrom difflib import SequenceMatcherfrom PySide6.QtCore import Qt, QRegularExpressionfrom PySide6.QtGui import (QFont, QColor, QSyntaxHighlighter, QTextCharFormat,QTextCursor, QTextDocument)from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,QHBoxLayout, QPlainTextEdit, QPushButton, QFileDialog,QLabel, QSplitter)# ---------- Diff 计算 ----------def compute_diff(a_lines, b_lines):返回 b_lines 的标注列表: (equal|add|del|change, line, a_idx)sm SequenceMatcher(None, a_lines, b_lines, autojunkFalse)result []for tag, i1, i2, j1, j2 in sm.get_opcodes():if tag equal:for k in range(j2 - j1):result.append((equal, b_lines[j1 k], i1 k))elif tag insert:for k in range(j1, j2):result.append((add, b_lines[k], None))elif tag delete:for k in range(i1, i2):result.append((del, a_lines[k], k))elif tag replace:for k in range(j1, j2):result.append((change, b_lines[k], None))for k in range(i1, i2):result.append((del, a_lines[k], k))return result# ---------- 高亮器 ----------COLORS {equal: QColor(#F5F5F5),add: QColor(#C8E6C9), # 浅绿del: QColor(#FFCDD2), # 浅红change: QColor(#FFF59D), # 浅黄}class DiffHighlighter(QSyntaxHighlighter):def __init__(self, doc, marks):super().__init__(doc)self.marks marks # list[bool] —— 长度等于行数def highlightBlock(self, text):block_num self.currentBlock().blockNumber()if block_num len(self.marks) and self.marks[block_num]:fmt QTextCharFormat()fmt.setBackground(QColor(#FFEB3B))fmt.setProperty(QTextCharFormat.Property.FullWidthSelection, True)self.setFormat(0, len(text), fmt)# ---------- 主窗口 ----------class DiffWindow(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle(Python Diff Viewer)self.resize(1200, 700)central QWidget(); self.setCentralWidget(central)v QVBoxLayout(central)# 顶部按钮栏bar QHBoxLayout()self.btn_left QPushButton( 打开左文件)self.btn_right QPushButton( 打开右文件)self.btn_diff QPushButton( 对比)for b in (self.btn_left, self.btn_right, self.btn_diff):bar.addWidget(b)bar.addStretch()v.addLayout(bar)# 左右文本框self.left QPlainTextEdit(); self.left.setFont(QFont(Consolas, 11))self.right QPlainTextEdit(); self.right.setFont(QFont(Consolas, 11))for ed in (self.left, self.right):ed.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap)splitter QSplitter(Qt.Orientation.Horizontal)splitter.addWidget(self._wrap(左文件 (原版本), self.left))splitter.addWidget(self._wrap(右文件 (新版本), self.right))splitter.setSizes([600, 600])v.addWidget(splitter)# 信号self.btn_left.clicked.connect(lambda: self._open(self.left))self.btn_right.clicked.connect(lambda: self._open(self.right))self.btn_diff.clicked.connect(self._run_diff)def _wrap(self, title, edit):w QWidget(); lay QVBoxLayout(w); lay.setContentsMargins(0,0,0,0)lay.addWidget(QLabel(title))lay.addWidget(edit)return wdef _open(self, editor):path, _ QFileDialog.getOpenFileName(self, 选择文件)if path:with open(path, r, encodingutf-8, errorsignore) as f:editor.setPlainText(f.read())def _run_diff(self):a self.left.toPlainText().splitlines()b self.right.toPlainText().splitlines()diff compute_diff(a, b)# --- 左文件按 a 的顺序仅显示 del/equal ---left_marks []left_lines []# 同步高亮把 b 的 add/change 也映射到 a 侧对应行a_highlight set() # 在 a 中需要高亮的行号与 b 的 add/change 对齐# 重新跑一遍以同步两侧位置sm SequenceMatcher(None, a, b, autojunkFalse)a_pos, b_pos 0, 0a_blocks, b_blocks [], []for tag, i1, i2, j1, j2 in sm.get_opcodes():if tag equal:for k in range(i2 - i1):a_blocks.append((equal, a[i1k], i1k))b_blocks.append((equal, b[j1k], j1k))elif tag insert:for k in range(j1, j2):a_blocks.append((pad, , i1))b_blocks.append((add, b[k], j1k))elif tag delete:for k in range(i1, i2):a_blocks.append((del, a[k], i1k))b_blocks.append((pad, , j1))elif tag replace:# 先用 a 行标 del再用 b 行标 add/changefor k in range(i1, i2):a_blocks.append((del, a[k], i1k))b_blocks.append((pad, , j1k))for k in range(j1, j2):a_blocks.append((pad, , i1k))b_blocks.append((change, b[k], j1k))# 渲染到文本框 上色self._render(self.left, a_blocks)self._render(self.right, b_blocks)def _render(self, editor, blocks):editor.setPlainText(\n.join(b[1] for b in blocks))cursor editor.textCursor()cursor.select(QTextCursor.SelectionType.Document)fmt_default QTextCharFormat(); fmt_default.setBackground(QColor(white))cursor.setCharFormat(fmt_default)cursor.clearSelection()for i, (tag, _, _) in enumerate(blocks):color COLORS.get(tag)if not color: continueblock editor.document().findBlockByNumber(i)hl QTextCursor(block)hl.movePosition(QTextCursor.MoveOperation.EndOfBlock,QTextCursor.MoveMode.KeepAnchor)fmt QTextCharFormat(); fmt.setBackground(color)hl.setCharFormat(fmt)editor.viewport().update()if __name__ __main__:app QApplication(sys.argv)w DiffWindow(); w.show()sys.exit(app.exec())但缺行号显示补充
返回列表