В данной статье мы рассмотрим проблему, связанную с функциями StrToInt и TryStrToInt в Delphi и Free Pascal Compiler (FPC). Проблема заключается в том, что при попытке преобразовать строку, содержащую значение, превышающее пределы типа Integer, данные функции не генерируют ошибку. Вместо этого они возвращают неверные значения или ложноположительные результаты. Мы рассмотрим причины этого поведения и предложим возможные решения для обхода этой проблемы.
Описание проблемы
Рассмотрим пример программы, представленный в исходном сообщении:
program PgmStrToInt;
uses
SysUtils;
const
cStr: string = '355685667191548007';
var
vStr: String;
vInt: integer;
vInt64: int64;
begin
vStr := cStr + '8'; // Force vStr as a non const string
vInt64 := StrToInt64(vStr); // OK
WriteLn(vInt64, ' ', HexStr(vInt64, 16)); // OK
vInt := StrToInt(vStr); // Doesnt raise an error. Is it OK ?
WriteLn(vInt, ' ', HexStr(vInt, 8)); // Seems to take only the lower 4 bytes
WriteLn(TryStrToInt(vStr, vInt)); // Returns true (Should be false)
ReadLn;
end.
В этом примере происходит следующее: - Строка cStr содержит значение '355685667191548007', которое превышает пределы типа Integer. - При добавлении символа '8' к строке cStr значение становится еще больше, чем допустимое для типа Integer. - Функция StrToInt64 корректно преобразует строку в значение типа Int64 и возвращает правильное значение. - Функция StrToInt не генерирует ошибку и возвращает значение, которое является лишь нижними 4 байтами исходного числа, что приводит к неверному результату. - Функция TryStrToInt возвращает true, что также является ошибочным результатом.
Причина проблемы
Проблема заключается в том, что функции StrToInt и TryStrToInt используют внутреннюю функцию Val для преобразования строки в целое число. Функция Val не проверяет диапазон значений, а просто пытается преобразовать строку в число, умещающееся в переменную указанного типа. Если строка содержит значение, превышающее пределы типа Integer, то Val просто усекает это значение до нижних 4 байт, не генерируя ошибку.
Вот пример из документации FPC, который демонстрирует это поведение:
procedure TestValSignedTooHigh;
var
I8: Int8;
I16: Int16;
I32: Int32;
I64: Int64;
Err: Integer;
begin
{$R+}
writeln('TestValSignedTooHigh: RangeCheck=ON');
try
Val('128',I8,Err);
if Err<>0 then writeln('128: Err=',Err) else writeln('128 -> ',I8,' [???]')
except
on E: ERangeError do writeln('128: RangeCheckError !!');
end;
try
Val('32768',I16,Err);
if Err<>0 then writeln('32768: Err=',Err) else writeln('32768 -> ',I16,' [???]')
except
on E: ERangeError do writeln('32768: RangeCheckError !!');
end;
try
Val('2147483648',I32,Err);
if Err<>0 then writeln('2147483648: Err=',Err) else writeln('2147483648 -> ',I32,' [???]')
except
on E: ERangeError do writeln('2147483648: RangeCheckError !!');
end;
try
Val('9223372036854775808',I64,Err);
if Err<>0 then writeln('128: Err=',Err) else writeln('9223372036854775808 -> ',I64,' [???]')
except
on E: ERangeError do writeln('9223372036854775808: RangeCheckError !!');
end;
{$R-}
writeln('TestValSignedTooHigh: RangeCheck=OFF');
try
Val('128',I8,Err);
if Err<>0 then writeln('128: Err=',Err) else writeln('128 -> ',I8,' [???]')
except
on E: ERangeError do writeln('128: RangeCheckError !!');
end;
try
Val('32768',I16,Err);
if Err<>0 then writeln('32768: Err=',Err) else writeln('32768 -> ',I16,' [???]')
except
on E: ERangeError do writeln('32768: RangeCheckError !!');
end;
try
Val('2147483648',I32,Err);
if Err<>0 then writeln('2147483648: Err=',Err) else writeln('2147483648 -> ',I32,' [???]')
except
on E: ERangeError do writeln('2147483648: RangeCheckError !!');
end;
try
Val('9223372036854775808',I64,Err);
if Err<>0 then writeln('128: Err=',Err) else writeln('9223372036854775808 -> ',I64,' [???]')
except
on E: ERangeError do writeln('9223372036854775808: RangeCheckError !!');
end;
end;
В этом примере видно, что при включенном режиме проверки диапазона (RangeCheck=ON) функция Val генерирует ошибку при попытке преобразовать значение, превышающее пределы типа. Однако при выключенном режиме проверки диапазона (RangeCheck=OFF) функция Val просто усекает значение до нижних 4 байт, не генерируя ошибку.
Решение проблемы
Для решения этой проблемы можно использовать функцию TryStrToInt64, которая проверяет диапазон значений и возвращает false, если строка содержит значение, превышающее пределы типа Int64. Если вам действительно нужно преобразовать строку в значение типа Integer, вы можете использовать следующий подход:
Преобразуйте строку в значение типа Int64 с помощью функции StrToInt64.
Проверьте, что полученное значение умещается в диапазоне типа Integer.
Если значение умещается, преобразуйте его в тип Integer. Если нет, выбросьте исключение или верните ошибку.
Вот пример кода, демонстрирующего этот подход:
function SafeStrToInt(const S: string): Integer;
var
Int64Value: Int64;
ResultValue: Integer;
begin
Int64Value := StrToInt64(S);
if (Int64Value < Low(Integer)) or (Int64Value > High(Integer)) then
raise Exception.Create('Строка содержит значение, превышающее пределы типа Integer');
ResultValue := Int64Value;
Result := ResultValue;
end;
procedure TestSafeStrToInt;
var
S: string;
I: Integer;
begin
S := '355685667191548007';
try
I := SafeStrToInt(S);
WriteLn('Результат:', I);
except
on E: Exception do
WriteLn('Ошибка:', E.Message);
end;
end;
begin
TestSafeStrToInt;
ReadLn;
end.
В этом примере функция SafeStrToInt сначала преобразует строку в значение типа Int64, а затем проверяет, что это значение умещается в диапазоне типа Integer. Если значение не умещается, функция выбрасывает исключение. В противном случае она возвращает преобразованное значение.
Альтернативные решения
Если вам нужно использовать функции StrToInt и TryStrToInt в проектах, где проверка диапазона значений является критичной, можно рассмотреть следующие альтернативные подходы:
Использование функции TryStrToInt64: Если вам нужно преобразовать строку в значение типа Integer, но вы не уверены, что строка содержит допустимое значение, можно использовать функцию TryStrToInt64 для преобразования строки в значение типа Int64 и затем проверить, что это значение умещается в диапазоне типа Integer.
Использование пользовательской функции для проверки диапазона: Если вам нужно использовать функции StrToInt и TryStrToInt в проектах, где проверка диапазона значений является критичной, можно создать пользовательскую функцию, которая будет преобразовывать строку в значение типа Integer и проверять, что это значение умещается в диапазоне.
Обновление компилятора: Если у вас есть возможность обновить компилятор до последней версии, можно рассмотреть возможность использования более новых версий FPC и Lazarus, которые могут содержать исправления для этой проблемы. В последних версиях FPC и Lazarus проблема может быть уже решена.
Заключение
Проблема с функциями StrToInt и TryStrToInt заключается в том, что они не проверяют диапазон значений и могут возвращать неверные значения или ложноположительные результаты при попытке преобразовать строку, содержащую значение, превышающее пределы типа Integer. Для решения этой проблемы можно использовать функцию TryStrToInt64 или создать пользовательскую функцию для проверки диапазона значений. Также рекомендуется обновить компилятор до последней версии, чтобы воспользоваться возможными исправлениями этой проблемы.
В статье рассматривается проблема некорректной работы функций StrToInt и TryStrToInt в Delphi и Free Pascal Compiler (FPC) при преобразовании строк, содержащих числа, превышающие максимальное значение типа Integer, предлагаются способы её решения.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.