Некоторое время назад одна любезная душа прислала мне этот
модуль. Я нашел его весьма полезным, но применять его вам надлежит с некоторой
долей осторожности, ибо тэг %s иногда приводит к исключительным ситуациям.
unit Scanf;
interfaceuses SysUtils;
type
EFormatError = class(ExCeption);
function Sscanf(const s: string; const fmt: string;
const Pointers: arrayof Pointer): Integer;
implementation{ Sscanf выполняет синтаксический разбор входной строки. Параметры...
s - входная строка для разбора
fmt - 'C' scanf-форматоподобная строка для управления разбором
%d - преобразование в Long Integer
%f - преобразование в Extended Float
%s - преобразование в строку (ограничено пробелами)
другой символ - приращение позиции s на "другой символ"
пробел - ничего не делает
Pointers - массив указателей на присваиваемые переменные
результат - количество действительно присвоенных переменных
Например, ...
Sscanf('Name. Bill Time. 7:32.77 Age. 8',
'. %s . %d:%f . %d', [@Name, @hrs, @min, @age]);
возвратит ...
Name = Bill hrs = 7 min = 32.77 age = 8 }function Sscanf(const s: string; const fmt: string;
const Pointers: arrayof Pointer): Integer;
var
i, j, n, m: integer;
s1: string;
L: LongInt;
X: Extended;
function GetInt: Integer;
begin
s1 := '';
while (s[n] = ' ') and (Length(s) > n) do
inc(n);
while (s[n] in ['0'..'9', '+', '-'])
and (Length(s) >= n) dobegin
s1 := s1 + s[n];
inc(n);
end;
Result := Length(s1);
end;
function GetFloat: Integer;
begin
s1 := '';
while (s[n] = ' ') and (Length(s) > n) do
inc(n);
while (s[n] in ['0'..'9', '+', '-', '.', 'e', 'E'])
and (Length(s) >= n) dobegin
s1 := s1 + s[n];
inc(n);
end;
Result := Length(s1);
end;
function GetString: Integer;
begin
s1 := '';
while (s[n] = ' ') and (Length(s) > n) do
inc(n);
while (s[n] <> ' ') and (Length(s) >= n) dobegin
s1 := s1 + s[n];
inc(n);
end;
Result := Length(s1);
end;
function ScanStr(c: Char): Boolean;
beginwhile (s[n] <> c) and (Length(s) > n) do
inc(n);
inc(n);
if (n <= Length(s)) then
Result := Trueelse
Result := False;
end;
function GetFmt: Integer;
begin
Result := -1;
while (TRUE) dobeginwhile (fmt[m] = ' ') and (Length(fmt) > m) do
inc(m);
if (m >= Length(fmt)) then
break;
if (fmt[m] = '%') thenbegin
inc(m);
case fmt[m] of
'd': Result := vtInteger;
'f': Result := vtExtended;
's': Result := vtString;
end;
inc(m);
break;
end;
if (ScanStr(fmt[m]) = False) then
break;
inc(m);
end;
end;
begin
n := 1;
m := 1;
Result := 0;
for i := 0 to High(Pointers) dobegin
j := GetFmt;
case j of
vtInteger:
beginif GetInt > 0 thenbegin
L := StrToInt(s1);
Move(L, Pointers[i]^, SizeOf(LongInt));
inc(Result);
endelse
break;
end;
vtExtended:
beginif GetFloat > 0 thenbegin
X := StrToFloat(s1);
Move(X, Pointers[i]^, SizeOf(Extended));
inc(Result);
endelse
break;
end;
vtString:
beginif GetString > 0 thenbegin
Move(s1, Pointers[i]^, Length(s1) + 1);
inc(Result);
endelse
break;
end;
else
break;
end;
end;
end;
end.
Перевод контента на русский язык:
Это реализация функции sscanf в Delphi, которая похожа на соответствующую функцию из библиотеки C. Функция принимает три параметра: s (входная строка), fmt (строка форматирования, аналогичная тем, которые используются в scanf), и Pointers (массив указателей на переменные, которые получат парсированные значения).
Реализация использует серию вспомогательных функций (GetInt, GetFloat, GetString) для парсинга входной строки по форматному спецификации. Основной цикл проходит по строке форматирования, парсит каждый поле и присваивает его соответствующей переменной.
Вот некоторые заметки о коде:
Класс исключения EFormatError определён в верхней части модуля, но не используется в реализации.
Функция GetFmt используется для парсинга строки форматирования и определения типа значения, ожидаемого для каждого поля. Это делается путем сканирования строки форматирования по символам %, за которыми следует одиночный символ (d, f или s), указывающий тип значения.
Функция ScanStr используется для сканирования входной строки в поисках конкретного символа (например, пробела, цифры и т.д.). Она возвращает True, если символ найден, и False в противном случае.
В основном цикле реализация использует простой оператор break для выхода из цикла ранее, когда она находит неожиданный форматный спецификатор или обработала все поля в входной строке.
В отношении потенциальных проблем с форматными спецификаторами %s, вы правы, что они могут вызывать проблемы, если не используются осторожно. В этой реализации форматный спецификатор %s обрабатывается вызовом функции GetString, которая сканирует входную строку до обнаружения символа пробела (или конца строки). Если входная строка содержит несколько пробелов или другие символы whitespace, это может потенциально привести к неожиданному поведению.
Вот некоторые потенциальные улучшения, которые можно сделать в коде:
Добавьте более полное обслуживание ошибок и механизмы журналирования для помощи в диагностике проблем с недопустимыми строками форматирования или входными данными.
Рассмотрите использование библиотеки регулярных выражений (например, TPregExp в Delphi) для улучшения парсинга форматных спецификаторов и значений ввода.
Реализуйте поддержку дополнительных форматных спецификаторов, таких как %c для символов или %p для указателей.
Добавьте более полное тестирование модуля для обеспечения корректной работы реализации для различных случаев ввода.
В статье описывается модуль Sscanf для Delphi, который выполняет синтаксический разбор входной строки в соответствии с форматом scanf и позволяет присваивать значения переменным.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.