2009-02-21 11 views

Respuesta

18

Aquí está mi propia visión sobre el problema:

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]]; 
} 

Por defecto (si 0 se pasa por flags), lo hará de salida de las unidades del SI (base diez). Puede configurar kUnitStringBinaryUnits para seleccionar unidades binarias (base dos) adecuadas para memoria, o kUnitStringOSNativeUnits para seleccionar automáticamente el tipo de unidad según la versión del sistema operativo (pre-Leopard obtiene la base dos, después de que Leopard obtiene la base diez). La configuración kUnitStringLocalizedFormat formatea la cadena en función de la configuración regional actual del usuario. Por ejemplo:

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

Aquí está la función auxiliar requerida para las unidades del sistema operativo nativo:

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; 
} 
5
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

Recomendaría hacer que el parámetro 'theSize' sea del tipo 'size_t', que es un entero de 64 bits. El método anterior fallaría después de 2 gigabytes. – NilObject

+1

Tenga en cuenta que el uso de 1024 como base en comparación con 1000 no es coherente con las pautas de interfaz humana, y como tal, cualquier aplicación que use este código podría ser rechazada desde la tienda de aplicaciones. –

+1

cadena con formato no respeta localizaciones (por ejemplo, símbolos separador decimal) - esto debe hacerse con un número formateador como se ha visto anteriormente – monkeydom

22

Me papilla esto en una subclase 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 

Uso:

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" 
+0

Lo hice pero solo tengo dos métodos: stringFromSize: && stringFromSpeed: – Arvin

6

Aquí es una función más objetiva similar a C (utiliza NSNumber, NSArray, NSStirng, etc ...) para hacer esta conversión.

Esto se basa en la respuesta de Sidnicious, así que muchas gracias por el trabajo inicial realizado allí. También basado en artículos de Wikipedia.

Úselo generalmente así: [HumanReadableDataSizeHelper humanReadableSizeFromBytes:[NSNumber numberWithDouble:doubleValue]].

embargo, parece que desea unidades del SI con un multiplicador 1024 de modo que usaría así: [HumanReadableDataSizeHelper humanReadableSizeFromBytes:[NSNumber numberWithDouble:doubleValue] useSiPrefixes:YES useSiMultiplier:NO]

La razón por la que DEFAULT para prefijos binarios (ki, Mi) se debe a que esos parecen ser los más prefijo de unidad apropiado configurado para usar para tamaños de datos en una computadora. Lo que solicitó fueron los prefijos de la unidad SI pero usando un multiplicador de 1024, técnicamente incorrecto. Aunque señalaré que los prefijos SI para múltiplos de 1024 son bastante comunes y los prefijos binarios no son bien aceptados (según Wikipedia).

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 
98

A partir de OS X 10.8 y el IOS 6, puede utilizar NSByteCountFormatter.

Su ejemplo sería el siguiente:

[NSByteCountFormatter stringFromByteCount:20000000 countStyle:NSByteCountFormatterCountStyleFile]; 
+4

Esto realmente debería marcarse como la respuesta aceptada ahora. :) –

+2

El único inconveniente de usar esto es que el formato se fuerza utilizando la configuración regional actual. – Dids

1
- (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]]; 
} 
+0

Su código es bueno, pero en OSX debe considerar múltiplo de 1000 en lugar de 1024 desde el fabricante del disco duro, y también para el Finder, 1 GB = 1000 MB :-) – Mike97

0

Conozco las preguntas es para Obj C, pero si cualquier persona que busca una versión rápida:

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])" 

    } 
Cuestiones relacionadas