【问题标题】:Need help with "pack" for perl and php需要有关 perl 和 php 的“打包”的帮助
【发布时间】:2009-11-24 21:52:32
【问题描述】:

我的任务是将某人用 perl 制作的 crypt 函数转换为 php 代码。一切正常,除了这个:

Perl:

$wert = Encode::encode( "utf8", $wert );
$len=length $wert;
$pad = ($len % 16)?"0".chr(16 - ($len % 16)):"10";
$fuell = pack( "H*", $pad x (16 - $len % 16));

PHP:

$wert = utf8_encode($wert);
$len = mb_strlen($wert);
$pad = ( $len%16 ) ? '0'.chr(16 - ($len%16)) : '10';
$fuell = pack("H*", str_repeat($pad, (16 - $len % 16)));

php 版本适用于某些字符串。但是当我有类似 '2010-01-01T00:00:00.000' 的东西时,perl 版本可以正常工作,并且 php 版本会打印“PHP Warning: pack(): Type H: invalid hex digit”。

如果有人能发现 php 版本中的错误,我将不胜感激。

编辑:

这是我要转换成 php.ini 的完整函数。它是由一家不再为我们工作的公司的程序员制作的,所以我真的不知道最初的意图是什么。

sub crypt
{
    my $self = shift;
    my ($wert,$pw)= @_;
    $wert = Encode::encode( "utf8", $wert );
    $pw = Encode::encode( "utf8", $pw );
    $len=length $wert;
    $pad = ($len % 16)?"0".chr(16 - ($len % 16)):"10";
    $fuell = pack( "H*", $pad  x (16 - $len % 16));
    $wert=$wert.$fuell;
    $lenpw=length $pw;
    $fuell = ($lenpw % 16)? pack ("H*", "00" x (16 - $lenpw % 16)):"";
    $pw=$pw.$fuell;
    $cipher = new Crypt::Rijndael $pw, Crypt::Rijndael::MODE_CBC;
    $cipher->set_iv($pw);
    $crypted = encode_base64($cipher->encrypt($wert),"");

    return $crypted;
}

【问题讨论】:

标签: php perl pack


【解决方案1】:

看起来错误实际上出现在 both 版本中。格式代码H 查找一个十六进制数字,并且如 PHP 错误中所述,它没有找到(合法的)一个。罪魁祸首似乎是这个表达式:

chr(16 - ($len % 16))

Perl 版本没有抱怨,因为 Perl 版本的 pack 将转换字符,无论它是否是十六进制数字(这可能不是您想要的)。 documentation 更详细地介绍了实际发生的情况。

为防止出现错误,请尝试以下操作:

sprintf('%x', 16 - ($len % 16))

注意:虽然这应该可以解决您遇到的错误,但我不知道这是否是一个可接受的解决方案,因为我不知道 Perl 代码原作者的确切意图。

【讨论】:

  • +1 指出无效字符来自对chr 的调用,尽管我不确定sprintf 是否会做他想做的事情,因为正如你所说,我们不知道 Perl 代码作者的意图。
  • @Adam 我对是否应该包含 sprintf 有点犹豫。它真的看起来像原作者打算让表达式返回一个十六进制数字,所以我想我会包括它。编辑以减少对修复的重视,而更多地关注警示性说明。
  • @bish:实际上,看看 Perl 在我的测试中的行为,'sprintf()' 解决方案应该可以工作。原因是 Perl 会将每个字符 chr(1)chr(15) 视为具有较小的十六进制数字值,这当然是相同的。编码为“A”以上的字符的行为切换,但示例代码无法达到那么高。我建议 OP 应该尝试一下,看看它是否确实为 Perl 提供了相同的输出。
  • @Adam 好点。我没有完全意识到这一点。阅读代码后,我的脑海中看到了 chr(0) - chr(15) 与 pack('H*') 的结合,并立即将其标记为问题,因为对 chr 的调用不会产生十六进制字符。如果 sprintf 在这种情况下确实适用于 OP,我建议在 PHP 和 Perl 版本中使用它,或者至少在 Perl 版本中添加注释以明确使用 pack 的非直观行为的意图。我想我的头脑不像拉里那样工作。 =)
  • @bish:非常感谢! sprintf 似乎做得很好!
【解决方案2】:

似乎pack() 的 Perl 实现可以容忍输入字符串中的无效十六进制数字,而 PHP 版本显然不能。

考虑:

print pack("H*", "ZZ");

这会在 Perl 中打印 3(出于某种原因),但会导致您在 PHP 中提到的错误。

我不确定 Perl 究竟对这些“数字”做了什么,但它绝对不同于 PHP。

编辑: 看起来,Perl 实际上会将十六进制数字域向前“滚动”到字符集中。那就是:

0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ  #-- Give this to Perl...
0123456789ABCDEF0123456789ABCDEF0123  #-- .. and it's treated as this hex digit

因此,“ZZ”与“33”相同,这就是它打印3 的原因。请注意,根据文档,此行为定义不明确。因此,Perl 中的原始实现可以被认为是有缺陷的,因为它依赖于未明确定义的行为。

【讨论】:

  • 这是 Perl 中的已知错误/限制吗?似乎应该可以修复或向pack() 实现添加额外的错误条件。
  • @Ether:我认为这只是一个简单的实现,适用于有效的十六进制数字,并且碰巧溢出到字符集的其余部分。他们没有大声抱怨无效的十六进制数字,而是记录了该行为没有明确定义。他们这么说的事实意味着行为可能有一天会改变。我不知道为什么没有。
猜你喜欢
  • 2021-02-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-23
  • 2014-12-26
  • 2013-08-12
  • 2014-06-11
  • 1970-01-01
相关资源
最近更新 更多