在Perl中,我如何生成由8个十六进制数字组成的随机字符串?

16

使用Perl编写,不使用任何未随ActivePerl捆绑的额外模块,如何创建一个由0-F组成的8个字符的字符串,例如0F1672DA?填充应该可以控制,并且最好是恰好8个字符。

我想生成的字符串类型的更多示例:

28DA9782
55C7128A

7
高尔夫:perl -e'print[0..9,A..F]->[rand 16]for 1..8' - daxim
2
为什么要踩我?我确实需要学习编程。如果不是为了这些问题,Stackoverflow还有什么用处呢? - unixman83
4
我没有踩你的帖子。然而,通常情况下,排除任何CPAN模块都不会受到好评。此外,你应该努力提出自己的解决方案,并要求帮助而不是期望别人给你现成的解决方案。 - Sinan Ünür
2
CPAN在ActiveState上很麻烦。 :) 我推荐Strawberry Perl。 - Quentin
@Quentin - 我在AS Perl上使用CPAN并没有遇到太多问题。他们通过ppm(ppm inst dmakeppm inst mingw)提供C编译器和dmake。我更喜欢使用cpanm直接从cpan安装模块(ppm inst App::cpanminus)。 - bvr
5个回答

23

原则上来说,你应该能够做到

#!/usr/bin/env perl

use strict; use warnings;

for (1 .. 10) {
    printf "%08X\n", rand(0xffffffff);
}

但是,你可能会发现 - 至少在一些系统上使用某些perl(如果不是全部),rand的范围仅限于32,768个值

您也可以研究String::Random的源代码,了解如何生成满足其他条件的随机字符串。

然而,我仍然警告不要在Windows系统上使用内置的rand。请参阅Math::Random::MT以获得高质量的随机数生成器。

#!/usr/bin/env perl

use strict; use warnings;

my @set = ('0' ..'9', 'A' .. 'F');
my $str = join '' => map $set[rand @set], 1 .. 8;
print "$str\n";

PS:在 Perl 5.20 中已经修复了 Windows 上的 rand 的问题:(参见)

这意味着 perl 随机数的质量将因平台而异。例如,Windows 上的 rand() 只有15位,而 POSIX 平台(如具备 drand48() 函数的 Linux)上的 rand() 则可达到 48 位。

现在,Perl 在所有平台上都使用自己内部的 drand48() 实现。但这并不会使 perl 的 rand 具有密码学安全性。[perl #115928]


肯定有一个随附在ActivePerl中的插件可以克服rand的限制吧? - unixman83
1
一个解决方案是每次使用 rand 生成一个数字?你的第二个示例是否这样做了? - unixman83
1
是的,第二种方法确实使用可适应的 @set(类似于 @Oleg 的方法,我投了赞成票)一位数字一个字符地生成字符串。 - Sinan Ünür

12

通用示例,允许任何字符范围:

my @chars = ('0'..'9', 'A'..'F');
my $len = 8;
my $string;
while($len--){ $string .= $chars[rand @chars] };
print "$string\n";

6
使用sprintf将数字转换为十六进制。
$foo .= sprintf("%x", rand 16) for 1..8;

5
sprintf("%08X", rand(0xFFFFFFFF))

一些人提到了 rand 函数的 windows 限制,其最大值为 rand(0x7FFF) 或 rand(32768) 十进制。我将使用二进制位移运算符 '<<' 来解决此问题。

# overcomes the windows-rand()-only-works-with-max-15bit-(32767)-limitation:
#   needed 8*4==32bit random-number:
#     first get the 15 high-significant bits shift them 17bits to the left,
#     then the next 15bits shifted 2 bits to the left,
#     then the last 2 bits with no shifting:
printf( '%08X', (
    (rand(0x8000)<<17) + (rand(0x8000)<<2) + rand(0b100) )
      );

但我认为这只是学术上的,因为这段代码非常笨拙,难以理解。
在实际生产中,我不会使用它,除非速度最重要。
但也许这是最快的解决方案,并且演示了一个克服Windows下rand()函数限制的模式...


3
我的陈述略微含糊:“至少在某些Windows系统上,rand的范围可能被限制为32,768个值。” - Sinan Ünür
1
我认为Sinan Ünür应该提交一个错误报告,如果这是真的。rand()返回值仅在32768个不同数字的集合中。perldoc -f告诉我rand应该返回一个分数。根据文档和我的测试,我认为我的解决方案是正确的。 - Lutz L.
3
为什么你不能提交一个缺陷报告? :) - brian d foy
@LutzL。我相信Sinan的话,而且基于ActiveState在Windows下的表现,老实说,我也这么认为。你怎么能在Linux下工作呢?你一定是有很差的硬件(或超级计算机 ;))。 - unixman83
1
Lutz和@briandfoy,问题出在rand的Windows实现上:rand函数返回一个范围在0到RAND_MAX(32767)之间的伪随机整数 这是一个平台限制。即使将来某个版本的perl改用其他的rand,原则仍然不变:如果你关心rand的输出结果,那么请使用具有良好定义属性的东西。 - Sinan Ünür
显示剩余3条评论

1

来自的“高尔夫”评论:

perl -e'print[0..9,A..F]->[rand 16]for 1..8'

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