Подтвердить что ты не робот

Как разделить числовые скаляры и строковые скаляры в Perl?

Perl обычно преобразует числовые значения в строковые и наоборот. Однако должно быть что-то, что позволяет, например, Data::Dumper, чтобы различать оба, как в этом примере:

use Data::Dumper;
print Dumper('1', 1);

# output:
$VAR1 = '1';
$VAR2 = 1;

Есть ли функция Perl, которая позволяет мне одинаково различать, сохраняется ли скалярное значение как число или строка?

4b9b3361

Ответ 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 (скаляр, массив, хэш). Может использовать регулярное выражение, чтобы найти тип значения.