fix_encoding.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. import os
  2. import re
  3. # Windows-1252 mappings for bytes 0x80-0x9F
  4. # These bytes appeared in the original UTF-8 stream but were interpreted as Windows-1252
  5. WIN1252_MAP = {
  6. 0x80: '\u20ac', # Euro sign
  7. 0x82: '\u201a',
  8. 0x83: '\u0192',
  9. 0x84: '\u201e',
  10. 0x85: '\u2026',
  11. 0x86: '\u2020',
  12. 0x87: '\u2021',
  13. 0x88: '\u02c6',
  14. 0x89: '\u2030',
  15. 0x8a: '\u0160',
  16. 0x8b: '\u2039',
  17. 0x8c: '\u0152',
  18. 0x8e: '\u017d',
  19. 0x91: '\u2018',
  20. 0x92: '\u2019',
  21. 0x93: '\u201c',
  22. 0x94: '\u201d',
  23. 0x95: '\u2022',
  24. 0x96: '\u2013',
  25. 0x97: '\u2014',
  26. 0x98: '\u02dc',
  27. 0x99: '\u2122',
  28. 0x9a: '\u0161',
  29. 0x9b: '\u203a',
  30. 0x9c: '\u0153',
  31. 0x9e: '\u017e',
  32. 0x9f: '\u0178',
  33. }
  34. # Reverse map: Windows-1252 character -> original byte
  35. WIN1252_REVERSE = {v: k for k, v in WIN1252_MAP.items()}
  36. def garbled_to_bytes(text):
  37. """Convert garbled text back to original UTF-8 bytes.
  38. For each character in the garbled text:
  39. 1. Try to encode as GBK -> gives us original UTF-8 bytes (usually 2 bytes)
  40. 2. If it's a Windows-1252 character -> gives us the original single byte
  41. 3. Try Latin-1 for bytes 0xA0-0xFF
  42. 4. Otherwise, skip (unrecoverable)
  43. """
  44. result = bytearray()
  45. for c in text:
  46. # Try GBK encoding first (most common - 2 bytes)
  47. try:
  48. gbk_bytes = c.encode('gbk')
  49. result.extend(gbk_bytes)
  50. continue
  51. except (UnicodeEncodeError, LookupError):
  52. pass
  53. # Check if it's a Windows-1252 character (1 byte)
  54. if c in WIN1252_REVERSE:
  55. result.append(WIN1252_REVERSE[c])
  56. continue
  57. # Try Latin-1 for bytes 0xA0-0xFF
  58. try:
  59. latin1_bytes = c.encode('latin-1')
  60. if len(latin1_bytes) == 1 and latin1_bytes[0] >= 0x80:
  61. result.extend(latin1_bytes)
  62. continue
  63. except (UnicodeEncodeError, LookupError):
  64. pass
  65. # Unrecoverable character - use placeholder
  66. result.append(ord('?'))
  67. return bytes(result)
  68. def fix_file(filepath):
  69. """Fix garbled Chinese in a single file."""
  70. with open(filepath, 'rb') as f:
  71. raw = f.read()
  72. # Detect and skip BOM
  73. bom = b''
  74. if raw.startswith(b'\xef\xbb\xbf'):
  75. bom = b'\xef\xbb\xbf'
  76. raw = raw[3:]
  77. # Decode as UTF-8
  78. text = raw.decode('utf-8')
  79. # Find all non-ASCII segments and try to fix them
  80. result_parts = []
  81. last_end = 0
  82. fixed_count = 0
  83. total_segments = 0
  84. for match in re.finditer(r'[^\x00-\x7F]+', text):
  85. garbled = match.group()
  86. start = match.start()
  87. end = match.end()
  88. total_segments += 1
  89. # Convert garbled text to original UTF-8 bytes
  90. original_bytes = garbled_to_bytes(garbled)
  91. # Try to decode as UTF-8
  92. try:
  93. fixed = original_bytes.decode('utf-8')
  94. result_parts.append(text[last_end:start])
  95. result_parts.append(fixed)
  96. last_end = end
  97. fixed_count += 1
  98. except UnicodeDecodeError:
  99. # If full decode fails, keep original
  100. pass
  101. result_parts.append(text[last_end:])
  102. new_text = ''.join(result_parts)
  103. if new_text != text:
  104. with open(filepath, 'wb') as f:
  105. f.write(bom + new_text.encode('utf-8'))
  106. return True, fixed_count, total_segments
  107. return False, 0, 0
  108. def main():
  109. base_dir = r'E:\workspace\dan\zxyj\zxyj-backend\src\main\java\com\zxyj'
  110. total_files = 0
  111. fixed_files = 0
  112. total_fixed = 0
  113. total_segments = 0
  114. for root, dirs, files in os.walk(base_dir):
  115. for fname in sorted(files):
  116. if not fname.endswith('.java'):
  117. continue
  118. fpath = os.path.join(root, fname)
  119. total_files += 1
  120. try:
  121. changed, fc, ts = fix_file(fpath)
  122. if changed:
  123. fixed_files += 1
  124. total_fixed += fc
  125. total_segments += ts
  126. rel = os.path.relpath(fpath, base_dir)
  127. print(f'Fixed: {rel} ({fc}/{ts} segments)')
  128. except Exception as e:
  129. print(f'Error: {os.path.relpath(fpath, base_dir)}: {e}')
  130. print(f'\nSummary: {fixed_files}/{total_files} files fixed, {total_fixed}/{total_segments} segments')
  131. if __name__ == '__main__':
  132. main()