• Как реализовать копирование файлов с одинаковыми именами под разными именами, а не перезаписывать созданный файл при нахождении одноимённого файла?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Есть несколько простых правил:

    0. Следите за отступами и форматируйте код правильно.
    Для питона это самое главное правило. Вы некорректно наставили тегов для оборачивания кода, я вам лишние убрал, но ваши отступы в коде поломаны. К этому нужно относиться внимательно.
    Помните, что помимо пробелов бывают табуляции. Табуляция - это один широкий символ, но его ширина нигде не регламентирована, а питон по количеству пустых символов вначале строки понимает её вложенность в коде. Само собой смешение табуляций и пробелов в питоновском коде ведёт к страшной неразберихе и гарантированным ошибкам. Холивару "пробелы против табов" не одна сотня человеко-лет, но есть общее правило, зафиксированное в PEP: используйте 4 пробела в качестве одного отступа. Всегда. Без исключений.

    1. Не используйте скобки там, где они не нужны:
    disk = ('C:\\')
    filen1 = (f'{root}' + str('\\') + f'{name}')

    В питоне скобки принято использовать для обозначения кортежей. Кортеж формируется запятыми выражении, но скобками ограничивают нужные элементы. В любом случае лишние скобки в питоне - плохой стиль. В вашем случае они не ломают логику, но формируют плохую привычку у вас.

    1. Не конкатенируйте пути вручную, как вы это делаете здесь:
    f'{root}' + str('\\') + f'{name}'
    Приведение строки к строке бессмысленно. Могли бы просто написать так: f'{root}\\{name}', но и это плохая идея, потому что нельзя конкатенировать пути вручную. В разных операционных системах используются разные символы-разделители в путях. В питоне для работы с путями есть модули: os.path (древний как навоз мамонта) и pathlib (новый, объектный, очень удобный).

    2. Используйте pathlib для работы с путями.
    Изучите эту библиотеку. Вы откроете для себя массу полезных вещей.

    3. Используйте оператор with при работе с закрываемыми ресурсами.
    Ваш код:
    file1 = open(filen1, 'rb')
    file2 = open(filen2, 'wb')
    file2.write(file1.read())
    file1.close()
    file2.close()

    Станет таким:
    with open(filen1, 'rb') as file1, open(filen2, 'wb') as file2:
        file2.write(file1.read())

    А с использованием pathlib ещё лаконичнее:
    directory = Path('path/to/my/dir')
    fn1 = directory / 'file1.jpg'
    fn2 = directory / 'file2.jpg'
    file2.write_bytes(file1.read_bytes())

    Слеши расставятся в правильную сторону сами, как это нужно системе, вам никогда больше об этом не придётся задумываться. Не придётся думать об экранировании слешей, как это сделано у вас в путях. В библиотеке перекрыт оператор деления, через него удобно собирать путь.
    А ещё там полно всяких удобностей вроде проверки наличия файла, создания каталогов, удаления...

    4. Не смешивайте работу с каталогами и с файлами:
    if name in dirs or name in files:
    В каких-то случаях, возможно, и есть сходства, ведь в файловой системе каталог - это тоже файл. Но вы не сможете его ни прочитать, как таковой ни записать. По логике вашей программы получается, что, если есть каталог с таким именем и расширением, то вы его будете тоже пытаться читать. на этом ваш код и упадёт.
    В pathlib, кстати, есть отличный метод rglob, который рекурсивно итерируется по всем путям, соответствующим маске. Сделать это можно относительно любого каталога:
    files = Path('c:').rglob('*.jpg')
    найдёт все jpeg-картинки на вашем диске c:.
    При этом вы получите объект, по которому можно итерироваться циклом или превратить его в список. Элементы такой последовательности будут тоже типа pathlib.Path.

    5. Для решения любой задачи разбейте ее на элементарные части и решайте их по отдельности.
    В данном случае логично написать отдельную функцию безопасного копирования одного файла. Эта функция должна проверить есть ли целевое имя файла (если нет, то копирует), какого размера там файл (если такого-же до байта, то нужно проверить хеш и отказаться от копирования при совпадении, если другого, то нужно менять целевое имя или путь). С помощью pathlib можно подставлять расширение и быстро получить все файлы по маске в каталоге. Это позволит, например, быстро понять какой самый последний файл с именем вида 'file_name_00023.jpg' по маске 'file_name_*****.jpg', достать его цифровой хвостик, превратить в целое число, прибавить единицу и снова подставить в имя: Path('file_name_{last_index+1:05}.jpg').

    6. Подозреваю вы хотите написать безопасную дедупликацию картинок.
    • Прочитайте про хеш-функции и как они могут помочь сказать наверняка одинаковы ли два файла.
    • Прочитайте про символические ссылки. Это как ярлыки, но на уровне файловой системы и с файлами можно работать точно так же как с оригиналами. Такие ссылки позволят не копировать и не дублировать данные, когда требуется лишь как-то альтернативно сгруппировать их по каталогам.
    • Прочитайте про жесткие ссылки. Это выглядит как несколько имён одного файла в произвольных каталогах. Если хоть одно имя останется не удалённым, то и файл на диске будет. Данные хранятся один раз, а лежит файл как бы сразу в нескольких местах и нет никакого приоритета между ними.
    • Подумайте о журналировании. Иногда правильнее вместо реальных операций с файловой системой написать подробный журнал того, что вы хотите сделать в ней, а затем, по окончании опасного длительного процесса сборки этого журнала, можно утвердить и согласовать действия с пользователем, проанализировать список предстоящих операций и выполнить уже их спокойно или отказаться от них.
    Ответ написан
    1 комментарий