Lazarus, встроенный парсер командной строки.

Преамбула

В Lazarus есть довольно неплохой парсер командной строки, который (почти) работает из коробки.

Для его использования нужно создать приложение на базе класса TCustomApplication, который обладает таким функционалом. Готовый шаблон проекта имеется в комплекте. Проект —> Создать проект… и в появившемся окне выбрать тип проекта Консольное приложение:

Можно ввести параметры для генерации кода:

Основной код приложения размещается в процедуре DoRun, например, в procedure TMyApplication.DoRun;

Решил расширить пример с поиском файла по маске (копия), заодно поэкспериментировать с парсером командной строки.

Параметры будут такие:

Использование: smallfinder.exe <аргументы>
-h - эта помощь
-m <маска> - маска файла для поиска. Обязательный параметр
-d <директория> - Начальняя директория, если параметр не указан, используется текущая.
-s - включить в поиск подкаталоги

Анализ параметров командной строки

Примечание: весь код в процедуре TSmallfinder.DoRun.

Почему-то способ проверки из документации, случая, когда параметров нет вообще, у меня сработал криво, так что пришлось вспоминать более старый:

// check if no parameters - способ из документации нихуя не сработал

if ParamCount=0 then begin
	WriteHelp;
	Terminate;
	Exit;
end;

Но далее все вроде бы пошло как надо, единственное, что параметры регистрозависимые (т.е. -d и -D программа воспринимает как разные параметры), пока не стал с этим разбираться, может после, если сильно надо будет. Длинные имена параметров не использовал, только короткие.

Вывод помощи:

//help
if HasOption('h', '') then begin
	WriteHelp;
	Terminate;
	Exit;
end;

Процедуру WriteHelp можно создать при создании нового проекта, а потом только запомнить, примерно так:

procedure TSmallfinder.WriteHelp;
begin
  writeln('Usage: ',ExtractFileName(ExeName), ' <arguments>');
  WriteLn('-h - this help');
  WriteLn('-m <mask> - file mask for search. Parameter must be!');
  WriteLn('-d <directory> - start directory. If not, use current dir.');
  WriteLn('-s - include subdirs');
end;

Маска файла:

//mask
if HasOption('m','') then begin
	Mask:=GetOptionValue('m','');
	if Mask = '' then begin
		WriteHelp;
		Terminate;
		Exit;
	end;
end;

Стартовый каталог:

//start directory
StartDir:=GetOptionValue('d','');
if StartDir='' then begin
	StartDir:=GetCurrentDir();
end;

Искать в подкаталогах:

//Include subdirs
IncludeSubdirs:=HasOption('s','');

Ну и сам процесс поиска, до кучи:

WriteLn('Start directory: ',StartDir);
lstFiles := TStringList.Create;
FindAllFiles(lstFiles, StartDir, Mask, IncludeSubdirs);
i:=0;
while i < lstFiles.Count do begin
	WriteLn(lstFiles[i]);
	inc(i);
end;
lstFiles.Free();

Естественно, все нужные переменные перечисляем в секции var процедуры TSmallfinder.DoRun

var
   Mask, StartDir:string;
   IncludeSubdirs:boolean;
   i:LongInt;
   lstFiles:TStringList;

Примеры работы

smallfinder.exe -m *.exe -d C:\Windows

smallfinder.exe -m *.exe -d C:\Windows -s

smallfinder.exe -m *.exe

Ссылки

Мануал по обработке параметров командной строки
Пример целиком на GitHub

Lazarus: Транслит строки (в консоли)

1. Понадобятся модули regexpr и fgl:

uses regexpr, fgl;

regexpr нужен для небольшой оптимизации, a fgl — для создания аналога словаря (Dictionary).

2. Создаем тип для будущего словаря:

type
  TDictTrans=class(specialize TFPGMap<string, string>);

Документация по TFPGMap

3. Сделаем функцию для транслитерации, с одним параметром, входной строкой с русскими буквами:

function Translit(Str:string):string;
//тут будет код
end;

4. Заводим внутренние переменные функции:

var Regex:TRegExpr;
Dict:TDictTrans;
Ch,oStr,oTrans:string;
I:LongInt;

Regex — экземпляр класса для работы с регулярным выражением.
Dict — словарь для транслитерации.
Ch — транслитерируемый символ
oStr — выходная строка
oTrans — сюда будем возвращать результат транслита отдельного символа.
I — счетчик цикла, в котором будем анализировать строку.

Небольшая оптимизация

Создаем новое регулярное выражение для кириллицы (и пробела) и проверяем входную строку на наличие русских букв. Если их нет — возвращаем исходную строку и выходим из функции:

Regex:=TRegExpr.Create;
Regex.Expression:='[А-Я]|[а-я]|\s';
if not Regex.Exec(Str) then begin
   exit(Str);
end;

5. Заполняем словарь (транслит взят из старого армейского учебника времен СССР, можете сделать свой):

Dict:=TDictTrans.Create;
Dict.Add(' ','_');
Dict.Add('А','A'); Dict.Add('а','a');
...
Dict.Add('Я','JA'); Dict.Add('я','ja');

Словарь целиком на PasteBin

6. Инициализируем переменные, используемые в цикле:

Ch:=''; oStr:='';

7. Заводим цикл for, нумерация символов в строке идет с 1, длина строки получается функцией Length(Str):

for I:=1 to Length(Str) do begin
...
end;

8. В цикле получаем символ из строки:

Ch:=Copy(Str,I,1);

9. Пробуем получить данные из словаря по ключу, которым является русская буква. Если это удалось, присоединяем результат транслита к выходной строке, если нет — это не русская буква, присоединяем исходный символ к выходной строке:

if Dict.TryGetData(Ch, oTrans) then begin
	oStr:=oStr+oTrans; //russkaya bukva - transliteriruem
end
else begin
	oStr:=oStr+Ch; //nerusskaya bukva, ostavlaem v pokoe
end;

10. Освобождаем память словаря после цикла:

Dict.Free;

11. Возвращаем результат работы функции:

exit(oStr);

Функция целиком на PasteBin

12. Код основной программы:

var
    strInput, strOutput:string;
...
begin
  Write('Input string:'); ReadLn(strInput);
  strOutput:=Translit(strInput);
  WriteLn(strOutput);
  WriteLn('Press Enter...'); ReadLn();
end.

Для совместимости с русским языком в консоли необходимо добавить директивы компилятора, иначе словарь будет работать неправильно:

program translit;
{$mode objfpc} {H+}
{$codepage CP866}
...

$mode objfpc
H+ — чтоб строки по умолчанию не были ShortString‘ами
$codepage CP866 — установка кодовой страницы.

Документация по работе со строками

Проверка

Исходник примера на GitHub

Lazarus, поддержка русских букв в консоли (Windows 7)

Из коробки русские буквы в консоли поддерживаются через жопу:

Это потому что Lazarus по умолчанию создает файл в UTF8, а консоль Windows 7 поддерживает CP 866 (кодировку DOS/OEM), достаточно перекодировать файл:

1. Щелкаем по пустому месту в исходнике в редакторе.

2. Выбираем Параметры файла —> Кодировка

3. В выпадающем списке выбираем CP866:

4. В появившемся окне нажимаем кнопку Изменить файл:

5. ФАНФАРЫ!

Источник
Тестовый пример на GitHub

Lazarus, регулярные выражения.

Из коробки доступен мощный класс TRegExpr, вполне себе работает с регулярками. Сожрал даже C#-овскую, без изменения синтаксиса вообще. Пример регулярки для обнаружения русских букв:

program regexptest;
uses regexpr;
var  Regex:TRegExpr;

begin
     Regex:=TRegExpr.Create;
     Regex.Expression:='[а-я]|\s';
     Writeln(Regex.Exec('АБВГ'));
     Writeln(Regex.Exec('ABCD'));
     ReadLn();
end.

Документация
Пример на Киберфоруме

Этот пример на GitHub

UPD: Более лучшая регулярка для поиска кириллицы (и пробела).

[А-Я]|[а-я]|\s

Первая ([а-я]|\s) нормально работает, если формат файла исходника UTF-8, и текст в UTF-8, а вот с консолью в Win7 она работает только на строчных буквах, на заглавных не работает. А консоль требует CP866.

О других косяках кириллицы в консоли — в следующих выпусках нашего журнала.

Lazarus, список каталогов с подкаталогами

Плохо, что по маске не умеет каталоги искать. А в остальном все просто.

program alldirs;
uses Classes, SysUtils, FileUtil;
var
   lstDirs:TStringList;
   i: Integer;
begin
     lstDirs := TStringList.Create;
     FindAllDirectories(lstDirs,'C:\Windows',true);
     i:=0;
     while i < lstDirs.Count do begin
       WriteLn(lstDirs[i]);
       inc(i);
     end;
     WriteLn ('Found: ',lstDirs.Count);
     WriteLn ('Press Enter');
     lstDirs.Free();
     ReadLn();
end.

Ссылка на GitHub