С того момента, как подруга приобрела музыкальный центр, читающий диски mp3,
мой десктоп стал резать музыкальные болванки в небывалых ранее количествах. Не
сразу, но все же всплыл вопрос нормальной записи файлов, названных по-русски,
так как центр отказывался читать названия по-русски, выдавая вместо связки
"исполнитель-песня" какую-то чушь. Переименовывать "руками" сотню с лишним
файлов - обезьянья работа, которую можно произвести однократно из любви к
искусству, но не более. Возникло желание автоматизировать процесс, и я задумался
как это можно сделать.
В этот раз путь от задумки до претворения в жизнь был недолог. В этот же день
программа была готова. Ниже я попробую изложить процесс написания программы как
можно более полно.
Средством решения поставленной задачи была выбрана программа, помещаемая в
папку с mp3-файлами (для упрощения программы), предназначенными для копирования
на CD, и переименовывающая все файлы, содержащие в названии буквы кириллицы. Все
символы кириллицы заменяются на соответствующие им латинские по правилам
транслитерации. Так как музыкальный центр читает только названия файлов, но не
теги, то с ними морочиться я не счел нужным. Что получилось? Получилось, что
программа не получает от пользователя никаких указаний (кроме запуска) и никаких
сообщений пользователю не выдает. То есть нет необходимости в каком бы то ни
было интерфейсе, что значительно упрощает поставленную задачу и уменьшает размер
программы.
Распишем алгоритм работы программы. Он прост: перебор файлов в папке и
проверка их названий на наличие русских букв, при положительном ответе -
переименование. Все.
Что ж, приступим.
Для начала напишем процедуру переименования. У меня она получилась такой:
function Renaming(fn: TSearchRec): string; // Функция переименования файла .mp3 транслитерацией var S: string; i, j: Byte; Ch: Char; begin S := extractfilename(fn.Name); j := Length(S); result := ''; for i := 1 to j do begin Ch := S[i]; case Ch of 'А': result := result + 'A'; 'а': result := result + 'a'; 'Б': result := result + 'B'; 'б': result := result + 'b'; 'В': result := result + 'V'; 'в': result := result + 'v'; 'Г': result := result + 'G'; 'г': result := result + 'g'; 'Д': result := result + 'D'; 'д': result := result + 'd'; 'Е': result := result + 'E'; 'е': result := result + 'e'; 'Ё': result := result + 'Yo'; 'ё': result := result + 'yo'; 'Ж': result := result + 'Zh'; 'ж': result := result + 'zh'; 'З': result := result + 'Z'; 'з': result := result + 'z'; 'И': result := result + 'I'; 'и': result := result + 'i'; 'Й': result := result + 'I'; 'й': result := result + 'i'; 'К': result := result + 'K'; 'к': result := result + 'k'; 'Л': result := result + 'L'; 'л': result := result + 'l'; 'М': result := result + 'M'; 'м': result := result + 'm'; 'Н': result := result + 'N'; 'н': result := result + 'n'; 'О': result := result + 'O'; 'о': result := result + 'o'; 'П': result := result + 'P'; 'п': result := result + 'p'; 'Р': result := result + 'R'; 'р': result := result + 'r'; 'С': result := result + 'S'; 'с': result := result + 's'; 'Т': result := result + 'T'; 'т': result := result + 't'; 'У': result := result + 'U'; 'у': result := result + 'u'; 'Ф': result := result + 'F'; 'ф': result := result + 'f'; 'Х': result := result + 'H'; 'х': result := result + 'h'; 'Ц': result := result + 'Ts'; 'ц': result := result + 'ts'; 'Ч': result := result + 'Ch'; 'ч': result := result + 'ch'; 'Ш': result := result + 'Sh'; 'ш': result := result + 'sh'; 'Щ': result := result + 'Chsh'; 'щ': result := result + 'chsh'; 'Ъ': result := result + '''' + ''''; 'ъ': result := result + '''' + ''''; 'Ы': result := result + 'Y'; 'ы': result := result + 'y'; 'Ь': result := result + ''''; 'ь': result := result + ''''; 'Э': result := result + 'E'; 'э': result := result + 'e'; 'Ю': result := result + 'Yu'; 'ю': result := result + 'yu'; 'Я': result := result + 'Ya'; 'я': result := result + 'ya' else result := result + Ch end end; if FileExists(result) then result := '' // в случае существования файла с таким же именем end;
В функцию передается информация о найденном файле. Здесь из него извлекается
имя и посимвольно преображается. Результат преображения передается в вызывающую
программу. Обратите внимание на последнюю операцию в функции. В ней происходит
проверка на существование в папке файла с именем, совпадающим с результатом
транслитерации имени другого файла, так как в случае попытки присвоения уже
существующего имени происходит фатальная ошибка. Ведь равно возможны две
ситуации : переименование файла Queen - Show Must Go On.mp3 в Queen - Show Must
Go On.mp3, что глупо, и переименование Секрет - Алиса.mp3 в Sekret - Alisa.mp3
при существовании файла Sekret - Alisa.mp3 , что фатально. Сейчас же и отвечу,
почему не переименовывается здесь же. Первоначально новое имя файл обретал здесь
же, в функции переименования, но во время тестирования программы в папке с
большим количеством файлов, начинался перебор уже переименованных файлов. То
есть возникла необходимость разделения во времени процедур перебора и
непосредственного переименования.
Обратимся теперь к главной программе: var R: TextFile; Renamed: file; S, NewName: string; MusicFile: TSearchRec; i, b: Integer; begin AssignFile(R, 'R.1'); // связываем файловую переменную с временным файлом R.1 if FindFirst('*.mp3', faAnyFile, MusicFile) = 0 then begin Rewrite(R); b := 0; repeat NewName := Renaming(MusicFile); if NewName <> '' then begin inc(b); // считаем mp3-файлы WriteLn(R, NewName); // заносим новое название файла во временный файл WriteLn(R, MusicFile.Name) // заносим старое название файла туда же end until FindNext(MusicFile) <> 0; CloseFile(R); Reset(R); for i := 1 to b do begin ReadLn(R, NewName); ReadLn(R, S); AssignFile(Renamed, S); Rename(Renamed, NewName) end; CloseFile(R); Erase(R); FindClose(MusicFile) end end.
Начнем с переменных. R - текстовый файл, куда в процессе работы будут
заноситься "старое" и "новое" имена файлов. Файл временный (его название,
кстати, выбрано абсолютно случайно), и по окончании работы программы
уничтожается. С остальными, думаю, все понятно.
Программа перебирает содержимое директории по маске '*.mp3', отбрасывая все
"другие" файлы. Как только находится первый подходящий файл, создается временный
файл R.1, а сам музыкальный файл "отправляется" на переименование. После
перебора всех файлов директории временный файл закрывается и вновь открывается,
уже для чтения. Согласно сделанным в нем записям производится переименование.
Анализ. Нельзя сказать, что приведенная реализация идеальна. Начнем с того,
что если произвести извлечение имени файла в теле программы, и в функцию
отправлять уже fn : String, то эту процедуру без значительных преобразований
можно использовать в другой программе для транслитерации строк. Спорен также
выбор файла для хранения временных данных. Можно для этих же целей
воспользоваться динамическим массивом, но это увеличит требования программы к
объему оперативной памяти (хотя современные компьютеры не страдают ее
недостатком), зато вырастет скорость исполнения (человеку не ощутить разницу)
из-за различий времени доступа к постоянной и оперативной памяти. Мало того,
можно вообще отказаться от хранения временных данных. Ну и что с того, что
программа будет переименовывать уже переименованные файлы? От присвоения файлу
его же собственного имени мы себя застраховали, а увеличившееся время работы,
повторяю, неощутимо и абсолютно не критично.
То есть, размер кода можно значительно сократить, сохранив при этом
работоспособность программы, надо только преобразовать функцию Renaming в
процедуру.
Тогда основная программа будет выглядеть так: var MusicFile: TSearchRec; begin if FindFirst('*.mp3', faAnyFile, MusicFile) = 0 then begin repeat Renaming(MusicFile) until FindNext(MusicFile) <> 0; FindClose(MusicFile) end end.
Повторю, необходимо немного переделать функцию Renaming. Например так: procedure Renaming(fn: TSearchRec); var Renamed: file; OldName, NewName: string; i: Byte; Ch: Char; begin OldName := ExtractFileName(fn.Name); for i := 1 to Length(OldName) do begin Ch := OldName[i]; case Ch of 'А': NewName := NewName + 'A'; ... 'я': NewName := NewName + 'ya' else NewName := NewName + Ch end; if not FileExists(NewName) then begin AssignFile(Renamed, OldName); Rename(Renamed, NewName) end end;
И наоборот, программу можно усложнить, но тогда появится форма, компоненты,
интерфейс и все связанные с этим проблемы, но и восприниматься программа будет
совсем иначе.
|