35

I've already found how to capitalize all words of the sentence, but not the first word only.

NSString *txt =@"hi my friends!"
[txt capitalizedString];

I don't want to change to lower case and capitalize the first char. I'd like to capitalize the first word only without change the others.

Johan Kool
  • 15,637
  • 8
  • 64
  • 81
wal
  • 2,044
  • 3
  • 18
  • 22

9 Answers9

119

Here is another go at it:

NSString *txt = @"hi my friends!";
txt = [txt stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:[[txt substringToIndex:1] uppercaseString]];

For Swift language:

txt.replaceRange(txt.startIndex...txt.startIndex, with: String(txt[txt.startIndex]).capitalizedString)
mixel
  • 25,177
  • 13
  • 126
  • 165
Johan Kool
  • 15,637
  • 8
  • 64
  • 81
  • 9
    Just make sure that the string is always at least 1 character long, or that you are ready to handle the exception thrown when that's not the case. – Johan Kool Mar 15 '10 at 18:28
  • Add a if([txt length] > 1) check before you replace the string with the capitalized string. – looneydoodle May 11 '12 at 18:18
  • 8
    @looneydoodle You mean `if ([txt length] > 0)`. It works fine if the length is 1. – Johan Kool May 12 '12 at 02:19
  • Suggested edit: Add semi-colon as last character of first line. Would avert Xcode error on copy paste. – John Erck Oct 23 '13 at 18:30
  • 1
    This answer is wrong. See my answer for the correct approach. – Ken Thomases Jul 12 '14 at 10:41
  • 1
    See my [other answer](http://stackoverflow.com/a/30187158/60488) for a Swift solution to the problem of capitalizing the first character in a string. Ken Thomases has a point that you need to take localisation in account, and I recommend his answer is that is what you need, but in many cases this approach is sufficient. – Johan Kool May 12 '15 at 09:51
16

The accepted answer is wrong. First, it is not correct to treat the units of NSString as "characters" in the sense that a user expects. There are surrogate pairs. There are combining sequences. Splitting those will produce incorrect results. Second, it is not necessarily the case that uppercasing the first character produces the same result as capitalizing a word containing that character. Languages can be context-sensitive.

The correct way to do this is to get the frameworks to identify words (and possibly sentences) in the locale-appropriate manner. And also to capitalize in the locale-appropriate manner.

[aMutableString enumerateSubstringsInRange:NSMakeRange(0, [aMutableString length])
                                   options:NSStringEnumerationByWords | NSStringEnumerationLocalized
                                usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
    [aMutableString replaceCharactersInRange:substringRange
                                  withString:[substring capitalizedStringWithLocale:[NSLocale currentLocale]]];
    *stop = YES;
}];

It's possible that the first word of a string is not the same as the first word of the first sentence of a string. To identify the first (or each) sentence of the string and then capitalize the first word of that (or those), then surround the above in an outer invocation of -enumerateSubstringsInRange:options:usingBlock: using NSStringEnumerationBySentences | NSStringEnumerationLocalized. In the inner invocation, pass the substringRange provided by the outer invocation as the range argument.

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • For those who'd like to capitalize the first letter of every word, just change the last line to read `*stop = enclosingRange.location + enclosingRange.length == self.length;` – Authman Apatira Mar 21 '15 at 13:15
  • 2
    If you want to capitalize every word, then there's no need to enumerate by words at all. That's the default behavior of `-capitalizedStringWithLocale:`. Even if you did want to enumerate every word, the way to do that is to simply never set `*stop`. The enumeration will naturally end at the end of the string. (In general in Cocoa's enumeration-with-block methods, you should never set `*stop` unless you're setting it to true. That's mostly important in concurrent enumerations, but it's in the design contract for all cases.) – Ken Thomases Mar 21 '15 at 13:30
  • For languages where every noun is capitalized, like German, this will not work. It will only capitalize the first word in the sentence instead of every noun. – keithbhunter Nov 23 '16 at 16:08
  • 1
    @keithbhunter, OK, but that's not what the question was about. And I'd be surprised if there were **any** API which did the right thing for the situation you posit. – Ken Thomases Nov 24 '16 at 05:25
10

Use

- (NSArray *)componentsSeparatedByCharactersInSet:(NSCharacterSet *)separator

and capitalize the first object in the array and then use

- (NSString *)componentsJoinedByString:(NSString *)separator

to join them back

Tuomas Pelkonen
  • 7,783
  • 2
  • 31
  • 32
  • 1
    I am pretty alien to Objective-c, but you have to do so much to just capitalize the word?? – Teja Kantamneni Mar 12 '10 at 19:51
  • 2
    Yes, to capitalize the first word only. Unfortunately, Objective-C is not like Python where every operation can be done on one line. – Tuomas Pelkonen Mar 12 '10 at 19:59
  • @Teja: NSString does not natively support doing something to just one "word" in a string, and it's quite a verbose language anyway, so yes. – Chuck Mar 12 '10 at 20:03
4
pString = [pString
           stringByReplacingCharactersInRange:NSMakeRange(0,1)
           withString:[[pString substringToIndex:1] capitalizedString]];
Chris Nolet
  • 8,714
  • 7
  • 67
  • 92
Ela
  • 59
  • 2
1

you can user with regular expression i have done it's works for me simple you can paste below code +(NSString*)CaptializeFirstCharacterOfSentence:(NSString*)sentence{

NSMutableString *firstCharacter = [sentence mutableCopy];
NSString *pattern = @"(^|\\.|\\?|\\!)\\s*(\\p{Letter})";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:NULL];
[regex enumerateMatchesInString:sentence options:0 range:NSMakeRange(0, [sentence length]) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
    //NSLog(@"%@", result);
    NSRange r = [result rangeAtIndex:2];
    [firstCharacter replaceCharactersInRange:r withString:[[sentence substringWithRange:r] uppercaseString]];
}];
NSLog(@"%@", firstCharacter);
return firstCharacter;

} //Call this method NsString *resultSentence = [UserClass CaptializeFirstCharacterOfSentence:yourTexthere];

Ashok Kumar
  • 225
  • 4
  • 9
1

An alternative solution in Swift:

var str = "hello"

if count(str) > 0 {
    str.splice(String(str.removeAtIndex(str.startIndex)).uppercaseString, atIndex: str.startIndex)
}
Johan Kool
  • 15,637
  • 8
  • 64
  • 81
0

For the sake of having options, I'd suggest:

NSString *myString = [NSString stringWithFormat:@"this is a string..."];

char *tmpStr = calloc([myString length] + 1,sizeof(char));

[myString getCString:tmpStr maxLength:[myString length] + 1 encoding:NSUTF8StringEncoding];

int sIndex = 0;

/* skip non-alpha characters at beginning of string */
while (!isalpha(tmpStr[sIndex])) {
    sIndex++;
}

toupper(tmpStr[sIndex]);

myString = [NSString stringWithCString:tmpStr encoding:NSUTF8StringEncoding];

I'm at work and don't have my Mac to test this on, but if I remember correctly, you couldn't use [myString cStringUsingEncoding:NSUTF8StringEncoding] because it returns a const char *.

alesplin
  • 1,332
  • 14
  • 23
0

In swift you can do it as followed by using this extension:

extension String {
    func ucfirst() -> String {
        return (self as NSString).stringByReplacingCharactersInRange(NSMakeRange(0, 1), withString: (self as NSString).substringToIndex(1).uppercaseString)
    }    
}

calling your string like this:

var ucfirstString:String = "test".ucfirst()
Antoine
  • 23,526
  • 11
  • 88
  • 94
0

I know the question asks specifically for an Objective C answer, however here is a solution for Swift 2.0:

let txt = "hi my friends!"
var sentencecaseString = ""

for (index, character) in txt.characters.enumerate() {
    if 0 == index {
        sentencecaseString += String(character).uppercaseString
    } else {
        sentencecaseString.append(character)
    }
}

Or as an extension:

func sentencecaseString() -> String {
    var sentencecaseString = ""
    for (index, character) in self.characters.enumerate() {
        if 0 == index {
            sentencecaseString += String(character).uppercaseString
        } else {
            sentencecaseString.append(character)
        }
    }
    return sentencecaseString
}
pheedsta
  • 2,058
  • 2
  • 16
  • 11