Проблема с использованием KB
для 1024 байтов.Кило в качестве префикса обычно означает 1000 единиц, а не 1024.
Проблема усугубляется с MB
, поскольку она означает 1000*1000
, 1024*1024
и 1000*1024
.
* 1009.* На дискете объемом 1,44 МБ фактически хранится
1.44 * 1000 * 1024
.
Единственный реальный выход из этого - использовать новый KiB
(Кибибайт) для обозначения 1024 байта.
способ, которым вы реализовали это, также имеет ограничение, что вы не можете использовать 8.4Gi
для обозначения 8.4 * 1024 * 1024
.Чтобы убрать это ограничение, я использовал $RE{num}{real}
из Regexp :: Common вместо \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
.