perl获取用内存单元表示的值的方法

perl获取用内存单元表示的值的方法,perl,Perl,我正在寻找一种方法,将以下代码段简化为单个regexp语句: if( $current_value =~ /(\d+)(MB)*/ ){ $current_value = $1 * 1024 * 1024; } elsif( $current_value =~ /(\d+)(GB)*/ ){ $current_value = $1 * 1024 * 1024 * 1024; } elsif( $current_value =~ /(

我正在寻找一种方法,将以下代码段简化为单个regexp语句:

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(KB)、MB等的值执行求值。您知道如何减少块代码吗?

您可以设置如下哈希:

my %FACTORS = ( 'KB' => 1024, 'MB' => 1024**2, 'GB' => 1024**3 );
if ( $current_value =~ /(\d+)(KB|MB|GB)/ ) {
    $current_value = $1 * $FACTORS{$2};
}
然后像这样解析文本:

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


您可以在oneregexp中执行此操作,方法是将代码snippits放在regexp中,以不同的方式处理这三种情况

my $r;

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

对1024字节使用
KB
有问题。作为前缀的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
。为了消除这个限制,我使用了
$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

请注意,由于我使用了它中的
$RE{num}{real}
,因此它也可以与
5.3e1Ki

一起工作。首先,您的代码不能正常工作。您将
*
放在每条语句之后,因此KB | MB | GB部分是可选的(0或更多)。你确定这就是你想要的吗?我会放弃空字符串匹配;那不行。。。但是对于散列查找+1!这个想法很好,但是你的散列键是K/M/G,捕获是KB/MB/GB-你的代码在散列中找不到项目。另外,
1024*1024*1024
=
1024**3
@Konerak谢谢你的修复,我在发布后编辑了代码,没有运行单元测试。:-)是的,这个模块似乎完成了任务<代码>非格式化数字(“4K”,基数=>1024)产生4096个。太棒了!您可以保留整批测试,但只需将对string_to_bytes函数的调用替换为对模块的适当调用即可。现在您可以看到模块是否实现了您希望它实现的功能。我喜欢您使用
Ki
而不是
KB
。特别是因为
KB
意味着1000字节和1024字节。(更糟糕的是,它意味着1000*1000、1024*1024和1000*1024)好吧,OP有两种错误的方式。首先,OP将KB转换为一个(无量纲)因子N。现在KB不仅仅是一个因子N,而是一个数量为“B”(大概是字节)的因子,因此他们造成了维度不匹配。第二,OP有K的意思是1024,也就是“Eee,当我还是个孩子的时候,男人都是男人,K是1024,这就是为什么它应该是”大脑受损的思维。我碰巧顺便解决了这两个问题@Brad_Gilbert:顺便说一句,你神奇地把B变成了B,这对我来说是有问题的,因为在我看来,B意味着字节,B意味着位……当然,人们应该真正使用CPAN模块,比如Number::Format。在单个正则表达式中执行它更像是一个Perl编程练习。(Gianni Ceccarelli在2012-01-26伦敦Perl Mongers技术会议上的一次演讲中展示了一种类似的技术。)您真的应该使用
(?{local$r=…
(在这种情况下,这并不重要。)