Question

To begin, I am writing an iOS 5 app. By way of example, say that I have the following string:

100 - PARK STREET / JAMES PLACE

I would like to extract the two road names from this string in the most efficient (and code-elegant) way possible. I have tried combinations of using [string componentsSeparatedByString...] etc. but this gets very messy, very quickly. Additionally, it requires a large amount of conditional statements to handle a situation such as the following:

100 - BI-CENTENNIAL DRIVE / JAMES PLACE

since that contains a nested hyphen which would be split if we were using [string componentsSeparatedByString:@"-"] and require reassembly.

There are also situations where the string may have a slightly different format, such as:

100- BI-CENTENNIAL DRIVE / JAMES PLACE

(lack of a space between the number and hyphen)

100-BI-CENTENNIAL DRIVE /JAMES PLACE

(lack of any spaces surrounding the number at all, combined with no space between the slash and the second road name)

However, we can always assume that there will only be one slash in the string which separates the two road names.

The road names should also be stripped of any leading and trailing spaces.

I figured that this entire process would be possible in a more efficient and elegant manner using an NSScanner but unfortunately I don't have the necessary experience with this class to make it work. Any suggestions would be greatly appreciated.

Was it helpful?

Solution

You could also use Regular Expression.

Note that in the block I use capture blocks, via [result rangeAtIndex:i].
Index 1 will now be the house number, index 2 will return the first street and 3 the second street.

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {
        NSArray *streets = [NSArray arrayWithObjects:@"100 - PARK STREET / JAMES PLACE", @"100 - BI-CENTENNIAL DRIVE / JAMES PLACE", @"100- BI-CENTENNIAL DRIVE / JAMES PLACE", @"100-BI-CENTENNIAL DRIVE /JAMES PLACE", nil];

        NSString *text = [streets componentsJoinedByString:@" "];
        NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(\\d+) {0,1}- {0,1}(\\D+) *\\/ *(\\D+)" options:NSRegularExpressionCaseInsensitive error:nil];

        [regex enumerateMatchesInString:text options:0 
                                  range:NSMakeRange(0, [text length]) 
                             usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) 
        {
            for (int i = 1; i< [result numberOfRanges] ; i++) {
                NSLog(@"%@", [text substringWithRange:[result rangeAtIndex:i]]);
            }
        }];
    }
    return 0;
}

output:

100
PARK STREET 
JAMES PLACE 
100
BI-CENTENNIAL DRIVE 
JAMES PLACE 
100
BI-CENTENNIAL DRIVE 
JAMES PLACE 
100
BI-CENTENNIAL DRIVE 
JAMES PLACE

edit in response to the comments

int main (int argc, const char * argv[])
{

    @autoreleasepool {
        NSArray *streets = [NSArray arrayWithObjects:@"100 - PARK STREET / JAMES PLACE", @"100 - BI-CENTENNIAL DRIVE / JAMES PLACE", @"100- BI-CENTENNIAL DRIVE / JAMES PLACE", @"100-BI-CENTENNIAL DRIVE /JAMES PLACE",@"100 - PARK STREET", nil];

        NSRegularExpression *regex1 = [NSRegularExpression regularExpressionWithPattern:@"(\\d+) *- *([^\\/]+) *$" options:NSRegularExpressionCaseInsensitive error:nil];
        NSRegularExpression *regex2 = [NSRegularExpression regularExpressionWithPattern:@"(\\d+) *- *([^\\/]+) *\\/ *([^\\/]+) *$" options:NSRegularExpressionCaseInsensitive error:nil];
        for (NSString *text in streets) {                        
            NSRegularExpression *regex = ([regex1 numberOfMatchesInString:text options:NSRegularExpressionCaseInsensitive range:NSMakeRange(0, [text length])]) ? regex1 : regex2;
            [regex enumerateMatchesInString:text options:0 
                                      range:NSMakeRange(0, [text length]) 
                                 usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) 
             {
                 for (int i = 1; i< [result numberOfRanges] ; i++) {
                     NSLog(@"%@", [text substringWithRange:[result rangeAtIndex:i]]);
                 }

             }];
        }
    }
    return 0;
}

second edit

int main (int argc, const char * argv[])
{

    @autoreleasepool {
        NSArray *streets = [NSArray arrayWithObjects:   @"100 - PARK STREET / JAMES PLACE", 
                                                        @"100 - BI-CENTENNIAL DRIVE / JAMES PLACE", 
                                                        @"100- BI-CENTENNIAL DRIVE / JAMES PLACE", 
                                                        @"100-BI-CENTENNIAL DRIVE /JAMES PLACE",
                                                        @"100 - PARK STREET",
                                                        @"100 - PARK STREET / ",
                                                        @"100 - PARK STREET/ ",
                                                        @"100 - PARK STREET/",
                            nil];

        NSRegularExpression *regex1 = [NSRegularExpression regularExpressionWithPattern:@"(\\d+) *- *([^\\/]+) *$" options:NSRegularExpressionCaseInsensitive error:nil];
        NSRegularExpression *regex2 = [NSRegularExpression regularExpressionWithPattern:@"(\\d+) *- *([^\\/]+) *\\/ *([^\\/]*) *$" options:NSRegularExpressionCaseInsensitive error:nil];
        for (NSString *text in streets) { 

            text= [text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
            NSLog(@"\n>%@<", text);
            NSRegularExpression *regex = ([regex1 numberOfMatchesInString:text options:NSRegularExpressionCaseInsensitive range:NSMakeRange(0, [text length])]) ? regex1 : regex2;
            [regex enumerateMatchesInString:text options:0 
                                      range:NSMakeRange(0, [text length]) 
                                 usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) 
             {
                 for (int i = 1; i< [result numberOfRanges] ; i++) {
                     NSLog(@"%@", [text substringWithRange:[result rangeAtIndex:i]]);
                 }

             }];
        }
    }
    return 0;
}

OTHER TIPS

Just coded in my browser:

NSString* line = @"100- BI-CENTENNIAL DRIVE / JAMES PLACE";
NSScanner* scanner = [NSScanner scannerWithString:line];
NSString* number;
if (![scanner scanUpToString:@"-" intoString:&number])
    /* handle parse failure */;
NSString* firstRoad;
if (![scanner scanUpToString:@"/" intoString:&firstRoad])
    /* handle parse failure */;
NSString* secondRoad = [str substringFromIndex:[scanner scanLocation]];

There may be additional whitespace to trim from the resulting strings.

This looks like a job for NSRegularExpression.

I think an R.E. something like

^[0-9]+ *- *(.*)$

will match what you want.

Here's another example of using this horrible little NSScanner class.

Supposing you had a string, containing four values, and wanted to convert them into a CGRect:

NSString* stringToParse = @"10, 20, 600, 150";             
CGRect rect = [self stringToCGRect:stringToParse];

NSLog(@"Rectangle: %.0f, %.0f, %.0f, %.0f", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);

To do this, you'd write a nasty little function like this:

-(CGRect)stringToCGRect:(NSString*)stringToParse
{
    NSLog(@"Parsing the string: %@", stringToParse);
    int x, y, wid, hei;

    NSString *subString;
    NSScanner *scanner = [NSScanner scannerWithString:stringToParse];
    [scanner scanUpToCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet] intoString:nil];
    [scanner scanCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet] intoString:&subString];
    x = [subString integerValue];

    [scanner scanUpToCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet] intoString:nil];
    [scanner scanCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet] intoString:&subString];
    y = [subString integerValue];

    [scanner scanUpToCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet] intoString:nil];
    [scanner scanCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet] intoString:&subString];
    wid = [subString integerValue];

    [scanner scanUpToCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet] intoString:nil];
    [scanner scanCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet] intoString:&subString];
    hei = [subString integerValue];

    CGRect rect = CGRectMake(x, y, wid, hei);
    return rect;
}

Forgive my negativity, but I'm tired, it's 10.30pm at night, and I despise having to write Objective-C code like this, knowing full well that using any Microsoft development environment from the past 15 years, this would've taken one line of code.

Grrrr....

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top