ObjC/Cocoa 类用于将大小转换为人类可读的字符串?

问题描述 投票:0回答:9

有没有一种简单的方法可以做类似的事情

[NSMagicDataConverter humanStringWithBytes:20000000]

..这会返回“19.1MB”?

objective-c cocoa
9个回答
106
投票

从 OS X 10.8 和 iOS 6 开始,您可以使用 NSByteCountFormatter

您的示例如下所示:

[NSByteCountFormatter stringFromByteCount:20000000 countStyle:NSByteCountFormatterCountStyleFile];

21
投票

我会将其混入 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"

18
投票

这是我自己对这个问题的看法:

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
),它将输出 SI 单位(基数为 10)。您可以设置
kUnitStringBinaryUnits
选择适合内存的二进制(基数为 2)单位,或设置
kUnitStringOSNativeUnits
根据操作系统版本自动选择单位类型(Leopard 之前的为基数为 2,Leopard 后为基数为 10)。设置
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;
}

6
投票
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]);
}

6
投票

这里有一个更像 Objective C 的函数(使用 NSNumber、NSArray、NSStirng 等)来执行此转换。

这是基于 Sidnious 的回答,非常感谢您在那里所做的初步工作。 也基于维基百科文章。

一般这样使用:

[HumanReadableDataSizeHelper  humanReadableSizeFromBytes:[NSNumber numberWithDouble:doubleValue]]

但是,看起来您想要带有 1024 乘数的 SI 单位,因此您可以像这样使用它:

[HumanReadableDataSizeHelper  humanReadableSizeFromBytes:[NSNumber numberWithDouble:doubleValue]  useSiPrefixes:YES  useSiMultiplier:NO]

我默认使用二进制前缀(ki,Mi)的原因是因为它们似乎是用于计算机上数据大小的最合适的单位前缀集。 您请求的是 SI 单位前缀,但使用了 1024 的乘数,这在技术上是不正确的。 不过我会注意到,1024 倍数的 SI 前缀相当常见,而二进制前缀并未被很好地接受(根据维基百科)。

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

2
投票

您可以使用 FormatterKit 及其

TTTUnitOfInformationFormatter
类:

https://github.com/mattt/FormatterKit

也可以通过 CocoaPods 使用:

pod 'FormatterKit', '~> 1.1.1'

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
投票

我知道问题是针对 Obj C 的,但如果有人正在寻找 swift 版本:

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

    }

0
投票

这里是一个仅适用于当前 macOS 和 iOS 的 SI 双重人类方法。 它在大约 10^-16 范围内转换正或负双精度 <= double <= 10^17 and returns a scaled double < 1000, a power of ten engineering exponent, and 2 SI prefix strings. Although the code works well in all my Apps, its heart is loop-less and unlike other implementations I’ve seen, so I don’t know if I’ve missed a monstrous glitch:

NSInteger exp = ceil( log10( num ) );
if ( lto ) exp += 3;
NSInteger div = pow( 10, exp );
double res = num / div;
NSInteger eng = exp / 3;
NSInteger rem = exp - ( eng * 3 );
if ( rem == 0 &&  res != 1.0 ) { eng--; rem = 3; }
res *= pow( 10, rem );

这里的示例输出显示了其范围的正半部分:

double n;
NSArray *engNotation;
n = 1;
for ( int i = 0; i < 19; i++ ) {
    engNotation = [self doubleToSIEngineeringNotation:n];
    NSLog(@"%20ld = %9.4f [%@,%@,%@]", (long)n, [engNotation[ D2SIENvalue ] doubleValue], engNotation[ D2SIENexponent ], engNotation[ D2SIENprefixShort ], engNotation[ D2SIENprefixLong ] );
    n *= 10;
}

输出:

               1 =    1.0000 [0,,]
              10 =   10.0000 [0,,]
             100 =  100.0000 [0,,]
            1000 =    1.0000 [3,k,kilo]
           10000 =   10.0000 [3,k,kilo]
          100000 =  100.0000 [3,k,kilo]
         1000000 =    1.0000 [6,M,mega]
        10000000 =   10.0000 [6,M,mega]
       100000000 =  100.0000 [6,M,mega]
      1000000000 =    1.0000 [9,G,giga]
     10000000000 =   10.0000 [9,G,giga]
    100000000000 =  100.0000 [9,G,giga]
   1000000000000 =    1.0000 [12,T,tera]
  10000000000000 =   10.0000 [12,T,tera]
 100000000000000 =  100.0000 [12,T,tera]
1000000000000000 =    1.0000 [15,P,peta]
   10000000000000000 =   10.0000 [15,P,peta]
  100000000000000000 =  100.0000 [15,P,peta]
 1000000000000000000 =    1.0000 [18,E,exa]

这是代码:

typedef NS_ENUM(NSInteger, doubleToSIEngineeringNotation) {
    D2SIENvalue = 0,
    D2SIENexponent,
    D2SIENprefixShort,
    D2SIENprefixLong,
}

- (NSArray *)doubleToSIEngineeringNotation:(double)num {

// Convert a positive or negative double in the approximate range 10^-16 <= double <= 10^17
// to a scaled double < 1000 with power of ten engineering exponent and 2 SI prefix strings.
//
// EXIT:  NSArray of four items:
//  [ D2SIENvalue ]       NSNumber: the scaled double
//  [ D2SIENexponent ]    NSNumber: the power of ten engineering exponent
//  [ D2SIENprefixShort ] NSString: the SI prefix abbreviation
//  [ D2SIENprefixLong ]  NSString: the SI prefix long spelling
//
// EX:
//  NSArray *eng = [self doubleToSIEngineeringNotation:[track estimatedDataRate]];
//  NSString *dataRate = [NSString stringWithFormat:@"%.2f %@b/s", [eng[ D2SIENvalue ] doubleValue], eng[ D2SIENprefixShort ]];
//  [videoDict setObject:dataRate forKey:@"Data Rate" ];
//
//  e.g. Data Rate = 72.41 kb/s
//       Data Size =  8.91 MB
//       Weight    = 90.13 micro grams

NSArray *largeSI = @[ @[@"", @""], @[@"k", @"kilo"],  @[@"M", @"mega"],  @[@"G", @"giga"],
                      @[@"T", @"tera"], @[@"P", @"peta"], @[@"E", @"exa"],  @[@"Z", @"zetta"],
                      @[@"Y", @"yotta"], @[@"R", @"ronna"], @[@"Q", @"quetta"] ];

NSArray *smallSI = @[ @[@"", @""], @[@"m", @"milli"], @[@"µ", @"micro"], @[@"n", @"nano"],
                      @[@"p", @"pico"], @[@"f", @"femto"], @[@"a", @"atto"], @[@"z", @"zepto"],
                      @[@"y", @"yocto"], @[@"r", @"ronto"], @[@"r", @"quecto"] ];

if ( num == 0.0 ) return @[ @0.0, @0, @"", @"" ];
bool ngn = num < 0.0 ? YES : NO;
if ( ngn ) { num = -num; }
bool lto = num < 1.0 ? YES : NO;
if ( lto ) { num = 1.0 / num; }

NSInteger exp = ceil( log10( num ) );
if ( lto ) exp += 3;
NSInteger div = pow( 10, exp );
double res = num / div;
NSInteger eng = exp / 3;
NSInteger rem = exp - ( eng * 3 );
if ( rem == 0 &&  res != 1.0 ) { eng--; rem = 3; }
res *= pow( 10, rem );

if ( lto ) { res = 1.0 / res; }
if ( ngn ) res = -res;

return [NSArray arrayWithObjects:
            [NSNumber numberWithDouble:res],
            [NSNumber numberWithInteger:( (eng * 3) * (lto ? -1 : +1 ) )],
            ( lto ? smallSI[eng][0] : largeSI[eng][0] ),
            ( lto ? smallSI[eng][1] : largeSI[eng][1] ),
       nil];

}

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.