我正在寻找一种方法,将以下代码简化为一条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)等的值进行评估。如何减少代码块?
编号::格式化
use warnings;
use strict;
use Number::Format qw(format_bytes);
print format_bytes(1024), "n";
print format_bytes(2535116549), "n";
__END__
1K
2.36G
您可以设置这样的散列:
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
匹配。
使用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
现在你可以了
- 添加更多的测试用例(BIG数字会发生什么?你想发生什么?undef、字符串会发生什么,当kB用小k写时,当你遇到kibiB、kiB或kB时?)
- 把它变成一个模块
- 在POD中编写文档
- 将模块上传到CPAN
瞧!
您可以在一个正则表达式中执行此操作,方法是将代码snippits放在正则表达式内,以不同的方式处理这三种情况
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.44MB的软盘实际上可以容纳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
一起使用。