How can I capitalize the first letter of each sentence in an NSString?
For example, the string: @"this is sentence 1. this is sentence 2! is this sentence 3? last sentence here."
should become: @"This is sentence 1. This is sentence 2! Is this sentence 3? Last sentence here."

- 1,634
- 2
- 18
- 34
-
u can refer http://stackoverflow.com/questions/2432452/how-to-capitalize-the-first-word-of-the-sentence-in-objective-c – Sugan S Mar 20 '13 at 04:39
-
also this http://ioshouse.com/2012/10/how-to-capitalize-first-letter-of-a-word-or-a-sentence-in-objective-c/ – Sugan S Mar 20 '13 at 04:42
-
1These links address only the question of capitalizing the first letter of a single string, but not the combination of multiple strings with different separators (., !, ?). – Martin R Mar 20 '13 at 07:22
6 Answers
static NSString *CapitalizeSentences(NSString *stringToProcess) {
NSMutableString *processedString = [stringToProcess mutableCopy];
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en"];
// Ironically, the tokenizer will only tokenize sentences if the first letter
// of the sentence is capitalized...
stringToProcess = [stringToProcess uppercaseStringWithLocale:locale];
CFStringTokenizerRef stringTokenizer = CFStringTokenizerCreate(kCFAllocatorDefault, (__bridge CFStringRef)(stringToProcess), CFRangeMake(0, [stringToProcess length]), kCFStringTokenizerUnitSentence, (__bridge CFLocaleRef)(locale));
while (CFStringTokenizerAdvanceToNextToken(stringTokenizer) != kCFStringTokenizerTokenNone) {
CFRange sentenceRange = CFStringTokenizerGetCurrentTokenRange(stringTokenizer);
if (sentenceRange.location != kCFNotFound && sentenceRange.length > 0) {
NSRange firstLetterRange = NSMakeRange(sentenceRange.location, 1);
NSString *uppercaseFirstLetter = [[processedString substringWithRange:firstLetterRange] uppercaseStringWithLocale:locale];
[processedString replaceCharactersInRange:firstLetterRange withString:uppercaseFirstLetter];
}
}
CFRelease(stringTokenizer);
return processedString;
}

- 5,345
- 4
- 32
- 49
-
-
3You can also use `-[NSString enumerateSubstringsInRange:options:usingBlock:]` with `NSStringEnumerationBySentences` rather than `CFStringTokenizer` if you prefer Cocoa API. – Ken Thomases Mar 20 '13 at 07:33
-
I knew there was a way to do it without Core Foundation! But I was too tired/lazy to find it. =P – fumoboy007 Mar 20 '13 at 22:25
Use
-(NSArray *)componentsSeparatedByCharactersInSet:(NSCharacterSet *)separator
put all the separator(? ,. ,! ) from which you expect beginning of new sentence, make sure to put back the actual separator and capitalize the first object in the array and then use
-(NSString *)componentsJoinedByString:(NSString *)separator
to join them back with space separator
for capitalizing the first letter of each sentence run for loop for all elements of the array.
NSString *txt = @"hello!" txt = [txt stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:[[txt substringToIndex:1] uppercaseString]];

- 10,958
- 6
- 44
- 76
-
Sorry, but still that is confusing. How OP would be knowing which character was there when string was separated !!! ? – Janak Nirmal Mar 20 '13 at 05:09
-
yes, you are again right , I am able to think any solution for it. you have any ideas? – Manish Agrawal Mar 20 '13 at 05:18
-
Not yet, hats a really good question actually but without any efforts I guess ! – Janak Nirmal Mar 20 '13 at 06:53
This seems to work:
NSString *s1 = @"this is sentence 1. this is sentence 2! is this sentence 3? last sentence here.";
NSMutableString *s2 = [s1 mutableCopy];
NSString *pattern = @"(^|\\.|\\?|\\!)\\s*(\\p{Letter})";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:NULL];
[regex enumerateMatchesInString:s1 options:0 range:NSMakeRange(0, [s1 length]) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
//NSLog(@"%@", result);
NSRange r = [result rangeAtIndex:2];
[s2 replaceCharactersInRange:r withString:[[s1 substringWithRange:r] uppercaseString]];
}];
NSLog(@"%@", s2);
// This is sentence 1. This is sentence 2! Is this sentence 3? Last sentence here.
"(^|\\.|\\?|\\!)"
matches the start of the string or ".", "?", or "!","\\s*"
matches optional white space,"(\\p{Letter})"
matches a letter character.
So this pattern finds the first letter of each sentence. enumerateMatchesInString
enumerates all the matches and replaces the occurrence of the letter by the upper case letter.

- 529,903
- 94
- 1,240
- 1,382
This is the solution I finally came up with. I created a category to extend NSString with the following methods:
-(NSString *)capitalizeFirstLetter
{
//capitalizes first letter of a NSString
//find position of first alphanumeric charecter (compensates for if the string starts with space or other special character)
if (self.length<1) {
return @"";
}
NSRange firstLetterRange = [self rangeOfCharacterFromSet:[NSCharacterSet alphanumericCharacterSet]];
if (firstLetterRange.location > self.length)
return self;
return [self stringByReplacingCharactersInRange:NSMakeRange(firstLetterRange.location,1) withString:[[self substringWithRange:NSMakeRange(firstLetterRange.location, 1)] capitalizedString]];
}
-(NSString *)capitalizeSentences
{
NSString *inputString = [self copy];
//capitalize the first letter of the string
NSString *outputStr = [inputString capitalizeFirstLetter];
//capitalize every first letter after "."
NSArray *sentences = [outputStr componentsSeparatedByString:@"."];
outputStr = @"";
for (NSString *sentence in sentences){
static int i = 0;
if (i<sentences.count-1)
outputStr = [outputStr stringByAppendingString:[NSString stringWithFormat:@"%@.",[sentence capitalizeFirstLetter]]];
else
outputStr = [outputStr stringByAppendingString:[sentence capitalizeFirstLetter]];
i++;
}
//capitalize every first letter after "?"
sentences = [outputStr componentsSeparatedByString:@"?"];
outputStr = @"";
for (NSString *sentence in sentences){
static int i = 0;
if (i<sentences.count-1)
outputStr = [outputStr stringByAppendingString:[NSString stringWithFormat:@"%@?",[sentence capitalizeFirstLetter]]];
else
outputStr = [outputStr stringByAppendingString:[sentence capitalizeFirstLetter]];
i++;
}
//capitalize every first letter after "!"
sentences = [outputStr componentsSeparatedByString:@"!"];
outputStr = @"";
for (NSString *sentence in sentences){
static int i = 0;
if (i<sentences.count-1)
outputStr = [outputStr stringByAppendingString:[NSString stringWithFormat:@"%@!",[sentence capitalizeFirstLetter]]];
else
outputStr = [outputStr stringByAppendingString:[sentence capitalizeFirstLetter]];
i++;
}
return outputStr;
}
@end

- 1,634
- 2
- 18
- 34
This solution works for me:
NSMutableString *processedString = [NSMutableString stringWithString:[stringToProcess uppercaseString]];
NSRange range = {0, [processedString length]};
[processedString enumerateSubstringsInRange:range options:NSStringEnumerationBySentences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
substringRange.location++;
substringRange.length--;
NSString *replacementString = [[processedString substringWithRange:substringRange] lowercaseString];
[processedString replaceCharactersInRange:substringRange withString:replacementString];
}];
NB: As mentioned by fumoboy007 the string needs to be converted to upper case at the beginning otherwise enumeration does not work properly.

- 2,058
- 2
- 16
- 11
-
This is beautiful. However this first-uppercase-everything then -lowercase character-by-character is not too "optimal". Also, is this enumeration adhering to the current locale of the OS? I see the CFString based variation below, which is much longer and cumbersome - but is it more efficient than this one? – Motti Shneor Jun 25 '23 at 18:27
I wanted to do this today and came up with this for a mutable string "str" that can contain many sentences:
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(^|\\.|\\!|\\?)\\s*[a-z]" options:0 error:NULL];
for (NSTextCheckingResult* result in [regex matchesInString:str options:0 range:NSMakeRange(0, str.length)]) {
NSRange rng = NSMakeRange(result.range.length+result.range.location-1, 1);
[str replaceCharactersInRange:rng withString:[[str substringWithRange:rng] uppercaseString]];
}
My solution required that I only try to capitalize non-accented latin letters, hence the [a-z].
Used to perl I thought this was a bit long, so I checked stack overflow. Apart from one answer that is similar, I guess we can't go simpler than this...

- 1,014
- 9
- 26