实际数字转换为易读的值

9

我有一些以字节为单位的数据。我需要在图表上将这些值绘制为人类可读的标签(例如 2.5KB、14MB 等),并需要帮助实现函数(输入数据-实际值,输出-人类可读字符串)。

我已经编写了这样的函数,但我想要更优雅的实现方法。

function tickFormatter(value, type) {

    var suffix = (type == "bytes") ? ['B', 'KB', 'MB', 'GB'] : ['', 'K', 'M', 'G']

    if(value > (1024 * 1024 * 1024 * 1024)) {
        return (value / (1024 * 1024 * 1024 * 1024)).toFixed(2) + suffix[3]
    } else if(value > (1024 * 1024 * 1024)) {
        return (value / (1024 * 1024 * 1024)).toFixed(2) + suffix[2]
    } else if (value > (1024 * 1024)) {
        return (value / (1024 * 1024)).toFixed(2) + suffix[1]
    } else {
        return value.toFixed(2) + suffix[0]
    }
}
8个回答

28

我喜欢这个实现:简洁明了:

function readablizeBytes(bytes) {
    var s = ['bytes', 'kB', 'MB', 'GB', 'TB', 'PB'];
    var e = Math.floor(Math.log(bytes) / Math.log(1024));
    return (bytes / Math.pow(1024, e)).toFixed(2) + " " + s[e];
}

使用方法:

readablizeBytes(10000000)
"9.54 MB"

我不希望把这归功于我。


2
我不会为此负责。谁会为此负责? - yckart
2
'e' 代表什么? - chovy
kB 表示 2^10 字节,MB 表示 (2^10)^2 字节... - micred
1
如果你将第三行改为 var e = Math.floor(Math.log(Math.abs(value)) / Math.log(1000));,那么你就能够显示负数值了。 - gor
2
这些技术上不是KiB/MiB等,而不是KB/MB等吗? - Thoronwen
自然之道:创造,但不要为此而取得荣誉... 到目前为止最好的解决方案。 - Rodrigo

18

我使用的方法是这样的。它会将值四舍五入到最接近的单位,因此1000将显示为“0.98KB”。 如果您不想这样做,请将第一个Math.round更改为floor。

var SizePrefixes = ' KMGTPEZYXWVU';
function GetHumanSize(size) {
  if(size <= 0) return '0';
  var t2 = Math.min(Math.round(Math.log(size)/Math.log(1024)), 12);
  return (Math.round(size * 100 / Math.pow(1024, t2)) / 100) +
    SizePrefixes.charAt(t2).replace(' ', '') + 'B';
}

但是你对X、W、V和U后缀的来源有什么依据?维基百科上没有提到它们。 - Janus Troelsen
1
Xona,Weka,Vunda,Uda...这些不是正式的术语,但你可以在很多地方找到它们,比如:http://jimvb.home.mindspring.com/unitsystem.htm。不过,如果你处于那个规模,除了名称之外,你还有其他问题需要考虑 :) - Amir
(1 << 30) * 1000 => '0.98TB',加上 Math.floor()(1 << 30) * 1000 => '1000GB'。我期望得到 '1TB' - yckart
相反地,@Amir,在那樣的規模(宇宙的年齡,銀行余額,程式碼行數),你最大的問題很可能是正確使用這些前綴。 - Bob Stein

3
也许是这样吧?
function readable (nb_bytes) {
    if (nb_bytes < 1024) return nb_bytes + 'B';
    else if (nb_bytes < 1024 * 1024) return (Math.round((nb_bytes / 1024) * 100) / 100) + 'KB';
    else return (Math.round((nb_bytes / 1024 / 1024) * 100) / 100) + 'MB';
}

[编辑]

好的,既然你想要更加优雅的东西,我猜你在考虑一个循环。也许这个代码可以满足你的需求:

function readable (nb_bytes,type) {
    var suffix = type ? ['B','KB','MB','GB'] : ['','K','M','G'];
    var i = 0;
    while (nb_bytes > 1024 && i < suffix.length - 1) {
        ++i;
        nb_bytes = Math.round((nb_bytes / 1024) * 100) / 100;
    }
    return (nb_bytes) + suffix[i];
}

在这里,我假设type是一个布尔值 - 根据您的需要更改。


1

修改版的Amer:

(function GetHumanSize(size) {
  var SizePrefixes = ['','K','M','G','T','P','E','Z','Y'];
  if(size <= 0) return '0';
  var t2 = Math.min(Math.round(Math.log(size)/Math.log(1024)),
                    SizePrefixes.length-1);
  return String((Math.round(size * 100 / Math.pow(1024, t2)) / 100)) + 
         ' ' + SizePrefixes[t2] + 'iB';
})(Math.pow(2,131)) === "2251799813685248 YiB"

使用:

  • IEC后缀
  • 没有非标准后缀

1
function formatSize(size, standard) {
    if (standard) { 
        standard = standard.toLowerCase();
    }

    var n = 0, 
        base = standard == 'si' ? 1000 : 1024, 
        prefixes = ' KMGTPEZY';

    if (size < 1) {
        return 0;
    }
    else if (size >= base) {
        n = Math.floor( Math.log(size) / Math.log(base) );

        if (n >= prefixes.length) {
            return 'N/A';
        }

        size = ( size / Math.pow(base, n) ).toFixed(2) * 1 + ' ';
    }

    return size + prefixes[n] + ( n && standard == 'iec' ? 'i' : '' ) + 'B';
}

测试:
for (var i = 0; i++ < 10;) console.log( formatSize( Math.pow(10, i) ) ); 

输出:

10 B
100 B
1000 B
9.77 KB
97.66 KB
976.56 KB
9.54 MB
95.37 MB
953.67 MB
9.31 GB

来自2022年的问候:

function humanSize(size, iec = true) {
  let base = iec ? 1024 : 1000
  let units = ['', 'K', 'M', 'G', 'T']
  let i = Math.log(size) / Math.log(base) | 0
  return `${(size / Math.pow(base, i)).toFixed(2) * 1} ${units[i]}${i && iec ? 'i' : ''}B`
}
undefined
humanSize(2048)
'2 KiB'
humanSize(2000)
'1.95 KiB'
humanSize(500000000000)
'465.66 GiB'
humanSize(500000000000, false)
'500 GB'

1
我参考了前两种方法中最好的部分,并得出了这个方法。它比第一个方法更快,比第二个方法更慢。但其目的是始终保持恰好3个字符,而不进行四舍五入。限制数字为3个字符是因为容器的尺寸限制。此外,如果您想将其用于非基数2的数字格式,则只需将kilo更改为1000即可。如果数字小于1k,则它还具有短路功能。
var kilo = 1024, suffix = ' KMGTPEZYXWVU', humanReadable = function (number) {
  var retValue = false;
  if (typeof number == "number") {
      if (number < kilo) {
          retValue = number.toString();
      } else {
          var e = Math.floor(Math.log(number) / Math.log(kilo));
          retValue = Number((number / Math.pow(kilo, e)).toString().slice(0, 3)) + suffix.charAt(e) + 'B';
      }
  }
  return retValue;
};

0

你的回答真的帮了我很多,所以我决定编写一个实用方法来解决这个大小格式化问题。

请查看我的JSUtils存储库维基页面:https://bitbucket.org/AAverin/jsutils/ | https://github.com/AAverin/JSUtils 它有一个humanReadeableSize方法,使用Amir的四舍五入方式,但也支持在通常的2进制(KiB,MiB)和10进制数字(KB,MB)之间进行转换。

它可以向下舍入到最接近的值,但也可以向上舍入,例如,如果您想知道PB中有多少KB。

随意获取并在您的项目中使用!


0

可以使用number_to_human_size(number, options = {})

number_to_human_size(1234567)

示例:

number_to_human_size(123)                                          # => 123 Bytes
number_to_human_size(1234)                                         # => 1.21 KB
number_to_human_size(12345)                                        # => 12.1 KB
number_to_human_size(1234567)                                      # => 1.18 MB
number_to_human_size(1234567890)                                   # => 1.15 GB
number_to_human_size(1234567890123)                                # => 1.12 TB
number_to_human_size(1234567, precision: 2)                        # => 1.2 MB
number_to_human_size(483989, precision: 2)                         # => 470 KB
number_to_human_size(1234567, precision: 2, separator: ',')        # => 1,2 MB

就像文档中所述http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#method-i-number_to_human_size


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接