Есть ли более эффективный способ генерации случайного файла в Perl?

Это мой первый Perl script. Когда-либо:

#!/usr/bin/perl
if ($#ARGV < 1) { die("usage: <size_in_bytes> <file_name>\n"); }
open(FILE,">" . $ARGV[0]) or die "Can't open file for writing\n";
# you can control the range of characters here
my $minimum = 32;
my $range = 96;
for ($i=0; $i< $ARGV[1]; $i++) {
 print FILE chr(int(rand($range)) + $minimum);
}
close(FILE);
</file_name></size_in_bytes>

Его цель - сгенерировать файл в указанном размере, заполненном случайными символами.

Он работает, но он довольно медленный. Для записи 10 МБ случайного файла требуется несколько секунд. У кого-нибудь есть предложения/советы о том, как сделать это быстрее/лучше? Также не стесняйтесь указывать общие ошибки новичков.

3 ответа

  • Вы можете запросить rand создать для вас несколько значений каждый раз, когда вы его вызываете.
  • Соберите несколько символов перед вызовом print. Печать одного символа за раз неэффективна.

 

for (my $bytes = 0; $bytes < $num_bytes; $bytes += 4) {
 my $rand = int(rand($range ** 4));
 my $string = '';
 for (1..4) {
 $string .= chr($rand % $range + $minimum);
 $rand = int($rand / $range);
 }
 print FILE $string;
}


Запись данных потока из /dev/random.

#!/usr/bin/perl
use File::Copy;
if ($#ARGV < 1) { die("usage: <size_in_bytes>\n"); }
copy("/dev/random","tmp", $ARGV[0]) or die "Copy failed: $!";
</size_in_bytes>

не проверен.

Edit: Поскольку вы хотите диапазон, сделайте это.

Ваш диапазон составляет от 96 до 32, что составляет 64. 64 = 01000000b (0x40 в шестнадцатеричном формате). Просто сгенерируйте свои цифры и заготовьте побитовое И против числа, которое является диапазоном значений, которые должны быть сгенерированы-1, и добавьте нижнюю границу, предварительно сформировав побитовое ИЛИ с его значением (00100000b или 0x20)

Это позволит вам делать что-то вроде любой случайной строки (просто прочитайте raw hex из /dev/random ) и преобразуйте данные в ваш диапазон.


Если вам нужны случайные числа из диапазона, я не знаю более эффективного способа. Ваш script настроен на мои предпочтения:

#!/usr/bin/perl
use warnings;
use strict;
die("usage: $0 <size_in_bytes> <file_name>\n") unless @ARGV == 2;
my ($num_bytes, $fname) = @ARGV;
open(FILE, ">", $fname) or die "Can't open $fname for writing ($!)";
my $minimum = 32;
my $range = 96;
for (1 .. $num_bytes) {
 print FILE pack( "c", int(rand($range)) + $minimum);
}
close(FILE);
</file_name></size_in_bytes>

Я использую pack("c"), когда мне действительно нужен бинарный файл. chr() тоже может быть прекрасным, но IIRC действительно зависит от того, что использует символ, кодирующий вашу среду (думаю, ASCII vs. utf8.)

Кстати, если вам действительно нужен двоичный файл для совместимости с Windows, вы можете добавить binmode FILE; после open.

В противном случае, если диапазон не является обязательным, вы можете просто dd if=/dev/random of=$filename bs=1 count=$size_of_the_output (или в Linux быстрее crypto-unsafe /dev/urandom). Но это будет намного медленнее, поскольку /dev/random действительно пытается доставить реальные случайные биты - по мере их появления. И если их недостаточно (например, ваша платформа не имеет H/W RNG), то производительность действительно пострадает - по сравнению с невероятно быстрым генератором псевдослучайных чисел libc (Perl использует внутренне для реализации rand()),.

licensed under cc by-sa 3.0 with attribution.