Итак, имеем:
1. Заказчик xdsl, исполнитель fatum
2. Техническое задание, сформулированное в первом посте темы
3. Результирующий код на object pascal (в принципе, для перевода на freepascal достаточно заменить {$APPTYPE CONSOLE} на {$mode objfpc}):
- Код: Выделить всё
{$APPTYPE CONSOLE}
uses
SysUtils;
var mask:string;
function perevod(iname:string):string;
var i:integer;
c:char;
begin
result:='';
for i:=1 to length(iname) do
begin
c:=iname[i];
case c 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+'4'; 'ч': result:=result+'4'; 'Ш': 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+c
end
end;
end;
procedure _rename(path:string);
var sr:TSearchRec;
iname,oname,ext:string;
begin
if FindFirst(path+'*.*',faDirectory,sr)=0 then
begin
repeat
if (sr.Attr and faAnyFile and not faDirectory)=sr.Attr then
begin
iname:=sr.Name;
ext:=SysUtils.ExtractFileExt(iname);
if (ext<>mask) and (mask<>'') then continue;
oname:=perevod(iname);
if (FileExists(path+oname))then
oname:='';
RenameFile(path+iname,path+oname);
end
else
if (sr.Attr and faDirectory)=sr.Attr then
begin
if (sr.Name<>'..') and (sr.Name<>'.') then
_rename(path+sr.Name+'\');
end;
until FindNext(sr)<>0;
FindClose(sr);
end;
end;
var path:string;
begin
if paramstr(1)='--help' then
begin
writeln('renfile - utilite for translate');
writeln('russian file names to latin');
writeln('Call with 2 parametres:');
writeln('path - path to dir');
writeln('ext - file extension. if exists, all files will be translated');
exit;
end;
if paramstr(1)='' then
begin
writeln('Usage:');
writeln('renfile <path> [mask]');
writeln('for help use "renfile --help"');
exit;
end;
path:=trim(paramstr(1));
if paramstr(2)='' then mask:='' else
mask:=trim(paramstr(2));
if not DirectoryExists(path) then begin
writeln('Error! Directory not exists. Please check your parameter "path"');
exit;
end;
if path[length(path)]<>'\' then path:=path+'\';
_rename(path);
writeln('press Enter...');
readln;
end.
Взял на себя смелость и сделал пару косметических модификаций - исправил пару описок в сообщениях и ужал для лучшей представительности функцию perevod.
Исходное ТЗ:
0. Транслитератор имён файлов с рекурсивным обходом каталогов
1. консольная утилита
2. функционирующая на чистой, только-что установленной win32-системе.
3. принимающая два параметра: каталог_для_поиска маска_транслитируемых_файлов
4. НИЧЕГО, КРОМЕ УКАЗАННОГО - НЕ ДЕЛАЮЩАЯ
С первого взгляда - все соответствует. Файлы транслитерируются. Консольно. Без дополнительных библиотек. Утилита с двумя параметрами. Никаких дополнительных действий (сообщения об ошибках в наборе параметров и help-страницу будем считать неизбежной рюшечкой).
К сожалению, программа делает не то, что заказчик хотел, а то, что он сказал. Типичная ситуация, причина которой во многом - нечеткое или слабораскрытое техническое задание.
1. В ТЗ ничего не сказано о переименовании каталогов. Заказчика понять можно - он работает в линуксе, где всё является файлом - и регулярный файл, и каталог, и сокет, и пайп, и раздел жесткого диска и т.п. Исполнителя то-же понять можно - он пишет программу для виндовс, где файл и каталог - разные категории. Результат вполне очевиден - программа переименовывает ТОЛЬКО ФАЙЛЫ, оставляя рускоязычные каталоги без модификаций. К счастью, исправление данного недочёта решается добавлением одной строки
2. Вдогонку первому пункту. Вполне логичным явилось-бы добавление третьего параметра - маска_транслитерируемых_каталогов
3. В ТЗ нераскрыто понятие "маска_транслитируемых_файлов". В результате под маской исполнитель понимает расширение файла, а заказчик - классическое понимание маски (globbing). Примеры:
рисунок*.*,
*.*,
*.avi,
*.jp* и т.д. Исправление - нетривиально и, возможно, потребует полного переписывания функции _rename (лично я аналога юниксового glob в свежеустановленном виндовсе не знаю, разве что в дотнете, но желания нет жертвовать 3-4 кратным замедлением работы программы после переписывания ее на С#, а также требовать наличия дотнет на любой виндовс системе)
4. В ТЗ не сказано, что делать в ситуации, когда имя транслитерируемого файла уже занято. Другим файлом или каталогом. В результате исполнитель только фиксирует данный факт и ничего не предпринимает.
Суммарно эти недостатки приводят к тому, что для задач, решаемых заказчиком, данная утилита не подходит. В реальной жизни с этой точки начинаются препирательства между заказчиком и исполнителем, иногда решаемые миром, а иногда доходящие до судебных разбирательств. Мы выбираем мирный вариант
. Поле для деятельности расчищенно, готовлю свои варианты кода и жду код от всех желающих принять участие в обсуждении.
P.S. Думаю, разговор об оптимизации кода утилиты следует отложить до момента, когда она будет полностью удовлетворять нуждам заказчика. Однако уже сейчас можно увидеть низкую эффективность функции perevod, связанную с активными строковыми манипуляциями, скорость которых в паскале невысока. Дополнительно, данную функцию в ее исходном виде будет сложно модифицировать в транслитерацию имен файлов из неоднобайтной кодировки (например - из UTF-8). Для текущей задачи это несущественно, но с точки зрения возможного дальнейшего кроссплатформенного применения становится актульным.