fix_mojibake.py
· 5.1 KiB · Python
Originalformat
#!/usr/bin/env python3
import sys
import os
import argparse
from pathlib import Path
"""
Сделано для Димы Андреева, конвертор индексов HTTrack.
Файл на самом деле уже в UTF-8, но содержит испорченные символы (mojibake).
Это типичная ситуация, текст был прочитан как ISO-8859-1, но сохранен в UTF-8 "как есть" без декодирования
"""
def fix_mojibake_utf8(input_path, output_path=None):
"""
Исправляет текст, который был сохранён как UTF-8, но прочитан как ISO-8859-1
"""
try:
# Читаем файл как бинарный
with open(input_path, 'rb') as f:
raw_bytes = f.read()
# Исправляем mojibake
fixed_text = raw_bytes.decode('utf-8').encode('latin-1').decode('utf-8')
return fixed_text
except UnicodeDecodeError:
# Если не получается исправить как mojibake, пробуем прочитать как обычный UTF-8
try:
with open(input_path, 'r', encoding='utf-8') as f:
return f.read()
except:
with open(input_path, 'r', encoding='latin-1') as f:
return f.read()
except Exception as e:
print(f"✗ Ошибка при обработке {input_path}: {e}")
return None
def process_file(input_file, output_file):
"""Обрабатывает один файл"""
fixed_text = fix_mojibake_utf8(input_file)
if fixed_text is None:
return False
# Записываем исправленный текст
with open(output_file, 'w', encoding='utf-8') as f:
f.write(fixed_text)
return True
def process_directory(input_dir, prefix="_"):
"""Обрабатывает все HTML файлы в каталоге"""
input_dir = Path(input_dir)
if not input_dir.exists():
print(f"✗ Каталог не найден: {input_dir}")
return False
if not input_dir.is_dir():
print(f"✗ Это не каталог: {input_dir}")
return False
# Ищем все HTML файлы
html_files = list(input_dir.glob("*.html")) + list(input_dir.glob("*.htm"))
if not html_files:
print(f"✗ В каталоге {input_dir} не найдено HTML файлов")
return False
print(f"Найдено {len(html_files)} HTML файлов для обработки")
print("-" * 50)
processed = 0
for input_file in html_files:
# Создаем имя выходного файла с префиксом
output_file = input_file.with_name(f"{prefix}{input_file.name}")
print(f"Обработка: {input_file.name} → {output_file.name}")
if process_file(input_file, output_file):
processed += 1
else:
print(f" ✗ Ошибка при обработке {input_file.name}")
print("-" * 50)
print(f"✓ Обработано файлов: {processed}/{len(html_files)}")
return True
def main():
parser = argparse.ArgumentParser(
description='Исправляет mojibake (UTF-8, прочитанный как Latin-1) в файлах'
)
parser.add_argument('input', help='Входной файл или каталог')
parser.add_argument('output', nargs='?', help='Выходной файл (только для обработки одного файла)')
parser.add_argument('--prefix', default='_', help='Префикс для обработанных файлов (по умолчанию: _)')
args = parser.parse_args()
input_path = Path(args.input)
# Проверяем, является ли вход каталогом
if input_path.is_dir():
print(f"📁 Обработка каталога: {input_path}")
process_directory(input_path, args.prefix)
else:
# Обработка одного файла
if not input_path.exists():
print(f"✗ Файл не найден: {input_path}")
sys.exit(1)
if not args.output:
print("✗ Для обработки одного файла нужно указать выходной файл")
print("Использование: python script.py input.html output.html")
sys.exit(1)
print(f"📄 Обработка файла: {input_path}")
if process_file(input_path, args.output):
print(f"✓ Файл сохранен: {args.output}")
# Показываем превью
with open(args.output, 'r', encoding='utf-8') as f:
content = f.read(500)
print("\nПревью (первые 500 символов):")
print("-" * 50)
print(content)
print("-" * 50)
else:
print(f"✗ Не удалось обработать файл")
if __name__ == "__main__":
main()
| 1 | #!/usr/bin/env python3 |
| 2 | import sys |
| 3 | import os |
| 4 | import argparse |
| 5 | from pathlib import Path |
| 6 | """ |
| 7 | Сделано для Димы Андреева, конвертор индексов HTTrack. |
| 8 | |
| 9 | Файл на самом деле уже в UTF-8, но содержит испорченные символы (mojibake). |
| 10 | Это типичная ситуация, текст был прочитан как ISO-8859-1, но сохранен в UTF-8 "как есть" без декодирования |
| 11 | """ |
| 12 | |
| 13 | def fix_mojibake_utf8(input_path, output_path=None): |
| 14 | """ |
| 15 | Исправляет текст, который был сохранён как UTF-8, но прочитан как ISO-8859-1 |
| 16 | """ |
| 17 | try: |
| 18 | # Читаем файл как бинарный |
| 19 | with open(input_path, 'rb') as f: |
| 20 | raw_bytes = f.read() |
| 21 | |
| 22 | # Исправляем mojibake |
| 23 | fixed_text = raw_bytes.decode('utf-8').encode('latin-1').decode('utf-8') |
| 24 | |
| 25 | return fixed_text |
| 26 | |
| 27 | except UnicodeDecodeError: |
| 28 | # Если не получается исправить как mojibake, пробуем прочитать как обычный UTF-8 |
| 29 | try: |
| 30 | with open(input_path, 'r', encoding='utf-8') as f: |
| 31 | return f.read() |
| 32 | except: |
| 33 | with open(input_path, 'r', encoding='latin-1') as f: |
| 34 | return f.read() |
| 35 | except Exception as e: |
| 36 | print(f"✗ Ошибка при обработке {input_path}: {e}") |
| 37 | return None |
| 38 | |
| 39 | def process_file(input_file, output_file): |
| 40 | """Обрабатывает один файл""" |
| 41 | fixed_text = fix_mojibake_utf8(input_file) |
| 42 | if fixed_text is None: |
| 43 | return False |
| 44 | |
| 45 | # Записываем исправленный текст |
| 46 | with open(output_file, 'w', encoding='utf-8') as f: |
| 47 | f.write(fixed_text) |
| 48 | |
| 49 | return True |
| 50 | |
| 51 | def process_directory(input_dir, prefix="_"): |
| 52 | """Обрабатывает все HTML файлы в каталоге""" |
| 53 | input_dir = Path(input_dir) |
| 54 | |
| 55 | if not input_dir.exists(): |
| 56 | print(f"✗ Каталог не найден: {input_dir}") |
| 57 | return False |
| 58 | |
| 59 | if not input_dir.is_dir(): |
| 60 | print(f"✗ Это не каталог: {input_dir}") |
| 61 | return False |
| 62 | |
| 63 | # Ищем все HTML файлы |
| 64 | html_files = list(input_dir.glob("*.html")) + list(input_dir.glob("*.htm")) |
| 65 | |
| 66 | if not html_files: |
| 67 | print(f"✗ В каталоге {input_dir} не найдено HTML файлов") |
| 68 | return False |
| 69 | |
| 70 | print(f"Найдено {len(html_files)} HTML файлов для обработки") |
| 71 | print("-" * 50) |
| 72 | |
| 73 | processed = 0 |
| 74 | for input_file in html_files: |
| 75 | # Создаем имя выходного файла с префиксом |
| 76 | output_file = input_file.with_name(f"{prefix}{input_file.name}") |
| 77 | |
| 78 | print(f"Обработка: {input_file.name} → {output_file.name}") |
| 79 | |
| 80 | if process_file(input_file, output_file): |
| 81 | processed += 1 |
| 82 | else: |
| 83 | print(f" ✗ Ошибка при обработке {input_file.name}") |
| 84 | |
| 85 | print("-" * 50) |
| 86 | print(f"✓ Обработано файлов: {processed}/{len(html_files)}") |
| 87 | return True |
| 88 | |
| 89 | def main(): |
| 90 | parser = argparse.ArgumentParser( |
| 91 | description='Исправляет mojibake (UTF-8, прочитанный как Latin-1) в файлах' |
| 92 | ) |
| 93 | parser.add_argument('input', help='Входной файл или каталог') |
| 94 | parser.add_argument('output', nargs='?', help='Выходной файл (только для обработки одного файла)') |
| 95 | parser.add_argument('--prefix', default='_', help='Префикс для обработанных файлов (по умолчанию: _)') |
| 96 | |
| 97 | args = parser.parse_args() |
| 98 | |
| 99 | input_path = Path(args.input) |
| 100 | |
| 101 | # Проверяем, является ли вход каталогом |
| 102 | if input_path.is_dir(): |
| 103 | print(f"📁 Обработка каталога: {input_path}") |
| 104 | process_directory(input_path, args.prefix) |
| 105 | else: |
| 106 | # Обработка одного файла |
| 107 | if not input_path.exists(): |
| 108 | print(f"✗ Файл не найден: {input_path}") |
| 109 | sys.exit(1) |
| 110 | |
| 111 | if not args.output: |
| 112 | print("✗ Для обработки одного файла нужно указать выходной файл") |
| 113 | print("Использование: python script.py input.html output.html") |
| 114 | sys.exit(1) |
| 115 | |
| 116 | print(f"📄 Обработка файла: {input_path}") |
| 117 | if process_file(input_path, args.output): |
| 118 | print(f"✓ Файл сохранен: {args.output}") |
| 119 | |
| 120 | # Показываем превью |
| 121 | with open(args.output, 'r', encoding='utf-8') as f: |
| 122 | content = f.read(500) |
| 123 | print("\nПревью (первые 500 символов):") |
| 124 | print("-" * 50) |
| 125 | print(content) |
| 126 | print("-" * 50) |
| 127 | else: |
| 128 | print(f"✗ Не удалось обработать файл") |
| 129 | |
| 130 | if __name__ == "__main__": |
| 131 | main() |