将pack转换为Perl6

8
我想将以下内容从perl5转换为perl6:
$salt = pack "C*", map {int rand 256} 1..16;

它创建一个由16个字符组成的字符串,每个字符的值从0到255随机选择。Perl5对这些字符不赋予任何语义,因此它们可以是字节、Unicode代码点或其他内容。
我认为我可以应付。
$salt = (map {(^256).pick.chr},^16).join;

但我在使用pack时遇到了困难,以下是我的尝试:

use experimental :pack;

my $salt = pack("C*",(map {(^256).pick} , ^16)).decode('utf-8');

say $salt;
say $salt.WHAT;

结果可以是错误,

Malformed termination of UTF-8 string  
      in block <unit> at test.p6 line 3

或者类似于这样,
  j
  (Str)

我的思路是将整数List打包后返回一个Buf,然后对其进行解码就可以得到所需的Str更新: 正如评论和答案所建议的那样,Buf 是正确的对象。现在要跟进处理pack部分。
perl6 -e 'use experimental :pack; my $salt = pack("C*",(map {(^256).pick} , ^16));say $salt;say $salt.WHAT;'

缓冲区: 0x<7D> (缓冲区)

仅打包一个单元

另一方面,使用P5pack(Scimon建议的)会返回错误

perl6 -e 'use P5pack; my $salt = pack("C*",(map {(^256).pick} , ^16));say $salt;say $salt.WHAT;'
Cannot convert string to number: base-10 number must begin with valid digits or '.' in '⏏*' (indicated by ⏏)
  in sub one at /home/david/.rakudobrew/moar-master/install/share/perl6/site/sources/D326BD5B05A67DBE51C279B9B9D9B448C6CDC401 (P5pack) line 166
  in sub pack at /home/david/.rakudobrew/moar-master/install/share/perl6/site/sources/D326BD5B05A67DBE51C279B9B9D9B448C6CDC401 (P5pack) line 210
  in block <unit> at -e line 1

更新2:

我没有注意到区别。

perl6 -e 'say (map {(^256).pick}, ^16).WHAT;'
(Seq)
perl6 -e 'say Buf.new((^256).roll(16)).WHAT;'  
(Buf)

现在请将它们制成列表形式,
perl6 -e 'use experimental :pack; my $salt = pack("C*",(Buf.new((^256).roll(16)).list));say $salt;say $salt.WHAT;'
    Buf:0x<39>
    (Buf)

并且
perl6 -e 'use P5pack; my $salt = pack("C*",Buf.new((^256).roll(16)).list);say $salt;say $salt.WHAT;'
    Cannot convert string to number: base-10 number must begin with valid digits or '.' in '⏏*' (indicated by ⏏)
      in sub one at /home/david/.rakudobrew/moar-master/install/share/perl6/site/sources/D326BD5B05A67DBE51C279B9B9D9B448C6CDC401 (P5pack) line 166
      in sub pack at /home/david/.rakudobrew/moar-master/install/share/perl6/site/sources/D326BD5B05A67DBE51C279B9B9D9B448C6CDC401 (P5pack) line 210
      in block <unit> at -e line 1

参考资料:

缓冲区和二进制IO

Perl 6中pack/unpack的第一次尝试

感谢您的帮助。


1
你实际上想做什么?如果你想从前256个Unicode代码点中随机选择一个,使用decode('iso-8859-1')。虽然这是一件奇怪的事情,但你为什么认为它不应该是一个Buf呢?如果你试图生成一个(不安全的)加密密钥,那么你正在生成任意字节,而Buf将是适当的类型。 - ikegami
1
@ikegami,感谢您的评论。>> 您实际上想要做什么?-我在Rosetta Code的任务中看到了P5行,并希望将其翻译为P6作为学习过程。 - hkdtam
1
一个笔记 ^256.roll(16) 应该可以给你16个随机数字,而不需要映射。等我起来后会进一步查看。 - Scimon Proctor
@Scimon 谢谢,你说得对,请看我的更新2。 - hkdtam
3个回答

8
如ikegami在评论中所说,你真的应该使用Buf,它基本上是一个字节串。
my $salt = Buf.new((^256).roll(16));

你可以像下面这样将它写入文件:

spurt 'foo', $salt, :bin;

或者使用以下方式将其编码为base-64:
use MIME::Base64;
my $encoded = MIME::Base64.encode($salt);

但如果您需要更加安全的保障,请查看Crypt::Random

use Crypt::Random;
my $salt = crypt_random_buf(16);

5

谢谢你的建议,请看我的更新。也许我使用方法不正确? - hkdtam

2

显然,"C" x 16 是有效的,但是"C*" 却不行。不要问为什么,我也不知道。:-D

perl6 -e 'use experimental :pack; my $salt = pack("C" x 16,(Buf.new((^256).roll(16)).list));say $salt;say $salt.WHAT;'
Buf:0x<64 71 D4 E6 E6 AD 7B 1C DD A2 62 CC DD DA F3 08>
(Buf)

另一方面,拆包确实可行。
perl6 -e 'use experimental :pack; my $salt = pack("C" x 16,(Buf.new((^256).roll(16)).list)).unpack("C*");say $salt;say $salt.WHAT;'
(35 101 155 237 153 126 109 193 94 105 70 111 59 51 131 233)
(List)

总的来说,mscha的答案很简洁明了,大约是P6级别的。将一个Buf列表打包成一个Buf看起来有些愚蠢。
至于其他的打包转换,请注意这里链接中的两个关键点。
Here is the difference between Perl 5 and Perl 6 pack/unpack:

Perl 5                      Perl 6

pack(List)  --> Str         pack(List)  --> Buf
unpack(Str) --> List        unpack(Buf) --> List

同时,一些Perl 5模板规则假设Buf和Str之间存在一个简单的双向通道。在Perl 5中,Buf和Str之间实际上没有真正的区别,而且Perl 5经常利用这一点。

编辑:修正打字错误,s/masha/mscha/;


1
s/mascha/mscha/ - mscha
抱歉,打字错误已经修正。 - hkdtam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接