7

I used the below code to extract numbers from inputString using NSScanner

NSString *inputString = @"Dhoni7 notout at183*runs in 145andhehit15four's and10sixers100";
NSString *numberString;
NSArray *elements = [inputString componentsSeparatedByString:@" "];
for (int i=0; i<[elements count];i++)
{
    NSScanner *scanner = [NSScanner scannerWithString:[elements objectAtIndex:i]];
    NSCharacterSet *numbers = [NSCharacterSet characterSetWithCharactersInString:@"1234567890"];

    // Throw away characters before the first number.
    [scanner scanUpToCharactersFromSet:numbers intoString:NULL];

    // Collect numbers.
    [scanner scanCharactersFromSet:numbers intoString:&numberString];

    // Result.
    int number = [numberString integerValue];
    if (number != 0)
    {
        NSLog(@"%d\n",number);
        numberString = nil;
    }
}

My expected output is 7 183 145 15 10 100

but the output I am getting is 7 183 145 10

It just extracts first occurrence of a number from each word. Eg: if its Dho7ni89 it just detects the 7 and doesn't detect 89. I would be really happy if someone helps me figure out a way to fix this.

luk2302
  • 55,258
  • 23
  • 97
  • 137
Manju Basha
  • 665
  • 1
  • 9
  • 29
  • @matt Absolutely correct. Why would someone down vote this question? It's got a reasonable attempt at the answer at least it's not just another "write this for me" question. – Droppy May 24 '15 at 15:40

2 Answers2

10

Matt was a little bit faster. Why do you split your string in the beginning?
A little code rearrangement will yield the desired result:

NSScanner *scanner = [NSScanner scannerWithString:@"Dhoni7 notout at183*runs in 145andhehit15four's and10sixers100"];
NSCharacterSet *numbers = [NSCharacterSet characterSetWithCharactersInString:@"1234567890"];

while (true) {
    // Throw away characters before a number
    [scanner scanUpToCharactersFromSet:numbers intoString:NULL];

    // Read the number
    NSString *numberString;
    if ([scanner scanCharactersFromSet:numbers intoString:&numberString]) {
        long number = [numberString integerValue];
        NSLog(@"%ld\n",number);
    } else {
        break;
    }
}

Outputting

7 183 145 15 10 100

NOTE
You could also use

NSCharacterSet *numbers = [NSCharacterSet decimalDigitCharacterSet];

to filter out the numbers. I find this a bit neater than listing the chars. It will however include, for example, the decimal digits of the Indic scripts and Arabic as well.

luk2302
  • 55,258
  • 23
  • 97
  • 137
  • Actually this is even better than what I said, because instead of testing `isAtEnd`, you use the fact that `scan...` returns a BOOL. - It is scary writing what looks like an endless loop with `while(true)`, but that in fact is the correct approach. – matt May 24 '15 at 15:19
  • Yes, i know that `while (true)` is not the best but I do not think there is a way of putting both scanning operations in the condition. You could add a bool-value indicating that it has not found a number any more though... – luk2302 May 24 '15 at 15:28
  • 1
    Isn't zero a number too? You should use `strtol()` to detect the number as that allows for error-detection; something `[NSString integerValue]` does not. – Droppy May 24 '15 at 15:34
  • well, i need to clean the code a bit, that is true, however a `0` will be detected. – luk2302 May 24 '15 at 15:37
  • 1
    That's much better and my suggestion of using `strtol()` doesn't really help in this situation, given you know there can only be numbers in `numberString`. – Droppy May 24 '15 at 15:39
8

Your mistake is simple: you have started by making a false assumption. You artificially separate the string into words:

NSArray *elements = [inputString componentsSeparatedByString:@" "];
for (int i=0; i<[elements count];i++) {
    // scan for one number
}

Thus, that is exactly what happens. Each word is scanned for one number, once. If a word contains two numbers, the second number is never scanned - because that is not what you said to do.

The solution is to stop making that false assumption. Don't separate the string into words at all! Just keep repeating the process:

* scan up to a number
* scan the number

...until you reach the end of the scanner's string (isAtEnd).

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 3
    Of course I could actually write out the code for you, but that would not leave you with any challenge! I prefer to _teach_ you how to _think_ about the problem and let _you_ solve it. You are so close to having all the code you need already...! – matt May 24 '15 at 15:14
  • @matt : That worked perfectly for me. Thanks a bunch. It was very thoughtful of you to help me figure it out myself :) All I did was, removing the string separation code and replacing for loop with `while ([scanner isAtEnd] == NO)` – Manju Basha May 25 '15 at 03:11