44

Possible Duplicate:
ObjC/Cocoa class for converting size to human-readable string?

I'm new in Cocoa. I'm trying to get size of folder files properly. And display it in MB if it less 1 GB , or in GB.

The way I want it to display is rounded with one number after point.

Example 5.5 MB if it is more than 1000 > 1.1 GB

I'm trying to use this

 unsigned  long long size= ([[[NSFileManager defaultManager] attributesOfItemAtPath:fullPath error:nil] fileSize]);

But I can't a way properly convert number, and display it , as I want.

Thanks.

Community
  • 1
  • 1
User1234
  • 2,362
  • 4
  • 27
  • 57
  • 2
    [ObjC/Cocoa class for converting size to human-readable string?][1] [1]: http://stackoverflow.com/questions/572614/objc-cocoa-class-for-converting-size-to-human-readable-string – Kirby Todd Oct 21 '11 at 08:04
  • func memoryConvertible(_ value: UInt64) -> String { var convertedValue = value var multiplyFactor = 0 let tokens = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] while convertedValue > 1024 { convertedValue /= 1024 multiplyFactor += 1 } return String(format: "%d %@", convertedValue, tokens[multiplyFactor]) } – Jagveer Singh Aug 07 '20 at 13:21

3 Answers3

117

For converting file size to MB, Gb use below function

- (id)transformedValue:(id)value
{
    
    double convertedValue = [value doubleValue];
    int multiplyFactor = 0;
    
    NSArray *tokens = @[@"bytes",@"KB",@"MB",@"GB",@"TB",@“PB”, @“EB”, @“ZB”, @“YB”];
    
    while (convertedValue > 1024) {
        convertedValue /= 1024;
        multiplyFactor++;
    }
    
    return [NSString stringWithFormat:@"%4.2f %@",convertedValue, tokens[multiplyFactor]];
}

EDIT:

You can also use NSByteCountFormatter class. Available in iOS 6.0 / OS X v10.8 and later.

[NSByteCountFormatter stringFromByteCount:1999 countStyle:NSByteCountFormatterCountStyleFile];

You can use NSByteCountFormatterCountStyleFile, NSByteCountFormatterCountStyleMemory, NSByteCountFormatterCountStyleDecimal or NSByteCountFormatterCountStyleBinary in countStyle.

NSByteCountFormatterCountStyleFile: Specifies display of file or storage byte counts. The actual behavior for this is platform-specific; on OS X 10.8, this uses the decimal style, but that may change over time.

NSByteCountFormatterCountStyleMemory: Specifies display of memory byte counts. The actual behavior for this is platform-specific; on OS X 10.8, this uses the binary style, but that may change over time.

NSByteCountFormatterCountStyleDecimal: Specifies the number of bytes for KB explicitly, 1000 bytes are shown as 1 KB

NSByteCountFormatterCountStyleBinary: Specifies the number of bytes for KB explicitly, 1024 bytes are shown as 1 KB

Community
  • 1
  • 1
Parag Bafna
  • 22,812
  • 8
  • 71
  • 144
  • 2
    Please use proper KiB / MiB / etc. for binary prefixes. Apple has switched to SI (decimal) prefixes, and consistency is a good thing. – Dietrich Epp Oct 21 '11 at 08:44
  • I think the `,value` param in your last line is extraneous - you have two spots in the format string and three params. – MusiGenesis Jul 09 '13 at 20:39
  • This will crash if `convertedValue` is larger than 1024 TB - the loop will continue, enlarging `convertedValue`. Then we'll try to access index out of bounds in the `return` statement. Anyway, interesting idea :) – Nat Mar 22 '16 at 13:29
  • 1
    @DietrichEpp that's what I thought, but that's not what's written [here](https://support.apple.com/en-us/HT201402) – farzadshbfn Jun 06 '16 at 14:41
  • @farzadshbfn: That's "storage capacity", which is different. I have personally verified that file sizes are not reported that way. – Dietrich Epp Jun 06 '16 at 17:19
  • You can get Swift 3 version by this [link](http://stackoverflow.com/a/42756119/4593553). – Jerome Mar 13 '17 at 03:20
  • Swift 4 extension NSObject { func size(bytes:Int) -> String { var convertedValue = Double(bytes) var i = 0 let tokens = ["bytes","KB","MB","GB","TB","PB", "EB", "ZB", "YB"] while convertedValue > 1024 { convertedValue /= 1024.0 i = i + 1 } return "\(convertedValue) \(tokens[i])" } } – Shrikant Tanwade May 09 '19 at 07:59
60

If you're targeting OS X 10.8 or iOS 6, you can use NSByteCountFormatter.

I would write your example like this:

    NSError *error = nil;
    NSDictionary *attribs = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&error];
    if (attribs) {
        NSString *string = [NSByteCountFormatter stringFromByteCount:[attribs fileSize] countStyle:NSByteCountFormatterCountStyleFile];
        NSLog(@"%@", string);
    }
jrc
  • 20,354
  • 10
  • 69
  • 64
  • 1
    This is a perfect one-liner solution. Thanks :-) – ngoa Mar 08 '13 at 16:39
  • Unfortunately the NSByteCountFormatter uses the current locale (can't force it to en-US either), so the string can easily become much longer than initially anticipated. – Dids Oct 06 '15 at 13:23
8

Here is a piece of code from my library. (I hereby release it under the simplified BSD license so there.) It is fairly extensively tested, and it does all the rounding exactly correct. This is not as trivial as it sounds. It always gives two significant figures unless it prints three digits (e.g., 980 B) in which case all three digits are significant.

Using stringWithFormat:@"%..something...f" won't work because if you round 999999 bytes up to 1000 kilobytes, you want to display it as 1.0 MB, not as 1000 kB.

Note that this code also does "banker's rounding" or "unbiased rounding" or "round to even", whichever you want to call it. So 1050 becomes "1.0 kB", but 1150 becomes "1.2 kB". This is the exact same way that printf does it on my system and is the generally preferred rounding method for this sort of thing.

#include <stdio.h>
#include <string.h>
#include <stdint.h>

#define SIZE_BUFSZ 7
static char const SIZE_PREFIXES[] = "kMGTPEZY";

void
format_size(char buf[SIZE_BUFSZ], uint64_t sz)
{
    int pfx = 0;
    unsigned int m, n, rem, hrem;
    uint64_t a;
    if (sz <= 0) {
        memcpy(buf, "0 B", 3);
        return;
    }
    a = sz;
    if (a < 1000) {
        n = a;
        snprintf(buf, SIZE_BUFSZ, "%u B", n);
        return;
    }
    for (pfx = 0, hrem = 0; ; pfx++) {
        rem = a % 1000ULL;
        a = a / 1000ULL;
        if (!SIZE_PREFIXES[pfx + 1] || a < 1000ULL)
            break;
        hrem |= rem;
    }
    n = a;
    if (n < 10) {
        if (rem >= 950) {
            buf[0] = '1';
            buf[1] = '0';
            buf[2] = ' ';
            buf[3] = SIZE_PREFIXES[pfx];
            buf[4] = 'B';
            buf[5] = '\0';
            return;
        } else {
            m = rem / 100;
            rem = rem % 100;
            if (rem > 50 || (rem == 50 && ((m & 1) || hrem)))
                m++;
            snprintf(buf, SIZE_BUFSZ,
                     "%u.%u %cB", n, m, SIZE_PREFIXES[pfx]);
        }
    } else {
        if (rem > 500 || (rem == 500 && ((n & 1) || hrem)))
            n++;
        if (n >= 1000 && SIZE_PREFIXES[pfx + 1]) {
            buf[0] = '1';
            buf[1] = '.';
            buf[2] = '0';
            buf[3] = ' ';
            buf[4] = SIZE_PREFIXES[pfx+1];
            buf[5] = 'B';
            buf[6] = '\0';
        } else {
            snprintf(buf, SIZE_BUFSZ,
                     "%u %cB", n, SIZE_PREFIXES[pfx]);
        }
    }
}

Here is the test data:

{ 0, "0 B" },
{ 5, "5 B" },
{ 20, "20 B" },
{ 100, "100 B" },
{ 500, "500 B" },
{ 999, "999 B" },
{ 1000, "1.0 kB" },
{ 1050, "1.0 kB" },
{ 1051, "1.1 kB" },
{ 2349, "2.3 kB" },
{ 2350, "2.4 kB" },
{ 9949, "9.9 kB" },
{ 9950, "10 kB" },
{ 10000, "10 kB" },
{ 10500, "10 kB" },
{ 10501, "11 kB" },
{ 99499, "99 kB" },
{ 99500, "100 kB" },
{ 999499, "999 kB" },
{ 999500, "1.0 MB" },
{ 1000000, "1.0 MB" },
{ 952500000, "952 MB" },
{ 952500001, "953 MB" },
{ 1000000000, "1.0 GB" },
{ 2300000000000ULL, "2.3 TB" },
{ 9700000000000000ULL, "9.7 PB" }
Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • Nice library, but aren't the conversion wrong? 1 kB = 1024 B – Niklas Wulff Oct 21 '11 at 09:03
  • 2
    @NiklasRingdahl: The international standard for 1 kB is 1000 bytes. There is a long history of disagreement over this, but in the end, I will follow the international standard that has been in place since 1799. Apple also follows this convention now, but a few years ago did not. Other vendors are also inconsistent. Since this is an OS X question, it is also a good idea to follow Apple's lead and use 1000 (in addition to following the international standard). The standard prefixes for powers of 1024 are Ki, Mi, Gi, etc., and you should use these if you want binary prefixes. – Dietrich Epp Oct 21 '11 at 09:06
  • Unfortunately, it is all too common to argue about this on the internet. In fact, one can be sure when publishing code like this that *someone will complain,* no matter which side you choose. Maintaining a separate function and testing it for binary prefixes would be twice as much work. You are welcome to do so, but I have already put enough hours into this function and it serves me well. – Dietrich Epp Oct 21 '11 at 09:08
  • I was not aware about this discussion, thanks for a clear explanation. It seems logical as you say to use different prefixes for the different definitions of "kilo", as the strange binary standard is somewhat strange - I won't argue with you. :-) – Niklas Wulff Oct 21 '11 at 09:23
  • Thanks. I wanted to add some context, but I ended up going off on a rant :-) – Dietrich Epp Oct 21 '11 at 09:31