更新
This commit is contained in:
@@ -1,89 +1,89 @@
|
||||
"""修复 wechat_bot.py 的字节级损坏。
|
||||
|
||||
损坏形态:个别字节被就地替换成 0x3f('?'),字节长度不变,行结构完好。
|
||||
因此可以用 git HEAD 版本做参照:把损坏行里的 0x3f 当通配符,在 HEAD 里找
|
||||
长度相同、其余字节全部一致的唯一候选行来还原。
|
||||
|
||||
默认只报告不写入;加 --write 才真正落盘。
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def load_head() -> bytes:
|
||||
return subprocess.run(
|
||||
["git", "show", "HEAD:wechat_rpa/wechat_bot.py"],
|
||||
capture_output=True,
|
||||
check=True,
|
||||
cwd="..",
|
||||
).stdout
|
||||
|
||||
|
||||
def is_valid(line: bytes) -> bool:
|
||||
try:
|
||||
line.decode("utf-8")
|
||||
return True
|
||||
except UnicodeDecodeError:
|
||||
return False
|
||||
|
||||
|
||||
def candidates(broken: bytes, pool: dict) -> list:
|
||||
"""在同长度的候选里找出「除 0x3f 位置外完全一致」的行。"""
|
||||
found = []
|
||||
for other in pool.get(len(broken), ()): # 长度相同才可能是同一行
|
||||
if all(
|
||||
b == o or b == 0x3F
|
||||
for b, o in zip(broken, other)
|
||||
):
|
||||
found.append(other)
|
||||
return found
|
||||
|
||||
|
||||
def main():
|
||||
write = "--write" in sys.argv
|
||||
current = open("wechat_bot.py", "rb").read()
|
||||
head = load_head()
|
||||
|
||||
cur_lines = current.split(b"\n")
|
||||
head_lines = head.split(b"\n")
|
||||
|
||||
pool = {}
|
||||
for line in head_lines:
|
||||
pool.setdefault(len(line), []).append(line)
|
||||
|
||||
broken_idx = [i for i, line in enumerate(cur_lines) if not is_valid(line)]
|
||||
print(f"总行数 {len(cur_lines)},损坏行 {len(broken_idx)}")
|
||||
|
||||
repaired = list(cur_lines)
|
||||
fixed = unresolved = ambiguous = 0
|
||||
unresolved_lines = []
|
||||
for i in broken_idx:
|
||||
found = set(candidates(cur_lines[i], pool))
|
||||
if len(found) == 1:
|
||||
repaired[i] = found.pop()
|
||||
fixed += 1
|
||||
elif len(found) > 1:
|
||||
ambiguous += 1
|
||||
unresolved_lines.append((i, cur_lines[i], len(found)))
|
||||
else:
|
||||
unresolved += 1
|
||||
unresolved_lines.append((i, cur_lines[i], 0))
|
||||
|
||||
print(f"可唯一还原 {fixed},歧义 {ambiguous},HEAD 里找不到 {unresolved}")
|
||||
if unresolved_lines:
|
||||
print("\n需要人工确认的行(最多列 40 条):")
|
||||
for i, line, n in unresolved_lines[:40]:
|
||||
print(f" 第 {i + 1} 行 候选={n}: {line.decode('utf-8', 'replace')!r}")
|
||||
|
||||
if not write:
|
||||
print("\n(只报告,未写入。加 --write 才落盘)")
|
||||
return
|
||||
|
||||
out = b"\n".join(repaired)
|
||||
open("wechat_bot.py", "wb").write(out)
|
||||
print(f"\n已写回 {len(out)} 字节")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
"""修复 wechat_bot.py 的字节级损坏。
|
||||
|
||||
损坏形态:个别字节被就地替换成 0x3f('?'),字节长度不变,行结构完好。
|
||||
因此可以用 git HEAD 版本做参照:把损坏行里的 0x3f 当通配符,在 HEAD 里找
|
||||
长度相同、其余字节全部一致的唯一候选行来还原。
|
||||
|
||||
默认只报告不写入;加 --write 才真正落盘。
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def load_head() -> bytes:
|
||||
return subprocess.run(
|
||||
["git", "show", "HEAD:wechat_rpa/wechat_bot.py"],
|
||||
capture_output=True,
|
||||
check=True,
|
||||
cwd="..",
|
||||
).stdout
|
||||
|
||||
|
||||
def is_valid(line: bytes) -> bool:
|
||||
try:
|
||||
line.decode("utf-8")
|
||||
return True
|
||||
except UnicodeDecodeError:
|
||||
return False
|
||||
|
||||
|
||||
def candidates(broken: bytes, pool: dict) -> list:
|
||||
"""在同长度的候选里找出「除 0x3f 位置外完全一致」的行。"""
|
||||
found = []
|
||||
for other in pool.get(len(broken), ()): # 长度相同才可能是同一行
|
||||
if all(
|
||||
b == o or b == 0x3F
|
||||
for b, o in zip(broken, other)
|
||||
):
|
||||
found.append(other)
|
||||
return found
|
||||
|
||||
|
||||
def main():
|
||||
write = "--write" in sys.argv
|
||||
current = open("wechat_bot.py", "rb").read()
|
||||
head = load_head()
|
||||
|
||||
cur_lines = current.split(b"\n")
|
||||
head_lines = head.split(b"\n")
|
||||
|
||||
pool = {}
|
||||
for line in head_lines:
|
||||
pool.setdefault(len(line), []).append(line)
|
||||
|
||||
broken_idx = [i for i, line in enumerate(cur_lines) if not is_valid(line)]
|
||||
print(f"总行数 {len(cur_lines)},损坏行 {len(broken_idx)}")
|
||||
|
||||
repaired = list(cur_lines)
|
||||
fixed = unresolved = ambiguous = 0
|
||||
unresolved_lines = []
|
||||
for i in broken_idx:
|
||||
found = set(candidates(cur_lines[i], pool))
|
||||
if len(found) == 1:
|
||||
repaired[i] = found.pop()
|
||||
fixed += 1
|
||||
elif len(found) > 1:
|
||||
ambiguous += 1
|
||||
unresolved_lines.append((i, cur_lines[i], len(found)))
|
||||
else:
|
||||
unresolved += 1
|
||||
unresolved_lines.append((i, cur_lines[i], 0))
|
||||
|
||||
print(f"可唯一还原 {fixed},歧义 {ambiguous},HEAD 里找不到 {unresolved}")
|
||||
if unresolved_lines:
|
||||
print("\n需要人工确认的行(最多列 40 条):")
|
||||
for i, line, n in unresolved_lines[:40]:
|
||||
print(f" 第 {i + 1} 行 候选={n}: {line.decode('utf-8', 'replace')!r}")
|
||||
|
||||
if not write:
|
||||
print("\n(只报告,未写入。加 --write 才落盘)")
|
||||
return
|
||||
|
||||
out = b"\n".join(repaired)
|
||||
open("wechat_bot.py", "wb").write(out)
|
||||
print(f"\n已写回 {len(out)} 字节")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user