Y a-t-il un moyen simple de faire quelque chose comme
[NSMagicDataConverter humanStringWithBytes:20000000]
qui renverrait "19.1MB" ?
Y a-t-il un moyen simple de faire quelque chose comme
[NSMagicDataConverter humanStringWithBytes:20000000]
qui renverrait "19.1MB" ?
À partir de OS X 10.8 et iOS 6, vous pouvez utiliser NSByteCountFormatter .
Votre exemple ressemblerait à ceci :
[NSByteCountFormatter stringFromByteCount:20000000 countStyle:NSByteCountFormatterCountStyleFile];
J'en ferais une sous-classe de 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
Utilisation :
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"
Voici ma propre vision du problème :
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]];
}
Par défaut (si 0
est passé pour flags
), il produira des unités SI (base dix). Vous pouvez définir kUnitStringBinaryUnits
pour sélectionner des unités binaires (base deux) adaptées à la mémoire, ou kUnitStringOSNativeUnits
pour que le type d'unité soit automatiquement sélectionné en fonction de la version du système d'exploitation (avant Léopard, la base deux, après Léopard, la base dix). Réglage de kUnitStringLocalizedFormat
formate la chaîne de caractères en fonction de la locale actuelle de l'utilisateur. Par exemple :
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)
Voici la fonction d'aide requise pour les unités natives de l'OS :
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;
}
Voici une fonction plus objective de type C (qui utilise NSNumber, NSArray, NSStirng, etc...) pour effectuer cette conversion.
Ceci est basé sur la réponse de Sidnicious, donc un grand merci pour le travail initial effectué ici. Également basé sur les articles de Wikipedia.
Utilisez-le généralement comme ceci : [HumanReadableDataSizeHelper humanReadableSizeFromBytes:[NSNumber numberWithDouble:doubleValue]]
.
Mais, il semble que vous vouliez des unités SI avec un multiplicateur de 1024, donc vous l'utiliseriez comme ceci : [HumanReadableDataSizeHelper humanReadableSizeFromBytes:[NSNumber numberWithDouble:doubleValue] useSiPrefixes:YES useSiMultiplier:NO]
La raison pour laquelle j'utilise par défaut les préfixes binaires (ki, Mi) est que ceux-ci semblent être les préfixes d'unités les plus appropriés à utiliser pour les tailles de données sur un ordinateur. Ce que vous avez demandé, ce sont les préfixes d'unités SI mais en utilisant un multiplicateur de 1024, ce qui est techniquement incorrect. Je tiens toutefois à préciser que les préfixes SI pour les multiples de 1024 sont assez courants et que les préfixes binaires ne sont pas bien acceptés (selon 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
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]);
}
Je recommande de faire en sorte que le paramètre "theSize" soit de type "size_t", c'est-à-dire un entier de 64 bits. La méthode ci-dessus échouerait après 2 gigaoctets.
Veuillez noter que l'utilisation d'une base de 1024 au lieu de 1000 n'est pas conforme aux directives relatives à l'interface humaine et que, par conséquent, toute application utilisant ce code pourrait être rejetée de l'App Store.
La chaîne avec le format ne respecte pas les localisations (par exemple, les symboles de séparation décimale) - cela devrait être fait avec un formateur de nombres comme vu ci-dessus
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
15 votes
NSByteCountFormatter dans iOS 6.0 ou ultérieur et OS X 10.8 ou ultérieur
0 votes
stackoverflow.com/a/7846956/944634