Is there build in methods for formatting file sizes in Objective C? Or may be you could suggest me some library/source code/etc.?
What I mean is you have some file size that should be displayed something like this depending on given size:
1234 kb
1,2 mb
etc..
Thanks in advance
This one solves the problem quite elegantly:
[NSByteCountFormatter stringFromByteCount:countStyle:]
Example usage:
long long fileSize = 14378165;
NSString *displayFileSize = [NSByteCountFormatter stringFromByteCount:fileSize
countStyle:NSByteCountFormatterCountStyleFile];
NSLog(#"Display file size: %#", displayFileSize);
fileSize = 50291;
displayFileSize = [NSByteCountFormatter stringFromByteCount:fileSize
countStyle:NSByteCountFormatterCountStyleFile];
NSLog(#"Display file size: %#", displayFileSize);
Log output:
Display file size: 14.4 MB
Display file size: 50 KB
The output will be formatted properly according to the device's regional settings.
Available since iOS 6.0 and OS X 10.8.
Here's some code I found lying around. Not terribly efficient, and probably better attached to a category of NSNumberFormatter than NSNumber, and so on, but it seems to work
#interface NSNumber (FormatKibi)
- (NSString *)formatKibi;
#end
#implementation NSNumber (FormatKibi)
- (NSString *)formatKibi {
double value = [self doubleValue];
static const char suffixes[] = { 0, 'k', 'm', 'g', 't' };
int suffix = 0;
if (value <= 10000)
return [[NSString stringWithFormat:#"%5f", value]
substringToIndex:5];
while (value > 9999) {
value /= 1024.0;
++suffix;
if (suffix >= sizeof(suffixes)) return #"!!!!!";
}
return [[[NSString stringWithFormat:#"%4f", value]
substringToIndex:4]
stringByAppendingFormat:#"%c", suffixes[suffix]];
}
#end
I tested it with this:
int main(int argc, char *argv[]) {
for (int i = 1; i != argc; ++i) {
NSNumber *n = [NSNumber numberWithInteger:
[[NSString stringWithUTF8String:argv[i]]
integerValue]];
printf("%s ", [[n formatKibi] UTF8String]);
}
printf("\n");
return 0;
}
Then:
$ ./sizeformat 1 12 123 1234 12345 123456 1234567 12345678 1234567890 123456789012 12345678901234 1234567890123456 123456789012345678
1.000 12.00 123.0 1234. 12.0k 120.k 1205k 11.7m 1177m 114.g 11.2t 1122t !!!!!
Get the file size and then calculate whether it is in bytes or kb or mb
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:URL error:&attributesError];
NSNumber *fileSizeNumber = [fileAttributes objectForKey:NSFileSize];
long long fileSize = [fileSizeNumber longLongValue];
Then conversion table
1 byte = 8 bits
1 KiB = 1,024 bytes
1 MiB = 1024 kb
1 GiB = 1024 mb
check this link
Related
This is my java code. Now I want to implement same functionality in Objective-C.
int dkLen = 16;
int rounds = 1000;
PBEKeySpec keySpec = new PBEKeySpec(hashKey.toCharArray(),salt.getBytes(), rounds, dkLen * 8);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
return factory.generateSecret(keySpec).getEncoded();
This is my iOS implementation
- (void)getHashKey {
NSString *hash_key=#"MY_HASHKEY";
NSString *saltKey = #"MY_SALTKEY";
int dkLen = 16;
NSData *keyData = [hash_key dataUsingEncoding:NSUTF8StringEncoding];
NSData *salt = [saltKey dataUsingEncoding:NSUTF8StringEncoding];
uint rounds = 1000;
uint keySize = kCCKeySizeAES128;
NSMutableData *derivedKey = [NSMutableData dataWithLength:keySize];
CCKeyDerivationPBKDF(kCCPBKDF2, // algorithm
keyData.bytes, // password
keyData.length, // passwordLength
salt.bytes, // salt
salt.length, // saltLen
kCCPRFHmacAlgSHA1, // PRF
rounds, // rounds
derivedKey.mutableBytes, // derivedKey
dkLen*8); // derivedKeyLen
NSString *myString = [[NSString alloc] initWithData:derivedKey encoding:NSASCIIStringEncoding];
NSLog(#"derivedKey: %#", myString);
}
Is there any problem with algorithm which i am using in iOS
Use the Common Crypto CCKeyDerivationPBKDF function with the option kCCPRFHmacAlgSHA1.
Note PBEKeySpec keyLength is in bits, CCKeyDerivationPBKDF derivedKeyLen is in bytes.
For a more detailed answer provide all input (hashKey, salt) and the output in hex dump format plus the number of rounds, output length in bytes.
See this SO answer for sample code.
Update for revised question code:
CCKeyDerivationPBKDF returns 8-bit data bytes that is essentially not characters and many are not printable even if forced into NSASCIIStringEncoding. Forcing to NSASCIIStringEncoding even if there is no error returned is incorrect and non-useful. Instead either use the returned NSData or convert to Base64 or HexASCII encoding.
Change
NSString *myString = [[NSString alloc] initWithData:derivedKey encoding:NSASCIIStringEncoding];
Output: A´Öº÷"ùïó
to
NSString * myString = [derivedKey base64EncodedStringWithOptions:0];
Output: QbTWgbr3FSL57/MfBQAz4A==
Note: 1000 rounds is generally considered insufficient, something in the 10,000 to 100,000 range should be used.
Timings on an iPhone 6S:
rounds seconds
1000 0.003
10000 0.032
100000 0.309
1000000 3.047
I am trying to convert a nsstring with hex values into a float value.
NSString *hexString = #"3f9d70a4";
The float value should be = 1.230.
Some ways I have tried to solve this are:
1.NSScanner
-(unsigned int)strfloatvalue:(NSString *)str
{
float outVal;
NSString *newStr = [NSString stringWithFormat:#"0x%#",str];
NSScanner* scanner = [NSScanner scannerWithString:newStr];
NSLog(#"string %#",newStr);
bool test = [scanner scanHexFloat:&outVal];
NSLog(#"scanner result %d = %a (or %f)",test,outVal,outVal);
return outVal;
}
results:
string 0x3f9d70a4
scanner result 1 = 0x1.fceb86p+29 (or 1067282624.000000)
2.casting pointers
NSNumber * xPtr = [NSNumber numberWithFloat:[(NSNumber *)#"3f9d70a4" floatValue]];
result:3.000000
What you have is not a "hexadecimal float", as is produced by the %a string format and scanned by scanHexFloat: but the hexadecimal representation of a 32-bit floating-point value - i.e. the actual bits.
To convert this back to a float in C requires messing with the type system - to give you access to the bytes that make up a floating-point value. You can do this with a union:
typedef union { float f; uint32_t i; } FloatInt;
This type is similar to a struct but the fields are overlaid on top of each other. You should understand that doing this kind of manipulation requires you understand the storage formats, are aware of endian order, etc. Do not do this lightly.
Now you have the above type you can scan a hexadecimal integer and interpret the resultant bytes as a floating-point number:
FloatInt fl;
NSScanner *scanner = [NSScanner scannerWithString:#"3f9d70a4"];
if([scanner scanHexInt:&fl.i]) // scan into the i field
{
NSLog(#"%x -> %f", fl.i, fl.f); // display the f field, interpreting the bytes of i as a float
}
else
{
// parse error
}
This works, but again consider carefully what you are doing.
HTH
I think a better solutions is a workaround like this :
-(float) getFloat:(NSInteger*)pIndex
{
NSInteger index = *pIndex;
NSData* data = [self subDataFromIndex:&index withLength:4];
*pIndex = index;
uint32_t hostData = CFSwapInt32BigToHost(*(const uint32_t *)[data bytes]);
return *(float *)(&hostData);;
}
Where your parameter is an NSData which rapresents the number in HEX format, and the input parameter is a pointer to the element of NSData.
So basically you are trying to make an NSString to C's float, there's an old fashion way to do that!
NSString* hexString = #"3f9d70a4";
const char* cHexString = [hexString UTF8String];
long l = strtol(cHexString, NULL, 16);
float f = *((float *) &l);
// f = 1.23
for more detail please see this answer
I am trying to create a 16 byte and later 32 byte initialization vector in objective-c (Mac OS). I took some code on how to create random bytes and modified it to 16 bytes, but I have some difficulty with this. The NSData dumps the hex, but an NSString dump gives nil, and a cstring NSLog gives the wrong number of characters (not reproduced the same in the dump here).
Here is my terminal output:
2012-01-07 14:29:07.705 Test3Test[4633:80f] iv hex <48ea262d efd8f5f5 f8021126 fd74c9fd>
2012-01-07 14:29:07.710 Test3Test[4633:80f] IV string: (null)
2012-01-07 14:29:07.711 Test3Test[4633:80f] IV char string t^Q¶�^��^A
Here is the main program:
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
//NSString *iv_string = [NSString stringWithCString:iv encoding:NSUTF8StringEncoding];
testclass *obj = [testclass alloc];
NSData *iv_data = [obj createRandomNSData];
//[iv_string dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"iv hex %#",iv_data);
//NSString *iv_string = [[NSString alloc] initWithBytes:[iv_data bytes] length:16 encoding:NSUTF8StringE$
NSString *iv_string = [[NSString alloc] initWithData:iv_data encoding:NSUTF8StringEncoding];
NSLog(#"IV string: %#",iv_string);
NSLog(#"IV char string %.*s",[iv_data bytes]);
return 0;
]
(I left in the above some commented code that I tried and did not work also).
Below is my random number generater, taken from a stack overflow example:
#implementation testclass
-(NSData*)createRandomNSData
{
int twentyMb = 16;
NSMutableData* theData = [NSMutableData dataWithCapacity:twentyMb];
for( unsigned int i = 0 ; i < twentyMb/4 ; ++i )
{
u_int32_t randomBits = arc4random();
[theData appendBytes:(void*)&randomBits length:4];
}
NSData *data = [NSData dataWithData:theData];
[theData dealloc];
return data;
}
#end
I am really quite clueless as to what could be the problem here. If I have data as bytes, it should convert to a string or not necessarily? I have looked over the relevant examples here on stackoverflow, but none of them have worked in this situation.
Thanks,
Elijah
An arbitrary byte sequence may not be legal UTF8 encoding. As #Joachim Isaksson notes, there is seldom reason to convert to strings this way. If you need to store random data as a string, you should use an encoding scheme like Base64, serialize the NSData to a plist, or similar approach. You cannot simply use a cstring either, since NULL is legal inside of a random byte sequence, but is not legal inside of a cstring.
You do not need to build your own random byte creator on Mac or iOS. There's one built-in called SecRandomCopyBytes(). For example (from Properly encrypting with AES with CommonCrypto):
+ (NSData *)randomDataOfLength:(size_t)length {
NSMutableData *data = [NSMutableData dataWithLength:length];
int result = SecRandomCopyBytes(kSecRandomDefault,
length,
data.mutableBytes);
NSAssert(result == 0, #"Unable to generate random bytes: %d",
errno);
return data;
}
When converting NSData to NSString using an UTF8 encoding, you won't necessarily end up with the same number of bytes since not all binary values are valid encodings of characters. I'd say using a string for binary data is a recipe for problems.
What is the use of the string? NSData is exactly the datatype you want for storing binary data to begin with.
I have an NSString like so:
#"200hello"
or
#"0 something"
What I would like to be able to do is take the first occuring number in the NSString and convert it into an int.
So that #"200hello" would become int = 200.
and #"0 something" would become int = 0.
int value;
BOOL success = [[NSScanner scannerWithString:#"1000safkaj"] scanInteger:&value];
If the number is not always at the beginning:
NSCharacterSet* nonDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
int value = [[#"adfsdg1000safkaj" stringByTrimmingCharactersInSet:nonDigits] intValue];
Steve Ciarcia once said a single measured result is worth more than a hundred engineers opinions. And so begins the first, and last, "How to get an int value from a NSString" cook-off!
The following are the contenders: (microseconds taken and number of bytes used per match using the incredibly high precision for(x=0; x<100000; x++) {} micro-benchmark that has been handed down through the generations. Time measured via getrusage(), bytes used via malloc_size(). The string to be matched was normalized to 'foo 2020hello' for all cases, except those that required the number to be at the start. All conversions were normalized to 'int'. The two numbers after the time are normalized results relative to the best and worst performers.)
EDIT: These were the original numbers posted, see below for updated numbers. Also, times are from a 2.66 Core2 macbook pro.
characterSet time: 1.36803us 12.5 / 1.00 memory: 64 bytes (via Nikolai Ruhe)
original RKL time: 1.20686us 11.0 / 0.88 memory: 16 bytes (via Dave DeLong)
modified RKL time: 1.07631us 9.9 / 0.78 memory: 16 bytes (me, changed regex to \d+)
scannerScanInt time: 0.49951us 4.6 / 0.36 memory: 32 bytes (via Nikolai Ruhe)
intValue time: 0.16739us 1.5 / 0.12 memory: 0 bytes (via zpasternack)
rklIntValue time: 0.10925us 1.0 / 0.08 memory: 0 bytes (me, modified RKL example)
As I noted somewhere else in this message, I originally threw this in to a unit test harness I use for RegexKitLite. Well, being the unit test harness meant that I was testing with my private copy of RegexKitLite... which just so happened to have a bunch of debug stuff tacked on while tracking down a bug report from a user. The above timing results are approximately equivalent to calling [valueString flushCachedRegexData]; inside the for() {} timing loop (which was essentially what the inadvertent debugging stuff was doing). The following results are from compiling against the latest, unmodified, RegexKitLite available (3.1):
characterSet time: 1.36803us 12.5 / 1.00 memory: 64 bytes (via Nikolai Ruhe)
original RKL time: 0.58446us 5.3 / 0.43 memory: 16 bytes (via Dave DeLong)
modified RKL time: 0.54628us 5.0 / 0.40 memory: 16 bytes (me, changed regex to \d+)
scannerScanInt time: 0.49951us 4.6 / 0.36 memory: 32 bytes (via Nikolai Ruhe)
intValue time: 0.16739us 1.5 / 0.12 memory: 0 bytes (via zpasternack)
rklIntValue time: 0.10925us 1.0 / 0.08 memory: 0 bytes (me, modified RKL example)
This is slightly better than a 50% improvement. If you're willing to live slightly dangerously, you can coax a bit more speed out with the -DRKL_FAST_MUTABLE_CHECK compile time option:
original RKL time: 0.51188us 4.7 / 0.37 memory: 16 bytes using intValue
modified RKL time: 0.47665us 4.4 / 0.35 memory: 16 bytes using intValue
original RKL time: 0.44337us 4.1 / 0.32 memory: 16 bytes using rklIntValue
modified RKL time: 0.42128us 3.9 / 0.31 memory: 16 bytes using rklIntValue
This is usually good for about another 10% boost, and it's fairly safe to use (for more info, see the RKL docs). And while I was at it... why not use the faster rklIntValue too? Is there some kind of prize for beating the native, built in Foundation methods using an external, third party, non-integrated general purpose regex pattern matching engine? Don't believe the hype that "regexes are slow".
END EDIT
The RegexKitLite example can be found at RegexKitLite Fast Hex Conversion. Basically swapped strtoimax for strtol, and added a line of code to skip over leading characters that weren't [+-0-9]. (full disclosure: I'm the author of RegexKitLite)
Both 'scannerScanInt' and 'intValue' suffer from the problem that the number to be extracted must be at the start of the string. I think both will skip any leading white-space.
I modified Dave DeLongs regex from '[^\d]*(\d+)' to just '\d+' because that's all that's really needed, and it manages to get rid of a capture group usage to boot.
So, based on the above data, I offer the following recommendations:
There's basically two different capability classes here: Those that can tolerate extra 'stuff' and still get you the number (characterSet, RegexKitLite matchers, and rklIntValue), and those that basically need the number to be the very first thing in the string, tolerating at most some white space padding at the start (scannerScanInt and intValue).
Do not use NSCharacterClass to do these kinds of things. For the given example, 16 bytes is used to instantiate the first NSCharacterClass, then 32 bytes for the inverted version, and finally 16 bytes for the string result. The fact that a general purpose regex engine outperforms it by a double digit percentage margin while using less memory pretty much seals the deal.
(keep in mind I wrote RegexKitLite, so take the following with whatever sized grain of salt you feel is appropriate).
RegexKitLite turns in good times and uses the smallest amount of memory possible considering the fact that it's returning a NSString object. Since it uses a LRU cache internally for all the ICU regex engine stuff, those costs get amortized over time and repeated uses. It also takes seconds to change the regex if the need comes up (hex values? hex floats? Currencies? Dates? No problem.)
For the simple matchers, it should be obvious that you definitely should NOT use NSScanner to do these kinds of things. Using NSScanner to do a 'scanInt:' is no different than just calling [aString intValue]. The produce the same results with the same caveats. The difference is NSScanner takes FIVE times longer to the same thing, while wasting 32 bytes of memory in the process.... while [aString intValue] (probably) doesn't require one byte of memory to perform its magic- it probably just calls strtoimax() (or an equivalent) and since it has direct access to the pointer holding the strings contents....
The final one is 'rklIntValue', which again is just a slightly tweaked version of what you can find at (the 'RegexKitLite Fast Hex Conversion' link above, stackoverflow won't let me post it twice). It uses CoreFoundation to try to get direct access to the strings buffer, and failing that, allocates some space off the stack and copies a chunk of the string to that buffer. This takes all of, oh, three instructions on the CPU, and is fundamentally impossible to 'leak' like a malloc() allocation. So it uses zero memory and goes very, very fast. As an extra bonus, you pass to strtoXXX() the number base of the string to convert. 10 for decimal, 16 for hex (automatically swallowing a leading 0x if present), or 0 for automagic detection. It's a trivial, single line of code to skip the pointer over any 'uninteresting' characters until you get to what you want (I choose -,+, and 0-9). Also trivial to swap in something like strtod() if you need to parse double values. strtod() converts just about any valid floating point text: NAN, INF, hex floats, you name it.
EDIT:
Per request of the OP, here's a trimmed and minified version of the code that I used to perform the tests. One thing of note: While putting this together, I noticed that Dave DeLongs original regex didn't quite work. The problem is in the negated character set- meta-character sequences inside sets (ie, [^\d]+) mean the literal character, not the special meaning they have outside the character set. Replaced with [^\p{DecimalNumber}]*, which has the intended effect.
I originally bolted this stuff to a RegexKitLite unit test harness, so I left some bits and pieces for GC in. I forgot all about this, but the short version of what happens when GC is turned on is that times of everything BUT RegexKitLite double (that is, takes twice as long). RKL only takes about 75% longer (and that took an enormous, non-trivial amount of effort to get when I was developing it). The rklIntValue time stays exactly the same.
Compile with shell% gcc -DNS_BLOCK_ASSERTIONS -mdynamic-no-pic -std=gnu99 -O -o stackOverflow stackOverflow.m RegexKitLite.m -framework Foundation -licucore -lauto
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdint.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <objc/objc-auto.h>
#include <malloc/malloc.h>
#import <Foundation/Foundation.h>
#import "RegexKitLite.h"
static double cpuTimeUsed(void);
static double cpuTimeUsed(void) {
struct rusage currentRusage;
getrusage(RUSAGE_SELF, ¤tRusage);
double userCPUTime = ((((double)currentRusage.ru_utime.tv_sec) * 1000000.0) + ((double)currentRusage.ru_utime.tv_usec)) / 1000000.0;
double systemCPUTime = ((((double)currentRusage.ru_stime.tv_sec) * 1000000.0) + ((double)currentRusage.ru_stime.tv_usec)) / 1000000.0;
double CPUTime = userCPUTime + systemCPUTime;
return(CPUTime);
}
#interface NSString (IntConversion)
-(int)rklIntValue;
#end
#implementation NSString (IntConversion)
-(int)rklIntValue
{
CFStringRef cfSelf = (CFStringRef)self;
UInt8 buffer[64];
const char *cptr, *optr;
char c;
if((cptr = optr = CFStringGetCStringPtr(cfSelf, kCFStringEncodingMacRoman)) == NULL) {
CFRange range = CFRangeMake(0L, CFStringGetLength(cfSelf));
CFIndex usedBytes = 0L;
CFStringGetBytes(cfSelf, range, kCFStringEncodingUTF8, '?', false, buffer, 60L, &usedBytes);
buffer[usedBytes] = 0U;
cptr = optr = (const char *)buffer;
}
while(((cptr - optr) < 60) && (!((((c = *cptr) >= '0') && (c <= '9')) || (c == '-') || (c == '+'))) ) { cptr++; }
return((int)strtoimax(cptr, NULL, 0));
}
#end
int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
#ifdef __OBJC_GC__
objc_start_collector_thread();
objc_clear_stack(OBJC_CLEAR_RESIDENT_STACK);
objc_collect(OBJC_EXHAUSTIVE_COLLECTION | OBJC_WAIT_UNTIL_DONE);
#endif
BOOL gcEnabled = ([objc_getClass("NSGarbageCollector") defaultCollector] != NULL) ? YES : NO;
NSLog(#"Garbage Collection is: %#", gcEnabled ? #"ON" : #"OFF");
NSLog(#"Architecture: %#", (sizeof(void *) == 4UL) ? #"32-bit" : #"64-bit");
double startTime = 0.0, csTime = 0.0, reTime = 0.0, re2Time = 0.0, ivTime = 0.0, scTime = 0.0, rklTime = 0.0;
NSString *valueString = #"foo 2020hello", *value2String = #"2020hello";
NSString *reRegex = #"[^\\p{DecimalNumber}]*(\\d+)", *re2Regex = #"\\d+";
int value = 0;
NSUInteger x = 0UL;
{
NSCharacterSet *digits = [NSCharacterSet decimalDigitCharacterSet];
NSCharacterSet *nonDigits = [digits invertedSet];
NSScanner *scanner = [NSScanner scannerWithString:value2String];
NSString *csIntString = [valueString stringByTrimmingCharactersInSet:nonDigits];
NSString *reString = [valueString stringByMatching:reRegex capture:1L];
NSString *re2String = [valueString stringByMatching:re2Regex];
[scanner scanInt:&value];
NSLog(#"digits : %p, size: %lu", digits, malloc_size(digits));
NSLog(#"nonDigits : %p, size: %lu", nonDigits, malloc_size(nonDigits));
NSLog(#"scanner : %p, size: %lu, int: %d", scanner, malloc_size(scanner), value);
NSLog(#"csIntString : %p, size: %lu, '%#' int: %d", csIntString, malloc_size(csIntString), csIntString, [csIntString intValue]);
NSLog(#"reString : %p, size: %lu, '%#' int: %d", reString, malloc_size(reString), reString, [reString intValue]);
NSLog(#"re2String : %p, size: %lu, '%#' int: %d", re2String, malloc_size(re2String), re2String, [re2String intValue]);
NSLog(#"intValue : %d", [value2String intValue]);
NSLog(#"rklIntValue : %d", [valueString rklIntValue]);
}
for(x = 0UL, startTime = cpuTimeUsed(); x < 100000UL; x++) { value = [[valueString stringByTrimmingCharactersInSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]] intValue]; } csTime = (cpuTimeUsed() - startTime) / (double)x;
for(x = 0UL, startTime = cpuTimeUsed(); x < 100000UL; x++) { value = [[valueString stringByMatching:reRegex capture:1L] intValue]; } reTime = (cpuTimeUsed() - startTime) / (double)x;
for(x = 0UL, startTime = cpuTimeUsed(); x < 100000UL; x++) { value = [[valueString stringByMatching:re2Regex] intValue]; } re2Time = (cpuTimeUsed() - startTime) / (double)x;
for(x = 0UL, startTime = cpuTimeUsed(); x < 100000UL; x++) { value = [valueString rklIntValue]; } rklTime = (cpuTimeUsed() - startTime) / (double)x;
for(x = 0UL, startTime = cpuTimeUsed(); x < 100000UL; x++) { value = [value2String intValue]; } ivTime = (cpuTimeUsed() - startTime) / (double)x;
for(x = 0UL, startTime = cpuTimeUsed(); x < 100000UL; x++) { [[NSScanner scannerWithString:value2String] scanInt:&value]; } scTime = (cpuTimeUsed() - startTime) / (double)x;
NSLog(#"csTime : %.5lfus", csTime * 1000000.0);
NSLog(#"reTime : %.5lfus", reTime * 1000000.0);
NSLog(#"re2Time: %.5lfus", re2Time * 1000000.0);
NSLog(#"scTime : %.5lfus", scTime * 1000000.0);
NSLog(#"ivTime : %.5lfus", ivTime * 1000000.0);
NSLog(#"rklTime: %.5lfus", rklTime * 1000000.0);
[NSString clearStringCache];
[pool release]; pool = NULL;
return(0);
}
If the int value is always at the beginning of the string, you can simply use intValue.
NSString *string = #"123hello";
int myInt = [string intValue];
I would probably use a regular expression (implemented with the stellar RegexKitLite). Then it'd be something like:
#import "RegexKitLite.h"
NSString * original = #"foo 220hello";
NSString * number = [original stringByMatching:#"[^\\d]*(\\d+)" capture:1];
return [number integerValue];
The regex #"[^\d]*(\d+)" means "any number of non-numeric characters followed by at least one numeric character".
I came up with my own answer, potentially faster and easier than the others provided.
My answer does assume you know the position the number begins and ends though...
NSString *myString = #"21sss";
int numberAtStart = [[myString substringToIndex:2] intValue];
You can get to to work the other way too:
NSString *myString = #"sss22";
int numberAtEnd = [[myString substringFromIndex:3] intValue];
int i;
NSString* string;
i = [string intValue];
Is there a simple way to do something like..
[NSMagicDataConverter humanStringWithBytes:20000000]
..which would return "19.1MB"?
Starting in OS X 10.8 and iOS 6, you can use NSByteCountFormatter.
Your example would look like this:
[NSByteCountFormatter stringFromByteCount:20000000 countStyle:NSByteCountFormatterCountStyleFile];
I would mush this into an NSFormatter subclass.
#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
Usage:
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"
Here's my own take on the problem:
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]];
}
By default (if 0 is passed for flags), it will output SI units (base ten). You can set kUnitStringBinaryUnits to select binary (base two) units suitable for memory, or kUnitStringOSNativeUnits to have the unit type selected automatically based on OS version (pre-Leopard gets base two, post-Leopard gets base ten). Setting kUnitStringLocalizedFormat formats the string based on the user's current locale. For example:
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)
Here's the helper function required for OS-native units:
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]);
}
Here is a more Objective C-like function (uses NSNumber, NSArray, NSStirng, etc...) for doing this conversion.
This is based on Sidnicious's answer, so a big thanks for the initial work done there. Also based on Wikipedia articles.
Use it generally like this: [HumanReadableDataSizeHelper humanReadableSizeFromBytes:[NSNumber numberWithDouble:doubleValue]].
But, it looks like you want SI units with a 1024 multiplier so you would use it like this: [HumanReadableDataSizeHelper humanReadableSizeFromBytes:[NSNumber numberWithDouble:doubleValue] useSiPrefixes:YES useSiMultiplier:NO]
The reason I default to binary prefixes (ki, Mi) is because those seem to be the most appropriate unit prefix set to use for sizes of data on a computer. What you requested was the SI unit prefixes but using a multiplier of 1024, technically incorrect. Though I will note that SI prefixes for multiples of 1024 is fairly common and binary prefixes are not well accepted (according to 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
You can use FormatterKit and its TTTUnitOfInformationFormatter class:
https://github.com/mattt/FormatterKit
It is also available through CocoaPods with:
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]];
}
I know the questions is for Obj C but if anyone looking for a swift version:
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])"
}