《A Technique for Isolating Differences Between Files》
一种计算两个文件差异的算法。
以行作为基础单元。
Two observations
- 每个文件中出现一次且仅出现一次的行必须是同一行(未更改但可能已移动)。
- 如果在与“找到”行对紧邻的每个文件中存在彼此相同的行,则这些行必须是同一行。可找到未更改行的序列。
名词解释
- O: old file
- N: new file
- OC: O 中有某一行出现多少次 (0/1/many)
- NC: N 中有某一行出现多少次 (0/1/many)
- OLNO: 某一行在 O 中的行号,仅 OC = 1 时有效
还涉及一个符号表和两个数组 OA 和 NA,文件中每一行对应符号表中一项,新旧文件中每一行分别对应数组 OA 和 NA 中的一个元素。符号表中的一项和数组中的元素结构如下:
OA 和 NA 中的一个元素要只包含符号表项和行号其中之一。
@dataclass
class HeckelSymbolTableEntry: # 符号表条目
value: Any
oc: int = 0
nc: int = 0
olno: int = 0
HeckelSymbolTableEntryType = Union[int, HeckelSymbolTableEntry] # OA/NA 元素类型
# 符号表
st: Dict[Any: HeckelSymbolTableEntryType] = dict()
算法过程
算法一共包含6个PASS
PASS 1
- 按行读取 N 到一个序列中
- 如果符号表中不存在该行对应的一项,则创建
- 递增符号表中的 NC
- 在 NA 中新增行 i 对应的一个元素,内容为该行对应的符号表项。
# PASS 1
# a -> N
for idx, i in enumerate(self.a):
ste = st.setdefault(i, HeckelSymbolTableEntry(i))
ste.nc += 1
na.append(ste)
PASS 2
与 PASS 1 相同,只不过是处理文件 O,同时记录 OLNO。
# PASS 2
# b -> O
for idx, i in enumerate(self.b):
ste = st.setdefault(i, HeckelSymbolTableEntry(i))
ste.oc += 1
oa.append(ste)
ste.olno = idx
PASS 3
基于 observation 1 处理 NC = OC = 1 的那些行因为每一行都是相同的、未修改的,因此我们将NA 和 OA 中的符号指针替换为另一个文件中的行数。
例如,如果 NA[i] 对应这样一行,我们在符号表中查找 NA[i] 并将 NA[i] 设置为 OLNO 并将 OA[OLNO] 设置为 i。
# PASS 3
for i in range(len(na)):
if na[i].nc == na[i].oc == 1:
olno = na[i].olno
na[i] = olno
oa[olno] = i
PASS 4
应用 observation 2 并按升序处理 NA 中的每一行:如果 NA[i] 指向 O 中 第 j 行,并且 NA[i+1] 和 OA[j+1] 包含相同的符号表条目指针,则 OA[j+1] 设置为 i+1,NA[i+1] 设置为 j+1。
# PASS 4
for i in range(len(na)):
try:
if isinstance(na[i], int):
j = na[i]
if isinstance(na[i + 1], HeckelSymbolTableEntry) and na[i + 1] == oa[j + 1]:
oa[j + 1] = i + 1
na[i + 1] = j + 1
except IndexError:
pass
PASS 5
应用 observation 2 并按降序处理每个条目:如果 NA[i] 指向 O 中 第 j 行, 并且 NA[i-1] 和 OA[j-1] 包含相同的符号表条目指针,则 NA[i- 1] 被 j-1 替换,OA[j-1] 被 i-1 替换。
# PASS 5
for i in reversed(range(1, len(na))):
try:
if isinstance(na[i], int):
j = na[i]
if isinstance(na[i - 1], HeckelSymbolTableEntry) and na[i - 1] == oa[j - 1] and i >= 1 and j >= 1:
oa[j - 1] = i - 1
na[i - 1] = j - 1
except IndexError:
pass
PASS 6
数组 NA 现在包含编码差异所需的信息:如果 NA[i] 指向符号表条目,我们假设行 i 是插入,我们可以将其标记为新文本。 如果它指向 OA[j],但 NA[i+1] 不指向 OA[j+1],则行 i 处于删除或块移动的边界,可以这样标记。
Refrences
- A technique for isolating differences between files dl.acm.org/doi/10.1145…
- github.com/m-matelski/…