2012-02-06 20 views
2

我在尋找一種方式來下面一段代碼減少到一次正則表達式語句:perl的方式與存儲單元表示

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)等等表示。任何想法如何減少塊代碼?

+2

首先,您的代碼不會按原樣運行。你在每個語句後加上'*',所以KB | MB | GB部分是可選的(0或更多)。你確定這是你想要的嗎? – Konerak 2012-02-06 16:42:01

回答

3

你可以建立一個哈希這樣的:

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

,然後分析這樣的文字:

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

在您的例子正則表達式有一個*這我不知道你打算,因爲*的意思是「零個或多個」,因此(+\d)(MB)*將匹配1010MB10MBMB10MBMBMBMBMBMBMB

+0

我會丟掉空字符串匹配;這將無法正常工作......但對於散列查找+1! – pavel 2012-02-06 16:44:53

+0

這個想法是有道理的,但是你的散列鍵是K/M/G,你的捕獲是KB/MB/GB - 你的代碼不會在散列中找到這些項。 – Konerak 2012-02-06 16:49:25

+1

'([KMG] B)'更有趣,儘管不可讀。 – TLP 2012-02-06 17:02:09

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寫成時H小K,當你遇到kibiB或昆明植物或KB?)
  • 變成一個模塊中的POD
  • 寫文檔上傳該模塊CPAN

,瞧!

+0

CPAN已經有一些產品:'Number :: Format'和'Format :: Human :: Bytes' – toolic 2012-02-06 17:08:17

+0

非常好!您可以**保留整批測試**,但只需將對string_to_bytes函數的調用替換爲模塊的適當調用即可。現在你可以看到模塊是否做了你想做的事情。 – Konerak 2012-02-06 19:55:30

4

Number::Format

use warnings; 
use strict; 

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

__END__ 

1K 
2.36G 
+1

是的,這個模塊似乎是做這個工作的。 'unformat_number(「4K」,base => 1024)產生4096'。 – Konerak 2012-02-06 18:44:40

1

有一個問題用你能做到這一點在一個正則表達式,通過把代碼snippits 正則表達式來處理的三種情況不同

my $r; 

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

我喜歡你用'Ki'而不是'KB'。特別是因爲'KB'意味着1000字節和1024字節。 (['MB'](http://en.wikipedia.org/wiki/Megabyte)更糟糕,它意味着1000 * 1000,1024 * 1024和1000 * 1024) – 2012-02-07 00:30:18

+0

那麼OP有兩種錯誤。首先,OP將KB轉換爲(無量綱)係數N.現在KB不僅是因子N,而是「B」(可能是字節)數的因子,因此它們造成了尺寸不匹配。其次,OP的K含義爲1024,即「Eee,當我還是一個男孩時,男人是男人,而K是1024,這就是它應該是」腦殘的想法「。我碰巧解決了這兩個問題。 @Brad_Gilbert:順便說一下,你已經神奇地將B變成了B,這對我來說是有問題的,因爲在我看來B意味着字節和B意味着位... – zgpmax 2012-02-07 00:41:48

+1

當然,應該真的使用一個CPAN模塊,如Number: :格式。在單一的正則表達式中做更多的是Perl編程練習。 (類似的技術在Gianni Ceccarelli在倫敦Perl Mongers技術會議2012-01-26的演講中得到證實。) – zgpmax 2012-02-07 00:45:45

0

KB 1024字節。基洛作爲前綴通常意味着的東西,因爲它意味着1000*10001024*10241000*1024不是1024

問題更糟與MB得到1000。

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}Regexp::Common它還將與5.3e1Ki工作。