суббота, 6 января 2024 г.

Программа на Python для переименования файлов, сгенерированная ИИ

Мы до сих пор пользуемся винрарным портативным фотоаппаратом типа "мыльница" Canon SX280HS, который пока что рука не поднимается отправить в утиль - ведь основная функция до сих пор работает :)

В последние дни ушедшего года счётчик фотографий в очередной раз преодолел рубеж 10 тыс. снимков, что в таких фотоаппаратах Canon сбрасывает нумерацию, т.е. после снимка IMG_9999.jpg идёт IMG_0001.jpg, и последовательность нарушается, если фотографии оказываются в одном каталоге. Обычно я добавляю дату съёмки в начало имени файла (чтоб получить уникальные имена - возможно, пригодится в будущем), но это не особо помогает, если счётчик обнулился в течение дня.

Соответственно, возникает задача исправить имена так, чтобы последовательность соответствовала заснятым событиям. Проще всего подправить имена вида IMG_0001.jpg, заменив первый нуль на A, т.е. в моём случае имена вида 2023.12.24_IMG_0001.jpg, 2023.12.24_IMG_0010.jpg, ... преобразуются в 2023.12.24_IMG_A001.jpg, 2023.12.24_IMG_A010.jpg, ...

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

Для чистоты эксперимента всем сервисам задание будет сформулировано одинаковым образом - в текстовом виде на английском:

write a Python program to rename files with names like 2023.12.24_IMG_0001.jpg, 2023.12.24_IMG_0010.jpg, ... to 2023.12.24_IMG_A001.jpg, 2023.12.24_IMG_A010.jpg, ...

Copilot от Microsoft

Первым я решил опробовать Copilot от Microsoft, который бесплатно доступен всем желающим, - https://copilot.microsoft.com/.

На запрос сервис бодро выдал якобы рабочий код:

import os

def rename_files(directory, prefix):
    for filename in os.listdir(directory):
        if filename.startswith(prefix):
            # Extract the numeric part from the filename
            numeric_part = filename[len(prefix):].split(".")[0]
            try:
                numeric_value = int(numeric_part)
                new_filename = f"{prefix}_A{numeric_value:03d}.jpg"
                os.rename(os.path.join(directory, filename), os.path.join(directory, new_filename))
                print(f"Renamed {filename} to {new_filename}")
            except ValueError:
                print(f"Skipping {filename} (not a valid numeric filename)")

# Example usage:
directory_path = "."  # CHANGED: renaming files in the current directory
prefix_to_rename = "2023.12.24_IMG"
rename_files(directory_path, prefix_to_rename)

Нетрудно убедиться, что этот код требуемую замену не выполняет, а только выдаёт сообщения вида:

...
Skipping 2023.12.24_IMG_0089.jpg (not a valid numeric filename)
Skipping 2023.12.24_IMG_0090.jpg (not a valid numeric filename)
Skipping 2023.12.24_IMG_0091.jpg (not a valid numeric filename)
Skipping 2023.12.24_IMG_0092.jpg (not a valid numeric filename)
Skipping 2023.12.24_IMG_0093.jpg (not a valid numeric filename)
Skipping 2023.12.24_IMG_0094.jpg (not a valid numeric filename)
Skipping 2023.12.24_IMG_0095.jpg (not a valid numeric filename)

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

Впрочем, код достаточно простой и несложно увидеть, где AI накосячил, - всего два места надо поправить:

import os

def rename_files(directory, prefix):
    for filename in os.listdir(directory):
        if filename.startswith(prefix):
            # Extract the numeric part from the filename
            numeric_part = filename[len(prefix):].split(".")[0]
            try:
                numeric_value = int(numeric_part)
                new_filename = f"{prefix}A{numeric_value:03d}.jpg" # FIX2
                os.rename(os.path.join(directory, filename), os.path.join(directory, new_filename))
                print(f"Renamed {filename} to {new_filename}")
            except ValueError:
                print(f"Skipping {filename} (not a valid numeric filename)")

# Example usage:
directory_path = "."  # CHANGED: renaming files in the current directory
prefix_to_rename = "2023.12.24_IMG_" # FIX1
rename_files(directory_path, prefix_to_rename)

После этого замена работает так, как требуется.

GPT-4 от yeschat.ai

Бесплатного доступа к GPT-4 я пока что не нашёл, поэтому воспользовался сервисом www.yeschat.ai.

Ответ:

import os

def rename_files(directory):
    # Loop through all the files in the specified directory
    for filename in os.listdir(directory):
        # Check if the file name matches the desired pattern
        if filename.startswith("2023.12.24_IMG_") and filename.endswith(".jpg"):
            # Extract the number part from the file name
            number_part = filename.split('_')[2].split('.')[0]
            
            # Create the new file name by replacing the number part
            new_filename = filename.replace(number_part, 'A' + number_part.lstrip('0'))

            # Rename the file
            os.rename(os.path.join(directory, filename), os.path.join(directory, new_filename))
            print(f"Renamed '{filename}' to '{new_filename}'")

rename_files('.') # CHANGED: replacing in the current directory

В отличие от предыдущего варианта, в функцию передаётся каталог, но не префикс.

Замену он выполняет неправильно - в результате образуются файлы с именами 2023.12.24_IMG_A1.jpg, 2023.12.24_IMG_A10.jpg, ...

Если ассистенту сообщить о проблеме, то он делает вид, что понимает свою ошибку:

> The code doesn't work as expected - after renaming I get files with names like 2023.12.24_IMG_A1.jpg, 2023.12.24_IMG_A10.jpg,... , but I need 2023.12.24_IMG_A0001.jpg, 2023.12.24_IMG_A0010.jpg,...

>> I understand now. You need the renamed files to maintain their zero padding after the addition of the 'A' character. To achieve this, you can modify the script to add the 'A' and then reapply the zero padding to the number.

В итоге скрипт становится посложнее:

import os

def rename_files(directory):
    # Loop through all the files in the specified directory
    for filename in os.listdir(directory):
        # Check if the file name matches the desired pattern
        if filename.startswith("2023.12.24_IMG_") and filename.endswith(".jpg"):
            # Extract the number part from the file name
            number_part = filename.split('_')[2].split('.')[0]

            # Remove leading zeros, add 'A', and then reapply zero padding
            new_number_part = 'A' + number_part.lstrip('0').rjust(4, '0')

            # Create the new file name by replacing the number part
            new_filename = filename.replace(number_part, new_number_part)

            # Rename the file
            os.rename(os.path.join(directory, filename), os.path.join(directory, new_filename))
            print(f"Renamed '{filename}' to '{new_filename}'")

rename_files('.') # CHANGED: renaming in the current directory

Но результат переименования тоже неверный - получаются файлы с именами вида 2023.12.24_IMG_A0001.jpg, 2023.12.24_IMG_A0010.jpg, ... (лишний нуль после A).

Хотя достаточно было исправить исходную версию в одном месте:

import os

def rename_files(directory):
    # Loop through all the files in the specified directory
    for filename in os.listdir(directory):
        # Check if the file name matches the desired pattern
        if filename.startswith("2023.12.24_IMG_") and filename.endswith(".jpg"):
            # Extract the number part from the file name
            number_part = filename.split('_')[2].split('.')[0]
            
            # Create the new file name by replacing the number part
            new_filename = filename.replace(number_part, 'A' + number_part[1:]) # FIX

            # Rename the file
            os.rename(os.path.join(directory, filename), os.path.join(directory, new_filename))
            print(f"Renamed '{filename}' to '{new_filename}'")

rename_files('.') # CHANGED: renaming in the current directory

Claude 2.1 от yeschat.ai

Альтернативная LLM от того же сервиса www.yeschat.ai.

Ответ:

import os
import re

date_pattern = r"(\d{4}\.\d{2}\.\d{2})_IMG_\d{4}"

for filename in os.listdir("."):
    if re.match(date_pattern, filename):
        date_part = re.match(date_pattern, filename).group(1)
        idx_part = filename.split("_IMG_")[1].split(".")[0]
        new_idx_part = ""
        for c in idx_part:
            new_idx_part += chr(ord('A') + int(c) - 1)
        new_filename = f"{date_part}_IMG_{new_idx_part}.jpg"
        print(f"Renaming {filename} to {new_filename}") 
        os.rename(filename, new_filename)

Тут сразу обращает на себя внимание использоваение regexp'ов, код выглядит замороченным (в стиле "лапша 80-го уровня"), при этом он совершенно нерабочий - происходит замена без учёта префикса, в результате образуются имена вида 2023.12.24_IMG_@@@A.jpg, 2023.12.24_IMG_@@@B.jpg, ...

Незачот!

CodeLlama-34b-Instruct от together.ai (дополнение от 15.01.2024)

Open-source LLM на условно-бесплатном сервисе, которую можно (при необходимости) запустить и на своём оборудовании.

Ответ:

import os
dir_path = "." # CHANGED: renaming in the current directory
for filename in os.listdir(dir_path):
    if filename.startswith("2023.12.24_IMG_"):
        old_filepath = os.path.join(dir_path, filename)
        new_filename = filename.replace("IMG_", "IMG_A")
        new_filename = new_filename.replace(".jpg", "")
        new_filename = new_filename.zfill(4) + ".jpg"
        new_filepath = os.path.join(dir_path, new_filename)
        os.rename(old_filepath, new_filepath)
Даже без запуска ясно, что переименование будет неверным - в результате получатся имена с лишним нулём. Зато, если выкинуть "бредовый код", сгенерированное решение - одно из самых простых и его легко привести в порядок:
import os
dir_path = "." # CHANGED: renaming in the current directory
for filename in os.listdir(dir_path):
    if filename.startswith("2023.12.24_IMG_"):
        old_filepath = os.path.join(dir_path, filename)
        new_filename = filename.replace("IMG_0", "IMG_A")
        new_filepath = os.path.join(dir_path, new_filename)
        os.rename(old_filepath, new_filepath)

GPT-3.5 от OpenAI (дополнение от 16.01.2024)

Эту модель можно использовать бесплатно.
Ответ:
import os

def rename_files(directory):
    # Get a list of all files in the directory
    files = os.listdir(directory)

    for file_name in files:
        # Check if the file is a JPEG file with the specified naming pattern
        if file_name.endswith('.jpg') and file_name.startswith('2023.12.24_IMG_'):
            # Extract the numeric part of the file name
            numeric_part = file_name.split('_')[-1].split('.')[0]
            
            # Pad the numeric part with leading zeros to ensure correct sorting
            padded_numeric_part = numeric_part.zfill(4)

            # Create the new file name
            new_file_name = f'2023.12.24_IMG_A{padded_numeric_part}.jpg'

            # Build the full path of the file
            old_file_path = os.path.join(directory, file_name)
            new_file_path = os.path.join(directory, new_file_name)

            # Rename the file
            os.rename(old_file_path, new_file_path)
            print(f'Renamed: {file_name} -> {new_file_name}')

if __name__ == "__main__":
    target_directory = '.' # CHANGED: renaming in the current directory

    # Call the function to rename files
    rename_files(target_directory)
Нетрудно заметить (даже не запуская программу), что в результате переименования получатся имена с лишним нулём. Если об этом сообщить "интеллекту", то он "исправляет" ошибку, заменяя zfill(4) на zfill(3), что в целом является верным, однако ИИ не понимает, что numeric_part - строка, в которой сохранились ведущие нули, и zfill() тут не поможет.
Исправленый код может быть, например, таким:
import os

def rename_files(directory):
    # Get a list of all files in the directory
    files = os.listdir(directory)

    for file_name in files:
        # Check if the file is a JPEG file with the specified naming pattern
        if file_name.endswith('.jpg') and file_name.startswith('2023.12.24_IMG_'):
            # Extract the numeric part of the file name
            numeric_part = int(file_name.split('_')[-1].split('.')[0]) # FIX1
            
            # Pad the numeric part with leading zeros to ensure correct sorting
            padded_numeric_part = str(numeric_part).zfill(3) # FIX2

            # Create the new file name
            new_file_name = f'2023.12.24_IMG_A{padded_numeric_part}.jpg'

            # Build the full path of the file
            old_file_path = os.path.join(directory, file_name)
            new_file_path = os.path.join(directory, new_file_name)

            # Rename the file
            os.rename(old_file_path, new_file_path)
            print(f'Renamed: {file_name} -> {new_file_name}')

if __name__ == "__main__":
    target_directory = '.' # CHANGED: renaming in the current directory

    # Call the function to rename files
    rename_files(target_directory)
Но в целом решение выглядит излишне усложнённым.

Продолжение следует...

Комментариев нет:

Отправить комментарий