🏠 Домашнее задание

⚡ Суть задания

Консолидирующее задание объединяет исключения и работу с файловой системой.

Нужно написать скрипт, который получает путь к директории через sys.argv, находит все файлы numbers.txt, суммирует числа в каждом файле и записывает результат в summary.txt, обрабатывая ошибки и логируя их.

📋 Формулировка задания

Задание: напишите программу process_numbers.py, которая:

  1. Принимает путь к директории через аргумент командной строки.
  2. Проверяет, что директория существует. Если нет — выводит сообщение и завершает работу.
  3. Рекурсивно ищет файлы с именем numbers.txt.
  4. Читает каждый файл и суммирует числа построчно. Пустые строки игнорируются.
  5. Обрабатывает ошибки: файл не найден, некорректное число, проблемы с кодировкой.
  6. Записывает результат в файл summary.txt в текущей директории.
  7. Логирует ошибки в файл processing.log.

Пример запуска:

python process_numbers.py /home/user/data

Пример файла numbers.txt:

10
20
abc
30

Пример результата в summary.txt:

/home/user/data/project1/numbers.txt: 60
/home/user/data/project2/numbers.txt: 15

🛠️ Пошаговое решение

1

Импортируйте нужные модули и настройте логирование

import logging
import os
import sys

logging.basicConfig(
    filename="processing.log",
    format="%(asctime)s - %(levelname)s - %(message)s",
    level=logging.ERROR,
)
2

Проверьте аргументы командной строки

if len(sys.argv) != 2:
    print("Использование: python process_numbers.py <директория>")
    sys.exit(1)

source_dir = sys.argv[1]

if not os.path.isdir(source_dir):
    print(f"Ошибка: директория '{source_dir}' не существует.")
    sys.exit(1)
3

Найдите все файлы numbers.txt

def find_numbers_files(directory: str) -> list[str]:
    """Рекурсивно ищет файлы с именем numbers.txt."""
    found: list[str] = []
    for root, dirs, files in os.walk(directory):
        if "numbers.txt" in files:
            found.append(os.path.join(root, "numbers.txt"))
    return found
4

Посчитайте сумму чисел в одном файле

from typing import Optional


def sum_numbers_in_file(path: str) -> Optional[float]:
    """Суммирует числа в файле. Возвращает None при ошибке."""
    total = 0.0
    try:
        with open(path, "r", encoding="utf-8") as file:
            for line_number, line in enumerate(file, start=1):
                line = line.strip()
                if not line:
                    continue
                try:
                    total += float(line)
                except ValueError:
                    logging.error(f"Некорректное число в {path}:{line_number} — '{line}'")
    except FileNotFoundError:
        logging.error(f"Файл не найден: {path}")
        return None
    except UnicodeDecodeError as e:
        logging.error(f"Ошибка кодировки в {path}: {e}")
        return None
    return total
⚠️ Проверить по документации: менеджер контекста with для открытия файлов не разбирается в исходной лекции, но является рекомендуемым способом автоматического закрытия файла.
5

Запишите результаты в summary.txt

def main() -> None:
    if len(sys.argv) != 2:
        print("Использование: python process_numbers.py <директория>")
        sys.exit(1)

    source_dir = sys.argv[1]

    if not os.path.isdir(source_dir):
        print(f"Ошибка: директория '{source_dir}' не существует.")
        sys.exit(1)

    files = find_numbers_files(source_dir)

    if not files:
        print("Файлы numbers.txt не найдены.")
        return

    with open("summary.txt", "w", encoding="utf-8") as output:
        for path in files:
            total = sum_numbers_in_file(path)
            if total is not None:
                output.write(f"{path}: {total}\n")
                print(f"{path}: {total}")
            else:
                output.write(f"{path}: ошибка обработки\n")


if __name__ == "__main__":
    main()

✅ Полное решение

# process_numbers.py
import logging
import os
import sys
from typing import Optional

logging.basicConfig(
    filename="processing.log",
    format="%(asctime)s - %(levelname)s - %(message)s",
    level=logging.ERROR,
)


def find_numbers_files(directory: str) -> list[str]:
    """Рекурсивно ищет файлы с именем numbers.txt."""
    found: list[str] = []
    for root, dirs, files in os.walk(directory):
        if "numbers.txt" in files:
            found.append(os.path.join(root, "numbers.txt"))
    return found


def sum_numbers_in_file(path: str) -> Optional[float]:
    """Суммирует числа в файле. Возвращает None при ошибке."""
    total = 0.0
    try:
        with open(path, "r", encoding="utf-8") as file:
            for line_number, line in enumerate(file, start=1):
                line = line.strip()
                if not line:
                    continue
                try:
                    total += float(line)
                except ValueError:
                    logging.error(
                        f"Некорректное число в {path}:{line_number} — '{line}'"
                    )
    except FileNotFoundError:
        logging.error(f"Файл не найден: {path}")
        return None
    except UnicodeDecodeError as e:
        logging.error(f"Ошибка кодировки в {path}: {e}")
        return None
    return total


def main() -> None:
    if len(sys.argv) != 2:
        print("Использование: python process_numbers.py <директория>")
        sys.exit(1)

    source_dir = sys.argv[1]

    if not os.path.isdir(source_dir):
        print(f"Ошибка: директория '{source_dir}' не существует.")
        sys.exit(1)

    files = find_numbers_files(source_dir)

    if not files:
        print("Файлы numbers.txt не найдены.")
        return

    with open("summary.txt", "w", encoding="utf-8") as output:
        for path in files:
            total = sum_numbers_in_file(path)
            if total is not None:
                output.write(f"{path}: {total}\n")
                print(f"{path}: {total}")
            else:
                output.write(f"{path}: ошибка обработки\n")


if __name__ == "__main__":
    main()

🧪 Как проверить решение

  1. Создайте тестовую директорию с вложенными папками.
  2. В нескольких папках создайте файлы numbers.txt с числами и несколькими некорректными строками.
  3. Запустите: python process_numbers.py путь_к_тестовой_директории.
  4. Убедитесь, что в summary.txt появились результаты для каждого файла.
  5. Проверьте processing.log: некорректные строки должны быть записаны как ERROR.
  6. Попробуйте запустить без аргументов и с несуществующей директорией — программа должна вывести понятное сообщение.

💡 Советы и типичные ошибки

  • Не ловите «голый» except: — указывайте конкретные исключения.
  • Используйте os.path.join вместо ручной склейки путей.
  • Проверяйте аргументы sys.argv перед обращением по индексу.
  • Не коммитьте лог-файлы и summary.txt — добавьте их в .gitignore.