Чтоб приплыл к тебе,
Сука-Вовочка…
Синекольчатый осьминог.

Чтоб приплыл к тебе,
Сука-Вовочка…
Синекольчатый осьминог.
Продолжаем бодаться с русским языком в консоли (копия).
Ну не может же быть так, что виндовая консоль и Unicode (UTF-8) не поддерживает, подумал я. У меня и функции, которые, собственно, в программе нужны, UTF8 требуют, и с русским языком, если исходник не в UTF-8 работают криво, и в документации по Lazarus написано, что он поддерживает вывод на консоль в UTF-8, и в документации по винде написано, что она тоже нежно любит UTF-8, хотя может и в OEM(которая CP866).
Хинт оказался небольшим, неочевидным, и вообще был обнаружен чисто случайно, кодировку исходника надо поменять на на CP866, как я делал по ссылке выше, а на UTF-8 с BOM!
И нигде в документации (не в виндовой, не в Лазарувской) об этом не сказано, ну или закопано в такие бездны Варпа, что не докопался.
До (исходник в UTF-8):
После (исходник в UTF-8 с BOM):
program Project1; begin WrileLn('Какая-то фигня с русскими буквами'); WrileLn('А, уже не фигня'); Readln(); end.
В 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
1. Понадобятся модули regexpr
и fgl
:
uses regexpr, fgl;
regexpr
нужен для небольшой оптимизации, a fgl
— для создания аналога словаря (Dictionary
).
2. Создаем тип для будущего словаря:
type TDictTrans=class(specialize TFPGMap<string, string>);
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');
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);
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
— установка кодовой страницы.
Документация по работе со строками
Из коробки русские буквы в консоли поддерживаются через жопу:
Это потому что Lazarus по умолчанию создает файл в UTF8, а консоль Windows 7 поддерживает CP 866 (кодировку DOS/OEM), достаточно перекодировать файл:
1. Щелкаем по пустому месту в исходнике в редакторе.
2. Выбираем Параметры файла —> Кодировка
3. В выпадающем списке выбираем CP866:
4. В появившемся окне нажимаем кнопку Изменить файл:
5. ФАНФАРЫ!