Perl способ получения значения, выраженного с помощью блока памяти - PullRequest
2 голосов
/ 06 февраля 2012

Я ищу способ сократить следующий фрагмент кода до одного выражения регулярного выражения:

if( $current_value =~ /(\d+)(MB)*/ ){
        $current_value = $1 * 1024 * 1024;
    }
    elsif( $current_value =~ /(\d+)(GB)*/ ){
        $current_value = $1 * 1024 * 1024 * 1024;
    }
    elsif( $current_value =~ /(\d+)(KB)*/ ){
        $current_value = $1 * 1024;
    }

Код выполняет оценку значения, которое может быть выражено как одно число (байты)., число и КБ (килобайты), с мегабайтами (МБ) и так далее.Есть идеи как уменьшить код блока?

Ответы [ 5 ]

4 голосов
/ 06 февраля 2012

Число :: Формат

use warnings;
use strict;

use Number::Format qw(format_bytes);
print format_bytes(1024), "\n";
print format_bytes(2535116549), "\n";

__END__

1K
2.36G
3 голосов
/ 06 февраля 2012

Вы можете настроить хеш следующим образом:

my %FACTORS = ( 'KB' => 1024, 'MB' => 1024**2, 'GB' => 1024**3 );

А затем проанализируйте текст следующим образом:

if ( $current_value =~ /(\d+)(KB|MB|GB)/ ) {
    $current_value = $1 * $FACTORS{$2};
}

В вашем примере регулярное выражение имеет *, что я не уверен, что вы намереваетесь, потому что * означает "ноль или более", и поэтому (+\d)(MB)* будет соответствовать 10 или 10MB или 10MBMB или 10MBMBMBMBMBMBMB.

1 голос
/ 07 февраля 2012

Вы можете сделать это в одном регулярном выражении, поместив фрагменты кода в регулярное выражение, чтобы по-разному обрабатывать три случая

my $r;

$current_value =~ s/
    (\d+)(?:
          Ki (?{ $r = $^N * 1024 })
        | Mi (?{ $r = $^N * 1024 * 1024 })
        | Gi (?{ $r = $^N * 1024 * 1024 * 1024 })
    )/$r/xso;
1 голос
/ 06 февраля 2012

Используя модифицированный код benzado, вот тест, который вы можете запустить, чтобы увидеть, работает ли он.

Мы советуем вам всегда помещать подобный код в метод многократного использования и написать для него небольшой модульный тест:

use Test::More;

plan tests => 4;

##
# Convert a string denoting '50MB' into an amount in bytes.
my %FACTORS = ( 'KB' => 1024, 'MB' => 1024*1024, 'GB' => 1024*1024*1024 );
sub string_to_bytes {
        my $current_value = shift;

        if ( $current_value =~ /(\d+)(KB|MB|GB)/ ) {
            $current_value = $1 * $FACTORS{$2};
        }
        return $current_value;
}

my $tests = {
        '50' => 50,
        '52KB' => 52*1024,
        '55MB' => 55*1024*1024,
        '57GB' => 57*1024*1024*1024
};

foreach(keys %$tests) {
        is( string_to_bytes($_),$tests->{$_},
            "Testing if $_ becomes $tests->{$_}");
}

Запуск этого дает:

$ perl testz.pl
1..4
ok 1 - Testing if 55MB becomes 57671680
ok 2 - Testing if 50 becomes 50
ok 3 - Testing if 52KB becomes 53248
ok 4 - Testing if 57GB becomes 61203283968

Теперь вы можете

  • Добавление дополнительных тестовых случаев (что происходит с БОЛЬШИМИ числами? Что вы хотите, чтобы произошло? Что за undef, для строк, когда kB записывается с маленьким k, когда вы встречаете kibiB или kiB или Kb?)
  • Преврати это в модуль
  • Написать документацию в POD
  • Загрузить модуль в CPAN

И вуаля!

0 голосов
/ 07 февраля 2012

Проблема с использованием KB для 1024 байтов.Кило в качестве префикса обычно означает 1000 единиц, а не 1024.

Проблема усугубляется с MB, поскольку она означает 1000*1000, 1024*1024 и 1000*1024.

* 1009.* На дискете объемом 1,44 МБ фактически хранится 1.44 * 1000 * 1024.

Единственный реальный выход из этого - использовать новый KiB (Кибибайт) для обозначения 1024 байта.


способ, которым вы реализовали это, также имеет ограничение, что вы не можете использовать 8.4Gi для обозначения 8.4 * 1024 * 1024.Чтобы убрать это ограничение, я использовал $RE{num}{real} из Regexp :: Common вместо \d+.


Некоторые другие ответы связывают соответствие, выписывая все возможныеМатчи.Это может стать очень утомительным, не говоря уже о склонности к ошибкам.Чтобы обойти это, я использовал ключи %multiplier для генерации регулярного выражения.Это означает, что если вы добавляете или удаляете элементы из %multiplier, вам не придется изменять регулярное выражение вручную.

use strict;
use warnings;
use Regexp::Common;

my %multiplier;
my $multiplier_match;
{

  # populate %multiplier
  my %exponent = (
    K => 1, # Kilo  Kibi
    M => 2, # Mega  Mebi 
    G => 3, # Giga  Gibi
    T => 4, # Tera  Tebi
    P => 5, # Peta  Pebi
    E => 6, # Exa   Exbi
    Z => 7, # Zetta Zebi
    Y => 8, # Yotta Yobi
  );
  while( my ($str,$exp) = each %exponent ){
    @multiplier{ $str,      "${str}B"  } = (1000 ** $exp) x2; # K  KB
    @multiplier{ "${str}i", "${str}iB" } = (1024 ** $exp) x2; # Ki KiB
  }
  # %multiplier now holds 32 pairs (8*4)

  # build $multiplier_match
  local $" #" # fix broken highlighting
    = '|';
  my @keys = keys %multiplier;
  $multiplier_match = qr(@keys);

}

sub remove_multiplier{
  die unless @_ == 1;
  local ($_) = @_;

  #  s/^($RE{num}{real})($multiplier_match)$/ $1 * $multiplier{$2} /e;
  if( /^($RE{num}{real})($multiplier_match)$/ ){
    return $1 * $multiplier{$2};
  }

  return $_;
}

Если вам абсолютно необходимо, чтобы 1K означал 1024, вам нужно изменить только одну строку.

# @multiplier{ $str, "${str}B"  } = (1000 ** $exp) x2; # K  KB
  @multiplier{ $str, "${str}B"  } = (1024 ** $exp) x2; # K  KB

Обратите внимание, что поскольку я использовал $RE{num}{real} из Regexp :: Common , он также будет работать с 5.3e1Ki.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...