Домашняя страница Дениса Минича

 

 

На главнуюЧто я делаю...Программки автора и не только...Творчество
Статьи автораКнига отзывов и предложенийОбо мне, любимомФотоальбом

 

 

 
 

Как я писал программу FileTranslator

С того момента, как подруга приобрела музыкальный центр, читающий диски 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 - текстовый файл, куда в процессе работы будут заноситься "старое" и "новое" имена файлов. Файл временный (его название, кстати, выбрано абсолютно случайно), и по окончании работы программы уничтожается. Renamed - переименовываемый музыкальный файл. С остальными, думаю, все понятно.

Программа перебирает содержимое директории по маске '*.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;

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

Скачать программу

 
 

 

(с) 2004 by Минич Д.В.  // При использовании материалов ссылка обязательна

Hosted by uCoz