7

What would be the most efficient way to convert a string like "ThisStringIsJoined" to "This String Is Joined" in objective-c?

I receive strings like this from a web service thats out of my control and I would like to present the data to the user, so I would just like to tidy it up a bit by adding spaces infront of each uppercase word. The strings are always formatted with each word beginning in an uppercase letter.

I'm quite new to objective-c so cant really figure this one out.

Thanks

Craigt
  • 3,418
  • 6
  • 40
  • 56
  • Possible duplicate: http://stackoverflow.com/questions/1588205/how-to-split-a-string-with-the-uppercase-character-in-iphone – 0x8badf00d Sep 06 '11 at 15:43

6 Answers6

39

One way of achieving this is as follows:

NSString *string = @"ThisStringIsJoined";
NSRegularExpression *regexp = [NSRegularExpression 
    regularExpressionWithPattern:@"([a-z])([A-Z])" 
    options:0 
    error:NULL];
NSString *newString = [regexp 
    stringByReplacingMatchesInString:string 
    options:0 
    range:NSMakeRange(0, string.length) 
    withTemplate:@"$1 $2"];
NSLog(@"Changed '%@' -> '%@'", string, newString);

The output in this case would be:

'ThisStringIsJoined' -> 'This String Is Joined'

You might want to tweak the regular expression to you own needs. You might want to make this into a category on NSString.

Ascendant
  • 2,430
  • 3
  • 26
  • 34
aLevelOfIndirection
  • 3,522
  • 14
  • 18
9

NSRegularExpressions are the way to go, but as trivia, NSCharacterSet can also be useful:

- (NSString *)splitString:(NSString *)inputString {

    int index = 1;
    NSMutableString* mutableInputString = [NSMutableString stringWithString:inputString];

    while (index < mutableInputString.length) {

        if ([[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:[mutableInputString characterAtIndex:index]]) {
            [mutableInputString insertString:@" " atIndex:index];
            index++;
        }
        index++;
    }

    return [NSString stringWithString:mutableInputString];
}
Matt Wilding
  • 20,115
  • 3
  • 67
  • 95
1

Here's a category on NSString that will do what you want. This will handle non-ASCII letters. It will also split "IDidAGoodThing" properly.

@implementation NSString (SeparateCapitalizedWords)

-(NSString*)stringBySeparatingCapitalizedWords
{
    static NSRegularExpression * __regex ;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSError * error = nil ;
        __regex = [ NSRegularExpression regularExpressionWithPattern:@"[\\p{Uppercase Letter}]" options:0 error:&error ] ;
        if ( error ) { @throw error ; }
    });

    NSString * result = [ __regex stringByReplacingMatchesInString:self options:0 range:(NSRange){ 1, self.length - 1 } withTemplate:@" $0" ] ;
    return result ;
}

@end
nielsbot
  • 15,922
  • 4
  • 48
  • 73
1

Here is Swift Code (objective c code by webstersx), Thanks !

var str: NSMutableString = "iLoveSwiftCode"

        var str2: NSMutableString = NSMutableString()

        for var i:NSInteger = 0 ; i < str.length ; i++ {

            var ch:NSString = str.substringWithRange(NSMakeRange(i, 1))
            if(ch .rangeOfCharacterFromSet(NSCharacterSet.uppercaseLetterCharacterSet()).location != NSNotFound) {
            str2 .appendString(" ")
            }
            str2 .appendString(ch)
        }
        println("\(str2.capitalizedString)")

    }

Output : I Love Swift Code

0

For anyone who came here looking for the similar question answered in Swift: Perhaps a cleaner (adding to Sankalp's answer), and more 'Swifty' approach:

func addSpaces(to givenString: String) -> String{
   var string = givenString

   //indexOffset is needed because each time replaceSubrange is called, the resulting count is incremented by one (owing to the fact that a space is added to every capitalised letter)
   var indexOffset = 0
   for (index, character) in string.characters.enumerated(){
       let stringCharacter = String(character)

       //Evaluates to true if the character is a capital letter
       if stringCharacter.lowercased() != stringCharacter{
           guard index != 0 else { continue } //"ILoveSwift" should not turn into " I Love Swift"
           let stringIndex = string.index(string.startIndex, offsetBy: index + indexOffset)
           let endStringIndex = string.index(string.startIndex, offsetBy: index + 1 + indexOffset)
           let range = stringIndex..<endStringIndex
           indexOffset += 1
           string.replaceSubrange(range, with: " \(stringCharacter)")
       }
   }
   return string
}

You call the function like so:

var string = "iLoveSwiftCode"
addSpaces(to: string)
//Result: string = "i Love Swift Code"

Alternatively, if you prefer extensions:

extension String{
    mutating func seperatedWithSpaces(){
        //indexOffset is needed because each time replaceSubrange is called, the resulting count is incremented by one (owing to the fact that a space is added to every capitalised letter)
        var indexOffset = 0
        for (index, character) in characters.enumerated(){
            let stringCharacter = String(character)

            if stringCharacter.lowercased() != stringCharacter{
                guard index != 0 else { continue } //"ILoveSwift" should not turn into " I Love Swift"
                let stringIndex = self.index(self.startIndex, offsetBy: index + indexOffset)
                let endStringIndex = self.index(self.startIndex, offsetBy: index + 1 + indexOffset)
                let range = stringIndex..<endStringIndex
                indexOffset += 1
                self.replaceSubrange(range, with: " \(stringCharacter)")
            }
        }
    }
}

Call the method from a string:

var string = "iLoveSwiftCode"
string.seperatedWithSpaces()
//Result: string = "i Love Swift Code"
Mayur
  • 33
  • 5
-1

You could try making a new string that is a lowercase copy of the original string. Then compare the two strings and insert spaces wherever the characters are different.

Use the NSString method to turn to lowercase.

- (NSString *)lowercaseString