メモリユニットで表現された値を取得するperlの方法

私は次のコードを1つの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(キロバイト)、メガバイト(MB)などで表すことができる値の評価を実行します。どのようにブロックコードを減らすための任意のアイデア?

2
まず第一に、あなたのコードはそのまま動作しません。各文の後ろに * を置くので、KB | MB | GB部分はオプションです(0以上)。あなたはそれがあなたが望むものだと確信していますか?
追加された 著者 Konerak,

5 答え

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
4
追加された
うん、このモジュールは仕事をしているようだ。 unformat_number( "4K"、base => 1024)は4096を返します。
追加された 著者 Konerak,

次のようにハッシュを設定することができます:

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

そして、このようにテキストを解析する:

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

あなたの例では、 * は "0以上"を意味するので、正規表現にはあなたが意図していないと思われる * があるので、(+ \ d)( MB)* 10 または 10MB または 10MBMB または 10MBMBMBMBMBMB と一致します。

3
追加された
([KMG] B)は読みやすくはありませんが、さらに魅力的です。
追加された 著者 TLP,
また、 1024 * 1024 * 1024 = 1024 ** 3
追加された 著者 TLP,
@Konerak修正していただきありがとうございます。投稿後にコードを編集し、単体テストを実行しませんでした。 :-)
追加された 著者 benzado,
アイデアは健全ですが、あなたのハッシュキーはK/M/Gです。あなたのキャプチャはKB/MB/GBです。あなたのコードはハッシュの項目を見つけられません。
追加された 著者 Konerak,
私は空の文字列の一致を削除します。それは動作しません...しかし、ハッシュのルックアップ+1!
追加された 著者 pavel,

ベンダドの修正コードを使用して、動作するかどうかを調べるためのテストです。

このようなコードを再利用可能なメソッドに入れることをお勧めします。小さな単位テストを書く:

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

今できること

  • テストケースを追加する(BIG番号はどうなりますか?何が起こりますか?undef、文字列、kBが小さなkで書かれたとき、kibiBまたはkiBまたはKbと出会うとき)
  • これをモジュールにする
  • PODでドキュメントを書く
  • モジュールをCPANにアップロードする

とボイル!

1
追加された
CPANにはすでにいくつかの製品があります: Number :: Format Format :: Human :: Bytes
追加された 著者 toolic,
優秀な! 一括してテストを続けることができますが、string_to_bytes関数の呼び出しを、モジュールへの適切な呼び出しで置き換えてください。これでモジュールがあなたがしたいことをしているかどうかを見ることができます。
追加された 著者 Konerak,

コードスニペットを3つのケースを別々に処理する正規表現を入れて 1つの正規表現で行うことができます

my $r;

$current_value =~ s/
    (\d+)(?:
          Ki (?{ $r = $^N * 1024 })
        | Mi (?{ $r = $^N * 1024 * 1024 })
        | Gi (?{ $r = $^N * 1024 * 1024 * 1024 })
    )/$r/xso;
1
追加された
私はあなたが KB の代わりに Ki を使用したのが好きです。特に KB は1000バイトと1024バイトの両方を意味するためです。 ( MB はさらに悪く、1000 * 1000、 1024 * 1024、および1000 * 1024)
追加された 著者 Brad Gilbert,
あなたは本当に(?{local $ r = ... )を使用していたはずです(この場合、それは問題ではありません)。
追加された 著者 Brad Gilbert,
さて、OPには2つの方法が間違っていました。まずKBはKBを(無次元の)因数Nに変換していました。KBは単なるN因子ではなく、いくつかの 'B'(おそらくはバイト)の因数であり、次元の不一致が生じました。第2に、OPはKを意味します。1024は「私が子供のときは男性、男性の場合は1024、Kが1024の場合は脳が傷ついた考え方です。私は両者の問題を解決するために起こった。 @Brad_Gilbert:脇の下として、あなたは魔法のようにBをbに変えました。私の考えでは、Bはバイトを意味し、Bはビットを意味するので疑問です。
追加された 著者 zgpmax,
もちろん、Number :: FormatのようなCPANモジュールを実際に使うべきです。単一の正規表現でそれを行うことは、Perlプログラミングの練習のほうがはるかです。 (同様の手法が、2012-01-26ロンドンPerl Mongers技術会議のGianni Ceccarelli氏の講演で実演されました。)
追加された 著者 zgpmax,

1024バイトに KB を使用する際に問題があります。接頭辞としてのキロは、一般的に1024ではないことを意味します。

1000 * 1000 1024 * 1024 1000 * 1024 MB >。

1.44 MBのフロッピーは、実際に 1.44 * 1000 * 1024 を保持します。

このうち唯一の実際の方法は、新しい KiB (Kibibyte)を1024バイトを使用することです。


実装した方法にも 8.4Gi を使用して 8.4 * 1024 * 1024 を使用できないという制限があります。この制限を取り除くために、私は Regexp :: Regexpから$ RE {num} {real} を使用しました。 \ d + の代わりに共通を使用します。


他の答えのいくつかは、すべての可能性のある試合を書き出すことによって試合をハードワイヤします。エラーが発生しやすいことは言うまでもありません。この問題を回避するために、正規表現を生成するために%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を意味することが絶対必要な場合は、1行だけを変更する必要があります。

# @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 でも動作します。

0
追加された