Как разделить числовые скаляры и строковые скаляры в Perl?
Perl обычно преобразует числовые значения в строковые и наоборот. Однако должно быть что-то, что позволяет, например, Data::Dumper
, чтобы различать оба, как в этом примере:
use Data::Dumper;
print Dumper('1', 1);
# output:
$VAR1 = '1';
$VAR2 = 1;
Есть ли функция Perl, которая позволяет мне одинаково различать, сохраняется ли скалярное значение как число или строка?
Ответы
Ответ 1
Невозможно найти это, используя чистый perl. Данные:: Dumper использует библиотеку C для ее достижения. Если принудительно использовать Perl, он не выделяет строки из чисел, если они выглядят как десятичные числа.
use Data::Dumper;
$Data::Dumper::Useperl = 1;
print Dumper(['1',1])."\n";
#output
$VAR1 = [
1,
1
];
Ответ 2
Скаляр имеет ряд различных полей. При использовании Perl 5.8 или выше Data:: Dumper проверяет, есть ли что-нибудь в поле IV (целочисленное значение). В частности, он использует что-то похожее на следующее:
use B qw( svref_2object SVf_IOK );
sub create_data_dumper_literal {
my ($x) = @_; # This copying is important as it "resolves" magic.
return "undef" if !defined($x);
my $sv = svref_2object(\$x);
my $iok = $sv->FLAGS & SVf_IOK;
return "$x" if $iok;
$x =~ s/(['\\])/\\$1/g;
return "'$x'";
}
Вы можете использовать подобные трюки. Но имейте в виду,
-
Очень сложно вывести числа с плавающей запятой без потерь. (Число плавающих указателей идентифицируется с помощью $sv->FLAGS & SVf_NOK
.)
-
Вам нужно правильно избежать определенных байтов (например, NUL) в строковых литералах.
-
Скаляр может иметь более одного значения, сохраненного в нем. Например, !!0
содержит строку (пустую строку), число с плавающей запятой (0
) и целое число со знаком (0
). Как вы можете видеть, разные значения даже не всегда эквивалентны. Для более драматического примера проверьте следующее:
$ perl -E'open($fh, "non-existent"); say 0+$!; say "".$!;'
2
No such file or directory
Ответ 3
Это сложнее. Perl изменяет внутреннее представление переменной в зависимости от контекста, в котором используется переменная:
perl -MDevel::Peek -e '
$x = 1; print Dump $x;
$x eq "a"; print Dump $x;
$x .= q(); print Dump $x;
'
SV = IV(0x794c68) at 0x794c78
REFCNT = 1
FLAGS = (IOK,pIOK)
IV = 1
SV = PVIV(0x7800b8) at 0x794c78
REFCNT = 1
FLAGS = (IOK,POK,pIOK,pPOK)
IV = 1
PV = 0x785320 "1"\0
CUR = 1
LEN = 16
SV = PVIV(0x7800b8) at 0x794c78
REFCNT = 1
FLAGS = (POK,pPOK)
IV = 1
PV = 0x785320 "1"\0
CUR = 1
LEN = 16
Ответ 4
Основываясь на вашем комментарии, чтобы определить, требуется ли цитирование для оператора SQL, я бы сказал, что правильным решением является использование заполнителей, которые описаны в документации DBI.
Как правило, вы не должны интерполировать переменные непосредственно в строке запроса.
Ответ 5
Если переменная используется как число, это приводит к тому, что переменная будет считаться числовой в последующих контекстах. Однако обратное не совсем верно, как показано в этом примере:
use Data::Dumper;
my $foo = '1';
print Dumper $foo; #character
my $bar = $foo + 0;
print Dumper $foo; #numeric
$bar = $foo . ' ';
print Dumper $foo; #still numeric!
$foo = $foo . '';
print Dumper $foo; #character
Можно ожидать, что третья операция вернет $foo
в контексте строки (реверсирование $foo + 0
), но это не так.
Если вы хотите проверить, является ли что-то числом, стандартный способ - использовать регулярное выражение. То, что вы проверяете, зависит от того, какой номер вы хотите:
if ($foo =~ /^\d+$/) { print "positive integer" }
if ($foo =~ /^-?\d+$/) { print "integer" }
if ($foo =~ /^\d+\.\d+$/) { print "Decimal" }
И так далее.
Обычно не рекомендуется проверять, как что-то хранится внутри - вам, как правило, не нужно беспокоиться об этом. Однако, если вы хотите дублировать то, что здесь делает Dumper, это не проблема:
if ((Dumper $foo) =~ /'/) {print "character";}
Если вывод Dumper содержит одиночную кавычку, это означает, что она отображает переменную, представленную в виде строки.
Ответ 6
Возможно, вы захотите попробовать Params::Util::_NUMBER
:
use Params::Util qw<_NUMBER>;
unless ( _NUMBER( $scalar ) or $scalar =~ /^'.*'$/ ) {
$scalar =~ s/'/''/g;
$scalar = "'$scalar'";
}
Ответ 7
Модуль autobox::universal
, который поставляется с autobox, предоставляет type
, которая может быть использована для этой цели:
use autobox::universal qw(type);
say type("42"); # STRING
say type(42); # INTEGER
say type(42.0); # FLOAT
say type(undef); # UNDEF
Ответ 8
Одно простое решение, которое не упоминалось, было Scalar:: Util looks_like_number. Scalar:: Util - это основной модуль с 5.7.3, а look_like_number использует perlapi, чтобы определить, является ли скаляр числовым.
Ответ 9
Я не думаю, что есть функция perl, чтобы найти тип значения. Можно найти тип DS (скаляр, массив, хэш). Может использовать регулярное выражение, чтобы найти тип значения.