【发布时间】:2009-02-21 08:11:29
【问题描述】:
有没有一种简单的方法可以做类似的事情......
[NSMagicDataConverter humanStringWithBytes:20000000]
..哪个会返回“19.1MB”?
【问题讨论】:
-
NSByteCountFormatter 在 iOS 6.0 或更高版本和 OS X 10.8 或更高版本中
标签: objective-c cocoa
有没有一种简单的方法可以做类似的事情......
[NSMagicDataConverter humanStringWithBytes:20000000]
..哪个会返回“19.1MB”?
【问题讨论】:
标签: objective-c cocoa
从 OS X 10.8 和 iOS 6 开始,您可以使用 NSByteCountFormatter。
您的示例如下所示:
[NSByteCountFormatter stringFromByteCount:20000000 countStyle:NSByteCountFormatterCountStyleFile];
【讨论】:
我会把它混入一个 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"
【讨论】:
这是我自己对这个问题的看法:
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 单位(以十为底)。您可以设置kUnitStringBinaryUnits 来选择适合内存的二进制(以二为底)单位,或kUnitStringOSNativeUnits 以根据操作系统版本自动选择单位类型(Leopard 之前的以二为底,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;
}
【讨论】:
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]);
}
【讨论】:
这是一个更类似于 Objective C 的函数(使用 NSNumber、NSArray、NSStirng 等)来进行这种转换。
这是基于 Sidncious 的回答,非常感谢您在那里完成的初步工作。同样基于维基百科的文章。
一般这样使用它:[HumanReadableDataSizeHelper humanReadableSizeFromBytes:[NSNumber numberWithDouble:doubleValue]]。
但是,您似乎想要具有 1024 乘数的 SI 单位,因此您可以像这样使用它:[HumanReadableDataSizeHelper humanReadableSizeFromBytes:[NSNumber numberWithDouble:doubleValue] useSiPrefixes:YES useSiMultiplier:NO]
我默认使用二进制前缀 (ki, Mi) 的原因是因为它们似乎是最适合用于计算机上数据大小的单位前缀集。您要求的是 SI 单位前缀,但使用 1024 的乘数,技术上不正确。虽然我会注意到 1024 的倍数的 SI 前缀相当普遍,并且二进制前缀不被很好地接受(根据 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
【讨论】:
您可以使用 FormatterKit 及其TTTUnitOfInformationFormatter 类:
https://github.com/mattt/FormatterKit
它也可以通过 CocoaPods 获得:
pod 'FormatterKit', '~> 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]];
}
【讨论】:
我知道问题是针对 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])"
}
【讨论】: