from argparse import ArgumentParser from itertools import zip_longest from pathlib import Path CHUNK_SIZE = 16 def hl(b: str): return f"\033[31;47;1m{b}\033[0m" if __name__ == "__main__": parser = ArgumentParser() parser.add_argument("file1", type=Path, help="First file.") parser.add_argument("file2", type=Path, help="Second file.") args = parser.parse_args() # Read files with open(args.file1, "rb") as fd1, open(args.file2, "rb") as fd2: file1 = fd1.read() file2 = fd2.read() # Find differences diff = [b1 == b2 for b1, b2 in zip_longest(file1, file2, fillvalue=-1)] n1 = len(file1) n2 = len(file2) values1 = [f"{b:02X}" for b in file1] values2 = [f"{b:02X}" for b in file2] # Highlight different values values1 = [b if diff[i] else hl(b) for i, b in enumerate(values1)] values2 = [b if diff[i] else hl(b) for i, b in enumerate(values2)] # Pad to same length values1 = values1 + [" "] * (max(n1, n2) - n1) values2 = values2 + [" "] * (max(n1, n2) - n2) # Print bytes in chunks print() print(args.file1.name, " " * ((3 * CHUNK_SIZE - 1) + 2 - len(args.file1.name)), args.file2.name) print("-" * (3 * CHUNK_SIZE - 1), " ", "-" * (3 * CHUNK_SIZE - 1)) for chunk in [slice(i, i + CHUNK_SIZE) for i in range(0, max(n1, n2), CHUNK_SIZE)]: print(" ".join(values1[chunk]), " ", " ".join(values2[chunk])) print() print(f"Files differ at {diff.count(False)} places.")