获取用内存单位表示的值

问题描述 投票:0回答:5

我正在寻找一种方法将以下代码片段减少为单个正则表达式语句:

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;
    }

代码对可以表示为单个数字(字节)、数字和 KB(千字节)、兆字节(MB)等的值进行评估。如何减少代码块?

perl
5个回答
5
投票

数字::格式

use warnings;
use strict;

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

输出:

1K
2.36G

4
投票

你可以像这样设置一个哈希:

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
投票

使用 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

瞧!


1
投票

您可以在one正则表达式中完成此操作,方法是将代码片段放在正则表达式中以不同方式处理这三种情况

my $r;

$current_value =~ s/
    (\d+)(?:
          Ki (?{ $r = $^N * 1024 })
        | Mi (?{ $r = $^N * 1024 * 1024 })
        | Gi (?{ $r = $^N * 1024 * 1024 * 1024 })
    )/$r/xso;

0
投票

使用

KB
表示 1024 字节有问题。 Kilo 作为前缀通常表示 1000 个东西,而不是 1024。

使用

MB
时问题会变得更糟,因为它意味着
1000*1000
1024*1024
1000*1024

一张 1.44 MB 的软盘实际上可以容纳

1.44 * 1000 * 1024

解决这个问题的唯一真正方法是使用新的

KiB
(Kibibyte)来表示 1024 字节。


您实现它的方式也有限制,您不能使用

8.4Gi
来表示
8.4 * 1024 * 1024
。为了消除该限制,我使用
Regexp::Common
中的 $RE{num}{real} 而不是
\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

请注意,由于我使用了

Regexp::Common
中的 $RE{num}{real},它也适用于
5.3e1Ki

© www.soinside.com 2019 - 2024. All rights reserved.