【问题标题】:Determining if a perl scalar is a string or integer确定 perl 标量是字符串还是整数
【发布时间】:2016-05-15 14:46:27
【问题描述】:

根据我的阅读,perl 没有整数或字符串的真正类型,对他来说任何 $variable 都是标量。

最近我在一个旧脚本上遇到了很多麻烦,该脚本正在生成另一个进程所需的 JSON 对象,其中 JSON 中的值必须是整数,经过一些调试后我发现因为一个简单的打印函数:

JSON::encode_json

正在生成一个字符串而不是一个整数,这是我的示例:

use strict; 
use warnings; 
use JSON;
my $score1 = 998;
my $score2 = 999;
print "score1: ".$score1."\n";
my %hash_object = ( score1 => $score1, score2 => $score2 );
my $json_str = encode_json(\%hash_object);  # This will work now
print "$json_str";

它输出:

score1: 998
{"score1":"998","score2":999}

不知何故 perl 变量有一个类型,或者至少 JSON::encode_json 是这样认为的。

有没有办法以编程方式找到这种类型以及为什么在进行上述连接这样的操作时会改变这种类型?

【问题讨论】:

  • %arr 是一个可怕的变量名称。首先,它是一个hash,而不是一个数组,其次,标识符应该反映一个变量用途,而不是它的性质。 % 说它是一个哈希,所以 %hash 是多余的,有点可笑。但有时没什么好说的,尤其是关于聚合数据结构,我经常求助于%data,我认为这很好
  • 感谢您的建议,我同意您的观点并在我的示例中进行了一些更改。
  • 只是一个想法建议,不确定它在实践中是否有效。但是你可以用 int 包装 score1 吗?例如:my %hash_object = ( score1 => int($score1), score2 => $score2 );

标签: json perl variables


【解决方案1】:

首先,它有些不正确。是的,每个值都是标量,但标量具有单独的数字和字符串值。对于这个特定问题,您只需要知道这些,所以我将省略详细信息。

您真正需要的是查看 JSON 模块文档的 MAPPING / PERL -> JSON / simple scalars 部分:

JSON::XS 和 JSON::PP 会将未定义的标量编码为 JSON null 之前最后在字符串上下文中使用的值、标量 编码为 JSON 字符串,以及其他任何数字值。

(这里的文档实际上有点错误。它不是“最后一次在字符串上下文中使用”它应该是“曾经在字符串上下文中使用过” - 一旦标量获得字符串值它不会消失,直到你 明确写入一个新数字。)

您可以通过字符串化来强制类型为字符串:

my $x = 3.1; # some variable containing a number
"$x";        # stringified
$x .= "";    # another, more awkward way to stringify
print $x;    # perl does it for you, too, quite often

顺便说一句,上面的这条评论正是关于你的 998 变成字符串的原因。

您可以通过 numifying 强制类型为数字:

my $x = "3"; # some variable containing a string
$x += 0;     # numify it, ensuring it will be dumped as a number
$x *= 1;     # same thing, the choice is yours.

【讨论】:

  • 我知道存在许多“黑客”来强制变量为整数,我很好奇为什么 perl 会做出这样的改变,因为我只是在一些操作中使用了我的变量,我没有做任何分配.
  • 再次重申:您在print 中使用过它。这会导致隐式字符串化。使用. 和任何其他操作字符串的运算符/函数也会导致这种情况。
  • 感谢您的回答,我现在明白这是预期的行为。
【解决方案2】:

Perl 标量可以同时保存一个字符串和一个标量值。其中任何一个都可以作为 JSON 值有效,因此如果有一个,则始终使用 字符串值

这是一个罕见的问题,因为大多数软件应该接受带引号或不带引号的数字表示。 JSON 文档中的所有内容最初都是一个字符串,如果要求模块在将数据交给您之前应用任意字符串 → 浮点转换,可能会丢失重要数据

但如果你真的需要写一个不带引号的数字值,那么你可以强制一个标量作为数字类型:只需向它添加零。

同样,如果一个标量变量只包含一个数字变量,那么您可以强制 Perl 仅通过将其用作字符串来生成和存储其等效字符串。对我来说最明显的方法是用引号括起来

此代码似乎可以执行您想要的操作

use strict;
use warnings 'all';
use feature 'say';

use JSON;

my ($score1, $score2) = (998, 999);

print "score1: $score1\n";

my %data = ( score1 => 0+$score1, score2 => 0+$score2 );

say encode_json(\%data);

输出

score1: 998
{"score1":998,"score2":999}

【讨论】:

  • This is a very rare problem, as most software will accept either a quoted or unquoted representation of a number 我不同意,如果我需要在 C/C++ 中使用该值会怎样?更多的代码行来确保字符串值确实是一个数字。
  • @OlegV.Volkov 是的,根据您回答中的信息,类型由“最近使用的上下文”确定。 (重新)分配是没有必要的。
  • 我也会争论这是否是“罕见”问题。如果您正在为公共 API 进行输出,您不知道另一方的语言是否以不同的方式处理字符串和数字。会是 Lua 在表键中对它们进行不同的处理吗?会不会是 C 语言,它只是强类型并且会发出嘶哑的声音?当你的意思是数字时,最好小心返回一个数字。
  • @AdrianToma:是的,有例外。但是如果看到field: "999"而不是field: 999,那么消耗JSON数据的程序就死了是不可原谅的
  • @OlegV.Volkov:“当你指的是数字时,请注意返回一个数字” 这不是那么简单。例如,邮政编码是数字吗?在 XML 中,一切都是字符串,要求 JSON 数据源(一种非常简单的序列化格式)在是否在数字数据周围加上引号时非常小心,这似乎很奇怪
猜你喜欢
  • 1970-01-01
  • 2010-09-24
  • 2010-09-09
  • 1970-01-01
  • 2014-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-05
相关资源
最近更新 更多