4

Possible Duplicate:
Removing leading zeroes from a string

I need to remove leading 0s in an NSString, a quick way I can think of is to convert the string to an NSNumber then convert back to NSString which will give me the clean string, although I'm not sure if it works. Is there any other way to do this?

Thanks

Community
  • 1
  • 1
hzxu
  • 5,753
  • 11
  • 60
  • 95

3 Answers3

11

So just for fun, I decided to time the various ways of doing this. This was by no means a scientific test, done mostly for my amusement, but some of you might like to see it anyway.

I created a method and substituted the various routines to process strings representing the numbers 0 - 100,000 all with three leading 0's.

Keep in mind however, that even the slowest method is going to be perfectly acceptable if you only have one (or even one hundred) of these strings to trim. (Using the number formatter takes about .000012039905 seconds, or about 1/100th of a millisecond.) Other things such as how easy your code is to read and understand is usually more important unless you really do need to process a large file full of strings like this. My personal favorite is still the regular expression, because it is relatively fast and immediately obvious what you are trying to accomplish, even without documentation.

Here are the results (from fastest to slowest):

Looping through the string

// I tried this by looping through the utf8String and got the same times
// (actually, ever so slightly longer, probably since it had to create the c string)
//
// I did find that using `[string getCharacters:buffer range:range]` and
// iterating over the character buffer that it took another 0.01 seconds
// off the time.  Hardly worth it though.  :)
NSUInteger length = [string length];
for (NSUInteger i = 0; i < length; i++)
{
    if ([string characterAtIndex:i] != '0')
    {
        return [string substringFromIndex:i];
    }
}
return @"0";

// Time 1: 0.210126
// Time 2: 0.219159
// Time 3: 0.201496

Converting to an int and then back to a NSString

int num = [string intValue];
return [NSString stringWithFormat:@"%d", num];

// Time 1: 0.322206
// Time 2: 0.345259
// Time 3: 0.324954

long long num = [string longLongValue];
return [NSString stringWithFormat:@"%lld", num];

// Time 1: 0.364318
// Time 2: 0.344946
// Time 3: 0.364761
// These are only slightly slower, but you can do bigger numbers using long long

Using a NSScanner

NSScanner *scanner    = [NSScanner scannerWithString:string];
NSCharacterSet *zeros = [NSCharacterSet
                         characterSetWithCharactersInString:@"0"];
[scanner scanCharactersFromSet:zeros intoString:NULL];

return [string substringFromIndex:[scanner scanLocation]];

// Time 1: 0.505277
// Time 2: 0.481884
// Time 3: 0.487209

Using a regular expression

NSRange range = [string rangeOfString:@"^0*" options:NSRegularExpressionSearch];
return [string stringByReplacingCharactersInRange:range withString:@""];

// Time 1: 0.610879
// Time 2: 0.645335
// Time 3: 0.637690

Using a static number formatter

static NSNumberFormatter *formatter = nil;
if (formatter == nil)
{
    formatter             = [NSNumberFormatter new];
    formatter.numberStyle = NSNumberFormatterDecimalStyle;
}

NSNumber *number = [formatter numberFromString:string];
return [formatter stringFromNumber:number];

// Time 1: 1.774198
// Time 2: 1.753013
// Time 3: 1.753893

Using a number formatter

NSNumberFormatter *formatter = [NSNumberFormatter new];
formatter.numberStyle        = NSNumberFormatterDecimalStyle;

NSNumber *number             = [formatter numberFromString:string];
return [formatter stringFromNumber:number];

// Time 1: 11.978336
// Time 2: 12.039905
// Time 3: 11.904984
// No wonder Apple recommends reusing number formatters!
lnafziger
  • 25,760
  • 8
  • 60
  • 101
  • Excellent work! How about if you use a compiled `NSRegularExpression` rather than the `rangeOfString:options:` with `NSRegularExpressionSearch`? I imagine that using the same compiled regular expression each time will be notably faster. – dreamlax Nov 13 '12 at 05:08
  • @dreamlax: I started to do that one but don't know the regex routines well enough to put that one together quickly. If you know it better and can give me an example, I can get a time for it easily. – lnafziger Nov 13 '12 at 05:11
  • Never mind about it, I gave it a go myself and it was only about 10% faster in my case. I thought it would be a bit more substantial than that. It's an improvement nonetheless but not one worth fretting about unless you are doing millions and millions of these replacements :) – dreamlax Nov 13 '12 at 05:59
  • Cool, if you want to share the technique for anyone that might want it, I can add it to the list. – lnafziger Nov 13 '12 at 06:12
5

You could use regular expressions.

NSString *numStr = @"0001234";
NSRange range = [numStr rangeOfString:@"^0*" options:NSRegularExpressionSearch];
NSString *result = [numStr stringByReplacingCharactersInRange:range withString:@""];

If you doing it a lot (as in thousands of times) you may benefit from using a compiled NSRegularExpression.

Rui Peres
  • 25,741
  • 9
  • 87
  • 137
dreamlax
  • 93,976
  • 29
  • 161
  • 209
  • +1, I like this approach better than the ones taken on the duplicate question. (Go add it there too, lol.) – lnafziger Nov 13 '12 at 03:28
  • I've tried to search some methods of NSString like findFirstNotOf but in vain. Your answer is more useful than mine. I'll vote you up. – sunkehappy Nov 13 '12 at 04:14
2

This works, I've tested it. And I think this is a very good way. Less code less bug. KISS.

NSString *numStr = @"000123";
int num = numStr.intValue;
numStr = [NSString stringWithFormat:@"%d", num];
sunkehappy
  • 8,970
  • 5
  • 44
  • 65
  • This is limited to the range of `int` but should suffice for most purposes. – dreamlax Nov 13 '12 at 03:23
  • Yeah, this is straight-forward and not terribly inefficient. Probably a simple loop on characterAtIndex followed by a substring op would be faster, but much more complex and error-prone. – Hot Licks Nov 13 '12 at 03:26
  • @HotLicks: Actually, this is just barely less efficient than the method that you propose, and is about twice as fast as the regex method. I'll post my times shortly. – lnafziger Nov 13 '12 at 04:27
  • @lnafziger -- Yeah, with either scheme the creation of the new string is going to be the lion's share of the work, swamping the character counting or number formatting. Fastest of all would be passing just the offset to the string to a next stage that will somehow be doing a substring or whatever anyway, but that scenario is unlikely. – Hot Licks Nov 13 '12 at 12:32
  • By the way, for people who want to use this technique: there is almost no performance penalty when using a long long instead of an int and will work with MUCH larger numbers. – lnafziger Nov 13 '12 at 16:06