Подтвердить что ты не робот

ObjC/Cocoa класс для преобразования размера в удобочитаемую строку?

Есть ли простой способ сделать что-то вроде.

[NSMagicDataConverter humanStringWithBytes:20000000]

.., который вернет "19.1MB"?

4b9b3361

Ответ 1

Вот моя собственная проблема:

enum {
    kUnitStringBinaryUnits     = 1 << 0,
    kUnitStringOSNativeUnits   = 1 << 1,
    kUnitStringLocalizedFormat = 1 << 2
};

NSString* unitStringFromBytes(double bytes, uint8_t flags){

    static const char units[] = { '\0', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' };
    static int maxUnits = sizeof units - 1;

    int multiplier = (flags & kUnitStringOSNativeUnits && !leopardOrGreater() || flags & kUnitStringBinaryUnits) ? 1024 : 1000;
    int exponent = 0;

    while (bytes >= multiplier && exponent < maxUnits) {
        bytes /= multiplier;
        exponent++;
    }
    NSNumberFormatter* formatter = [[[NSNumberFormatter alloc] init] autorelease];
    [formatter setMaximumFractionDigits:2];
    if (flags & kUnitStringLocalizedFormat) {
        [formatter setNumberStyle: NSNumberFormatterDecimalStyle];
    }
    // Beware of reusing this format string. -[NSString stringWithFormat] ignores \0, *printf does not.
    return [NSString stringWithFormat:@"%@ %cB", [formatter stringFromNumber: [NSNumber numberWithDouble: bytes]], units[exponent]];
}

По умолчанию (если 0 передается для flags), он выдает единицы СИ (базовая десятка). Вы можете установить kUnitStringBinaryUnits для выбора двоичных (базовых двух) единиц, подходящих для памяти, или kUnitStringOSNativeUnits, чтобы тип устройства был выбран автоматически в зависимости от версии ОС (pre-Leopard получает базу два, пост-леопард получает базовую десятку). Настройка kUnitStringLocalizedFormat форматирует строку на основе текущей локали пользователя. Например:

unitStringFromBytes(1073741824, 0); // → "1.07 GB"
unitStringFromBytes(1073741824, kUnitStringBinaryUnits); // → "1 GB"
unitStringFromBytes(1073741824, kUnitStringOSNativeUnits | kUnitStringLocalizedFormat); // → "1.07 GB" (In Mac OS 10.6)
unitStringFromBytes(12345678901234567890123456789, kUnitStringOSNativeUnits | kUnitStringLocalizedFormat); // → "12,345.68 YB" (In Mac OS 10.6, in the US)
unitStringFromBytes(12345678901234567890123456789, kUnitStringOSNativeUnits | kUnitStringLocalizedFormat); // → "12.345,68 YB" (In Mac OS 10.6, in Spain)

Здесь вспомогательная функция, необходимая для исходных блоков ОС:

BOOL leopardOrGreater(){
    static BOOL alreadyComputedOS = NO;
    static BOOL leopardOrGreater = NO;
    if (!alreadyComputedOS) {
        SInt32 majorVersion, minorVersion;
        Gestalt(gestaltSystemVersionMajor, &majorVersion);
        Gestalt(gestaltSystemVersionMinor, &minorVersion);
        leopardOrGreater = ((majorVersion == 10 && minorVersion >= 5) || majorVersion > 10);
        alreadyComputedOS = YES;
    }
    return leopardOrGreater;
}

Ответ 2

Начиная с OS X 10.8 и iOS 6, вы можете использовать NSByteCountFormatter.

Ваш пример будет выглядеть так:

[NSByteCountFormatter stringFromByteCount:20000000 countStyle:NSByteCountFormatterCountStyleFile];

Ответ 3

Я бы заманил это в подкласс NSFormatter.

#import <Foundation/Foundation.h>

@interface SOFileSizeFormatter : NSNumberFormatter 
{
    @private
    BOOL useBaseTenUnits;
}

/** Flag signaling whether to calculate file size in binary units (1024) or base ten units (1000).  Default is binary units. */
@property (nonatomic, readwrite, assign, getter=isUsingBaseTenUnits) BOOL useBaseTenUnits;

@end

static const char sUnits[] = { '\0', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' };
static int sMaxUnits = sizeof sUnits - 1;

@implementation SOFileSizeFormatter

@synthesize useBaseTenUnits;

- (NSString *) stringFromNumber:(NSNumber *)number
{
    int multiplier = useBaseTenUnits ? 1000 : 1024;
    int exponent = 0;

    double bytes = [number doubleValue];

    while ((bytes >= multiplier) && (exponent < sMaxUnits)) {
        bytes /= multiplier;
        exponent++;
    }

    return [NSString stringWithFormat:@"%@ %cB", [super stringFromNumber: [NSNumber numberWithDouble: bytes]], sUnits[exponent]];
}

@end

Использование:

NSString *path = ...; // path to a file of 1,500,000 bytes
NSString *sizeString = nil;

NSNumber *sizeAttrib = [[[NSFileManager defaultManager] attributesOfItemAtPath:path error:NULL]objectForKey:NSFileSize];

SOFileSizeFormatter *sizeFormatter = [[[SOFileSizeFormatter alloc] init] autorelease];
[sizeFormatter setMaximumFractionDigits:2];

sizeString = [sizeFormatter stringFromNumber:sizeAttrib];
// sizeString ==> @"1.43 MB"

[sizeFormatter setUseBaseTenUnits:YES];
sizeString = [sizeFormatter stringFromNumber:sizeAttrib];
// sizeString ==> @"1.5 MB"

Ответ 4

NSString *stringFromFileSize(NSInteger theSize)
{
    /*
     From http://snippets.dzone.com/posts/show/3038 with slight modification
     */
    float floatSize = theSize;
    if (theSize<1023)
        return([NSString stringWithFormat:@"%i bytes",theSize]);
    floatSize = floatSize / 1024;
    if (floatSize<1023)
        return([NSString stringWithFormat:@"%1.1f KB",floatSize]);
    floatSize = floatSize / 1024;
    if (floatSize<1023)
        return([NSString stringWithFormat:@"%1.1f MB",floatSize]);
    floatSize = floatSize / 1024;

    return([NSString stringWithFormat:@"%1.1f GB",floatSize]);
}

Ответ 5

Вот более объективная C-подобная функция (использует NSNumber, NSArray, NSStirng и т.д.) для выполнения этого преобразования.

Это основано на Sidnicious ответе, поэтому большое спасибо за начальную работу, проделанную там. Также на основе статей Википедии.

Используйте его в целом так: [HumanReadableDataSizeHelper humanReadableSizeFromBytes:[NSNumber numberWithDouble:doubleValue]].

Но похоже, что вы хотите единицы СИ с мультипликатором 1024, чтобы использовать его следующим образом: [HumanReadableDataSizeHelper humanReadableSizeFromBytes:[NSNumber numberWithDouble:doubleValue] useSiPrefixes:YES useSiMultiplier:NO]

Причина, по умолчанию, для бинарных префиксов (ki, Mi) заключается в том, что они, по-видимому, являются наиболее подходящим префиксом устройства, установленным для размеров данных на компьютере. Вы запросили префиксы единицы СИ, но с использованием множителя 1024, технически неверного. Хотя я буду замечать, что префиксы SI для кратных 1024 довольно распространены, а бинарные префиксы не принимаются (согласно Википедии).

HumanReadableDataSizeHelper.h

@interface HumanReadableDataSizeHelper : NSObject


/**
    @brief  Produces a string containing the largest appropriate units and the new fractional value.
    @param  sizeInBytes  The value to convert in bytes.

    This function converts the bytes value to a value in the greatest units that produces a value >= 1 and returns the new value and units as a string.

    The magnitude multiplier used is 1024 and the prefixes used are the binary prefixes (ki, Mi, ...).
 */
+ (NSString *)humanReadableSizeFromBytes:(NSNumber *)sizeInBytes;

/**
    @brief  Produces a string containing the largest appropriate units and the new fractional value.
    @param  sizeInBytes  The value to convert in bytes.
    @param  useSiPrefixes  Controls what prefix-set is used.
    @param  useSiMultiplier  Controls what magnitude multiplier is used.

    This function converts the bytes value to a value in the greatest units that produces a value >= 1 and returns the new value and units as a string.

    When useSiPrefixes is true, the prefixes used are the SI unit prefixes (k, M, ...).
    When useSiPrefixes is false, the prefixes used are the binary prefixes (ki, Mi, ...).

    When useSiMultiplier is true, the magnitude multiplier used is 1000
    When useSiMultiplier is false, the magnitude multiplier used is 1024.
 */
+ (NSString *)humanReadableSizeFromBytes:(NSNumber *)sizeInBytes  useSiPrefixes:(BOOL)useSiPrefixes  useSiMultiplier:(BOOL)useSiMultiplier;


@end

HumanReadableDataSizeHelper.m

@implementation HumanReadableDataSizeHelper


+ (NSString *)humanReadableSizeFromBytes:(NSNumber *)sizeInBytes
{
    return [self humanReadableSizeFromBytes:sizeInBytes  useSiPrefixes:NO  useSiMultiplier:NO];
}


+ (NSString *)humanReadableSizeFromBytes:(NSNumber *)sizeInBytes  useSiPrefixes:(BOOL)useSiPrefixes  useSiMultiplier:(BOOL)useSiMultiplier
{
    NSString *unitSymbol = @"B";
    NSInteger multiplier;
    NSArray *prefixes;

    if (useSiPrefixes)
    {
        /*  SI prefixes
         http://en.wikipedia.org/wiki/Kilo-
         kilobyte   (kB)    10^3    
         megabyte   (MB)    10^6    
         gigabyte   (GB)    10^9    
         terabyte   (TB)    10^12   
         petabyte   (PB)    10^15   
         exabyte    (EB)    10^18   
         zettabyte  (ZB)    10^21   
         yottabyte  (YB)    10^24   
         */

        prefixes = [NSArray arrayWithObjects: @"", @"k", @"M", @"G", @"T", @"P", @"E", @"Z", @"Y", nil];
    }
    else
    {
        /*  Binary prefixes
         http://en.wikipedia.org/wiki/Binary_prefix
         kibibyte   (KiB)   2^10 = 1.024 * 10^3
         mebibyte   (MiB)   2^20 ≈ 1.049 * 10^6
         gibibyte   (GiB)   2^30 ≈ 1.074 * 10^9
         tebibyte   (TiB)   2^40 ≈ 1.100 * 10^12
         pebibyte   (PiB)   2^50 ≈ 1.126 * 10^15
         exbibyte   (EiB)   2^60 ≈ 1.153 * 10^18
         zebibyte   (ZiB)   2^70 ≈ 1.181 * 10^21
         yobibyte   (YiB)   2^80 ≈ 1.209 * 10^24
         */

        prefixes = [NSArray arrayWithObjects: @"", @"ki", @"Mi", @"Gi", @"Ti", @"Pi", @"Ei", @"Zi", @"Yi", nil];
    }

    if (useSiMultiplier)
    {
        multiplier = 1000;
    }
    else
    {
        multiplier = 1024;
    }

    NSInteger exponent = 0;
    double size = [sizeInBytes doubleValue];

    while ( (size >= multiplier) && (exponent < [prefixes count]) )
    {
        size /= multiplier;
        exponent++;
    }

    NSNumberFormatter* formatter = [[[NSNumberFormatter alloc] init] autorelease];
    [formatter setMaximumFractionDigits:2];
    [formatter setNumberStyle:NSNumberFormatterDecimalStyle]; // Uses localized number formats.

    NSString *sizeInUnits = [formatter stringFromNumber:[NSNumber numberWithDouble:size]];

    return [NSString stringWithFormat:@"%@ %@%@", sizeInUnits, [prefixes objectAtIndex:exponent], unitSymbol];
}


@end

Ответ 6

Вы можете использовать FormatterKit и класс TTTUnitOfInformationFormatter:

https://github.com/mattt/FormatterKit

Он также доступен через CocoaPods с:

pod 'FormatterKit', '~> 1.1.1'

Ответ 7

- (id)transformedValue:(id)value
{

    double convertedValue = [value doubleValue];
    int multiplyFactor = 0;

    NSArray *tokens = @[@"bytes",@"KB",@"MB",@"GB",@"TB"];

    while (convertedValue > 1024) {
        convertedValue /= 1024;
        multiplyFactor++;
    }

    return [NSString stringWithFormat:@"%4.2f %@",convertedValue, tokens[multiplyFactor]];
}

Ответ 8

Я знаю, что вопросы для Obj C, но если кто-то ищет быструю версию:

 public static func fileSizeDisplay(fromBytes:Int) -> String {
        let display = ["bytes","KB","MB","GB","TB","PB"]
        var value:Double = Double(fromBytes)
        var type = 0
        while (value > 1024){
            value /= 1024
            type = type + 1

        }
        return "\(String(format:"%g", value)) \(display[type])"

    }