有没有办法测试标量是否已被字符串化?

时间:2012-03-20 20:57:03

标签: perl

我正在编写一个从perl结构输出类似于JSON的东西的东西。我希望引用的行为如下:

"string" outputs "string"
"05" outputs "05"
"5" outputs "5"
5 outputs 5
05 outputs 5, or 05 would be acceptable

JSON :: XS通过测试标量是否已经“字符串化”来处理这个问题,我觉得这很酷。但是我没有办法在没有编写XS的情况下自己做这个测试,我宁愿避免。这可能吗?我没有在CPAN的任何地方找到这个,没有找到关于Scalar :: Util :: looks_like_number等的大量迂回,这完全不是我想要的。我能找到的唯一权宜之处是Devel :: Peek,感觉很邪恶。而且,就像JSON :: XS一样,我对这个场景很好:

my $a = 5;
print $a."\n";
# now $a outputs "5" instead of 5)

3 个答案:

答案 0 :(得分:5)

检查B::svref_2object

的输出
use B;
($x, $y, $z) = ("5", 5, 5.0);

print ref(B::svref_2object( \$x )), "\n";
print ref(B::svref_2object( \$y )), "\n";
print ref(B::svref_2object( \$z )), "\n";

输出:

B::PV
B::IV
B::NV

或者,正如池上所说,如果你更愿意查找pPOK旗帜:

if (B::svref_2object( \$x )->FLAGS & B::SVp_POK) {
    print "I guess \$x is stringy\n";
}

答案 1 :(得分:4)

您可以使用以下内容使数字不再显示为字符串:

$x = 0+$x;

例如,

$ perl -MJSON::XS -E'
   $_ = 4;
   say encode_json([$_]);  # [4]
   "".$_;
   say encode_json([$_]);  # ["4"]
   $_ = 0 + $_;
   say encode_json([$_]);  # [4]
'

检测某些内容是否已被字符串化更加困难,因为JSON :: XS正在研究Perl的内部结构。可以使用以下内容:

sub is_stringy {
   { no warnings 'void'; "".$_[0]; }
   return 1;
}

但我认为这不是你想要的:)我不知道如何在不编写一些XS代码的情况下检测“损坏”。您想知道的是,标量SvPOKp是否为真(在标量上调用SvGETMAGIC之后)。

use Inline C => <<'__EOI__';

   SV* is_stringy(SV* sv) {
      SvGETMAGIC(sv);
      return SvPOKp(sv) ? &PL_sv_yes : &PL_sv_no;
   }

__EOI__

$_ = 4;
say is_stringy($_) ?1:0;   # 0
{ no warnings 'void'; "".$_; }
say is_stringy($_) ?1:0;   # 1
$_ = 0+$_;
say is_stringy($_) ?1:0;   # 0

OO!事实证明B确实提供了SVp_POK,所以它可以(几乎)在不编写新的XS代码的情况下完成

use B qw( svref_2object SVp_POK );

sub is_stringy {
   my ($s) = @_;
   my $sv = svref_2object(\$s);
   #$sv->GETMAGIC();  # Not available
   return $sv->FLAGS & SVp_POK;
}

无法拨打SvGETMAGIC有弊端,但几乎所有时间都可以使用。

答案 2 :(得分:2)

这可能不是最好的方法,但如果JSON符合您的要求,为什么不使用呢?

sub is_stringy {
    encode_json([$_[0]]) =~ /["']/
}

is_stringy(5);      # undef
is_string("5");     # 1