CamelCase to underscores and back in Objective-C - objective-c

I'm looking for a simple, efficient way to convert strings in CamelCase to underscore notation (i.e., MyClassName -> my_class_name) and back again in Objective C.
My current solution involves lots of rangeOfString, characterAtIndex, and replaceCharactersInRange operations on NSMutableStrings, and is just plain ugly as hell :) It seems that there must be a better solution, but I'm not sure what it is.
I'd rather not import a regex library just for this one use case, though that is an option if all else fails.

Chris's suggestion of RegexKitLite is good. It's an excellent toolkit, but this could be done pretty easily with NSScanner. Use -scanCharactersFromSet:intoString: alternating between +uppercaseLetterCharacterSet and +lowercaseLetterCharacterSet. For going back, you'd use -scanUpToCharactersFromSet: instead, using a character set with just an underscore in it.

How about these:
NSString *MyCamelCaseToUnderscores(NSString *input) {
NSMutableString *output = [NSMutableString string];
NSCharacterSet *uppercase = [NSCharacterSet uppercaseLetterCharacterSet];
for (NSInteger idx = 0; idx < [input length]; idx += 1) {
unichar c = [input characterAtIndex:idx];
if ([uppercase characterIsMember:c]) {
[output appendFormat:#"_%#", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
} else {
[output appendFormat:#"%C", c];
}
}
return output;
}
NSString *MyUnderscoresToCamelCase(NSString *underscores) {
NSMutableString *output = [NSMutableString string];
BOOL makeNextCharacterUpperCase = NO;
for (NSInteger idx = 0; idx < [underscores length]; idx += 1) {
unichar c = [underscores characterAtIndex:idx];
if (c == '_') {
makeNextCharacterUpperCase = YES;
} else if (makeNextCharacterUpperCase) {
[output appendString:[[NSString stringWithCharacters:&c length:1] uppercaseString]];
makeNextCharacterUpperCase = NO;
} else {
[output appendFormat:#"%C", c];
}
}
return output;
}
Some drawbacks are that they do use temporary strings to convert between upper and lower case, and they don't have any logic for acronyms, so myURL will result in my_u_r_l.

Try this magic:
NSString* camelCaseString = #"myBundleVersion";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"(?<=[a-z])([A-Z])|([A-Z])(?=[a-z])" options:0 error:nil];
NSString *underscoreString = [[regex stringByReplacingMatchesInString:camelCaseString options:0 range:NSMakeRange(0, camelCaseString.length) withTemplate:#"_$1$2"] lowercaseString];
NSLog(#"%#", underscoreString);
Output: my_bundle_version

If your concern is just the visibility of your code, you could make a category for NSString using the methods you've designed already. That way, you only see the ugly mess once. ;)
For instance:
#interface NSString(Conversions) {
- (NSString *)asCamelCase;
- (NSString *)asUnderscored;
}
#implementation NSString(Conversions) {
- (NSString *)asCamelCase {
// whatever you came up with
}
- (NSString *)asUnderscored {
// whatever you came up with
}
}
EDIT: After a quick Google search, I couldn't find any way of doing this, even in plain C. However, I did find a framework that could be useful. It's called RegexKitLite. It uses the built-in ICU library, so it only adds about 20K to the final binary.

Here's my implementation of Rob's answer:
#implementation NSString (CamelCaseConversion)
// Convert a camel case string into a dased word sparated string.
// In case of scanning error, return nil.
// Camel case string must not start with a capital.
- (NSString *)fromCamelCaseToDashed {
NSScanner *scanner = [NSScanner scannerWithString:self];
scanner.caseSensitive = YES;
NSString *builder = [NSString string];
NSString *buffer = nil;
NSUInteger lastScanLocation = 0;
while ([scanner isAtEnd] == NO) {
if ([scanner scanCharactersFromSet:[NSCharacterSet lowercaseLetterCharacterSet] intoString:&buffer]) {
builder = [builder stringByAppendingString:buffer];
if ([scanner scanCharactersFromSet:[NSCharacterSet uppercaseLetterCharacterSet] intoString:&buffer]) {
builder = [builder stringByAppendingString:#"-"];
builder = [builder stringByAppendingString:[buffer lowercaseString]];
}
}
// If the scanner location has not moved, there's a problem somewhere.
if (lastScanLocation == scanner.scanLocation) return nil;
lastScanLocation = scanner.scanLocation;
}
return builder;
}
#end

Here's yet another version based on all the above. This version handles additional forms. In particular, tested with the following:
camelCase => camel_case
camelCaseWord => camel_case_word
camelURL => camel_url
camelURLCase => camel_url_case
CamelCase => camel_case
Here goes
- (NSString *)fromCamelCaseToDashed3 {
NSMutableString *output = [NSMutableString string];
NSCharacterSet *uppercase = [NSCharacterSet uppercaseLetterCharacterSet];
BOOL previousCharacterWasUppercase = FALSE;
BOOL currentCharacterIsUppercase = FALSE;
unichar currentChar = 0;
unichar previousChar = 0;
for (NSInteger idx = 0; idx < [self length]; idx += 1) {
previousChar = currentChar;
currentChar = [self characterAtIndex:idx];
previousCharacterWasUppercase = currentCharacterIsUppercase;
currentCharacterIsUppercase = [uppercase characterIsMember:currentChar];
if (!previousCharacterWasUppercase && currentCharacterIsUppercase && idx > 0) {
// insert an _ between the characters
[output appendString:#"_"];
} else if (previousCharacterWasUppercase && !currentCharacterIsUppercase) {
// insert an _ before the previous character
// insert an _ before the last character in the string
if ([output length] > 1) {
unichar charTwoBack = [output characterAtIndex:[output length]-2];
if (charTwoBack != '_') {
[output insertString:#"_" atIndex:[output length]-1];
}
}
}
// Append the current character lowercase
[output appendString:[[NSString stringWithCharacters:&currentChar length:1] lowercaseString]];
}
return output;
}

If you are concerned with the speed of your code you probably want to write a more performant version of the code:
- (nonnull NSString *)camelCaseToSnakeCaseString {
if ([self length] == 0) {
return #"";
}
NSMutableString *output = [NSMutableString string];
NSCharacterSet *digitSet = [NSCharacterSet decimalDigitCharacterSet];
NSCharacterSet *uppercaseSet = [NSCharacterSet uppercaseLetterCharacterSet];
NSCharacterSet *lowercaseSet = [NSCharacterSet lowercaseLetterCharacterSet];
for (NSInteger idx = 0; idx < [self length]; idx += 1) {
unichar c = [self characterAtIndex:idx];
// if it's the last one then just append lowercase of character
if (idx == [self length] - 1) {
if ([uppercaseSet characterIsMember:c]) {
[output appendFormat:#"%#", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
}
else {
[output appendFormat:#"%C", c];
}
continue;
}
unichar nextC = [self characterAtIndex:(idx+1)];
// this logic finds the boundaries between lowercase/uppercase/digits and lets the string be split accordingly.
if ([lowercaseSet characterIsMember:c] && [uppercaseSet characterIsMember:nextC]) {
[output appendFormat:#"%#_", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
}
else if ([lowercaseSet characterIsMember:c] && [digitSet characterIsMember:nextC]) {
[output appendFormat:#"%#_", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
}
else if ([digitSet characterIsMember:c] && [uppercaseSet characterIsMember:nextC]) {
[output appendFormat:#"%#_", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
}
else {
// Append lowercase of character
if ([uppercaseSet characterIsMember:c]) {
[output appendFormat:#"%#", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
}
else {
[output appendFormat:#"%C", c];
}
}
}
return output;
}

I have combined the answers found here into my refactoring library, es_ios_utils. See NSCategories.h:
#property(nonatomic, readonly) NSString *asCamelCaseFromUnderscores;
#property(nonatomic, readonly) NSString *asUnderscoresFromCamelCase;
Usage:
#"my_string".asCamelCaseFromUnderscores
yields #"myString"
Please push improvements!

I happened upon this question looking for a way to convert Camel Case to a spaced, user displayable string. Here is my solution which worked better than replacing #"_" with #" "
- (NSString *)fromCamelCaseToSpaced:(NSString*)input {
NSCharacterSet* lower = [NSCharacterSet lowercaseLetterCharacterSet];
NSCharacterSet* upper = [NSCharacterSet uppercaseLetterCharacterSet];
for (int i = 1; i < input.length; i++) {
if ([upper characterIsMember:[input characterAtIndex:i]] &&
[lower characterIsMember:[input characterAtIndex:i-1]])
{
NSString* soFar = [input substringToIndex:i];
NSString* left = [input substringFromIndex:i];
return [NSString stringWithFormat:#"%# %#", soFar, [self fromCamelCaseToSpaced:left]];
}
}
return input;
}

OK guys. Here is an all regex answer, which I consider the only true way:
Given:
NSString *MYSTRING = "foo_bar";
NSRegularExpression *_toCamelCase = [NSRegularExpression
regularExpressionWithPattern:#"(_)([a-z])"
options:NSRegularExpressionCaseInsensitive error:&error];
NSString *camelCaseAttribute = [_toCamelCase
stringByReplacingMatchesInString:MYSTRING options:0
range:NSMakeRange(0, attribute.length)
withTemplate:#"\\U$2"];
Yields fooBar.
Conversely:
NSString *MYSTRING = "fooBar";
NSRegularExpression *camelCaseTo_ = [NSRegularExpression
regularExpressionWithPattern:#"([A-Z])"
options:0 error:&error];
NSString *underscoreParsedAttribute = [camelCaseTo_
stringByReplacingMatchesInString:MYSTRING
options:0 range:NSMakeRange(0, attribute.length)
withTemplate:#"_$1"];
underscoreParsedAttribute = [underscoreParsedAttribute lowercaseString];
Yields: foo_bar.
\U$2 replaces second capture group with upper-case version of itself :D
\L$1 however, oddly, does not replace the first capture group with a lower-case version of itself :( Not sure why, it should work. :/

Related

Reverse NSString text

I have been googling so much on how to do this, but how would I reverse a NSString? Ex:hi would become: ih
I am looking for the easiest way to do this.
Thanks!
#Vince I made this method:
- (IBAction)doneKeyboard {
// first retrieve the text of textField1
NSString *myString = field1.text;
NSMutableString *reversedString = [NSMutableString string];
NSUInteger charIndex = 0;
while(myString && charIndex < [myString length]) {
NSRange subStrRange = NSMakeRange(charIndex, 1);
[reversedString appendString:[myString substringWithRange:subStrRange]];
charIndex++;
}
// reversedString is reversed, or empty if myString was nil
field2.text = reversedString;
}
I hooked up that method to textfield1's didendonexit. When I click the done button, it doesn't reverse the text, the UILabel just shows the UITextField's text that I entered. What is wrong?
Block version.
NSString *myString = #"abcdefghijklmnopqrstuvwxyz";
NSMutableString *reversedString = [NSMutableString stringWithCapacity:[myString length]];
[myString enumerateSubstringsInRange:NSMakeRange(0,[myString length])
options:(NSStringEnumerationReverse | NSStringEnumerationByComposedCharacterSequences)
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
[reversedString appendString:substring];
}];
// reversedString is now zyxwvutsrqponmlkjihgfedcba
Write a simple loop to do that:
// myString is "hi"
NSMutableString *reversedString = [NSMutableString string];
NSInteger charIndex = [myString length];
while (charIndex > 0) {
charIndex--;
NSRange subStrRange = NSMakeRange(charIndex, 1);
[reversedString appendString:[myString substringWithRange:subStrRange]];
}
NSLog(#"%#", reversedString); // outputs "ih"
In your case:
// first retrieve the text of textField1
NSString *myString = textField1.text;
NSMutableString *reversedString = [NSMutableString string];
NSInteger charIndex = [myString length];
while (myString && charIndex > 0) {
charIndex--;
NSRange subStrRange = NSMakeRange(charIndex, 1);
[reversedString appendString:[myString substringWithRange:subStrRange]];
}
// reversedString is reversed, or empty if myString was nil
textField2.text = reversedString;
jano’s answer is correct. Unfortunately, it creates a lot of unnecessary temporary objects. Here is a much faster (more complicated) implementation that basically does the same thing, but uses memcpy and unichar buffers to keep memory allocations to a minimum.
- (NSString *)reversedString
{
NSUInteger length = [self length];
if (length < 2) {
return self;
}
unichar *characters = calloc(length, sizeof(unichar));
unichar *reversedCharacters = calloc(length, sizeof(unichar));
if (!characters || !reversedCharacters) {
free(characters);
free(reversedCharacters);
return nil;
}
[self getCharacters:characters range:NSMakeRange(0, length)];
NSUInteger i = length - 1;
NSUInteger copiedCharacterCount = 0;
// Starting from the end of self, copy each composed character sequence into reversedCharacters
while (copiedCharacterCount < length) {
NSRange characterRange = [self rangeOfComposedCharacterSequenceAtIndex:i];
memcpy(reversedCharacters + copiedCharacterCount, characters + characterRange.location, characterRange.length * sizeof(unichar));
i = characterRange.location - 1;
copiedCharacterCount += characterRange.length;
}
free(characters);
NSString *reversedString = [[NSString alloc] initWithCharactersNoCopy:reversedCharacters length:length freeWhenDone:YES];
if (!reversedString) {
free(reversedCharacters);
}
return reversedString;
}
I tested this on 100,000 random multi-byte Unicode strings with lengths between 1 and 128. This version is about 4–5x faster than jano’s.
Enumerate substrings: 2.890528
MemCopy: 0.671090
Enumerate substrings: 2.840411
MemCopy: 0.662882
Test code is at https://gist.github.com/prachigauriar/9739805.
Update: I tried this again by simply converting to a UTF-32 buffer and reversing that.
- (NSString *)qlc_reversedStringWithUTF32Buffer
{
NSUInteger length = [self length];
if (length < 2) {
return self;
}
NSStringEncoding encoding = NSHostByteOrder() == NS_BigEndian ? NSUTF32BigEndianStringEncoding : NSUTF32LittleEndianStringEncoding;
NSUInteger utf32ByteCount = [self lengthOfBytesUsingEncoding:encoding];
uint32_t *characters = malloc(utf32ByteCount);
if (!characters) {
return nil;
}
[self getBytes:characters maxLength:utf32ByteCount usedLength:NULL encoding:encoding options:0 range:NSMakeRange(0, length) remainingRange:NULL];
NSUInteger utf32Length = utf32ByteCount / sizeof(uint32_t);
NSUInteger halfwayPoint = utf32Length / 2;
for (NSUInteger i = 0; i < halfwayPoint; ++i) {
uint32_t character = characters[utf32Length - i - 1];
characters[utf32Length - i - 1] = characters[i];
characters[i] = character;
}
return [[NSString alloc] initWithBytesNoCopy:characters length:utf32ByteCount encoding:encoding freeWhenDone:YES];
}
This is about 3–4x times faster than the memcpy version. The aforementioned gist has been updated with the latest version of the code.
Enumerate substrings: 2.168705
MemCopy: 0.488320
UTF-32: 0.150822
Enumerate substrings: 2.169655
MemCopy: 0.481786
UTF-32: 0.147534
Enumerate substrings: 2.248812
MemCopy: 0.505995
UTF-32: 0.154531
I thought I'd throw another version out there in case anyone's interested.. personally, I like the cleaner approach using NSMutableString but if performance is the highest priority this one is faster:
- (NSString *)reverseString:(NSString *)input {
NSUInteger len = [input length];
unichar *buffer = malloc(len * sizeof(unichar));
if (buffer == nil) return nil; // error!
[input getCharacters:buffer];
// reverse string; only need to loop through first half
for (NSUInteger stPos=0, endPos=len-1; stPos < len/2; stPos++, endPos--) {
unichar temp = buffer[stPos];
buffer[stPos] = buffer[endPos];
buffer[endPos] = temp;
}
return [[NSString alloc] initWithCharactersNoCopy:buffer length:len freeWhenDone:YES];
}
I also wrote a quick test as well to compare this with the more traditional NSMutableString method (which I also included below):
// test reversing a really large string
NSMutableString *string = [NSMutableString new];
for (int i = 0; i < 10000000; i++) {
int digit = i % 10;
[string appendFormat:#"%d", digit];
}
NSTimeInterval startTime = [[NSDate date] timeIntervalSince1970];
NSString *reverse = [self reverseString:string];
NSTimeInterval elapsedTime = [[NSDate date] timeIntervalSince1970] - startTime;
NSLog(#"reversed in %f secs", elapsedTime);
Results were:
using NSMutableString method (below) - "reversed in 3.720631 secs"
using unichar *buffer method (above) - "reversed in 0.032604 secs"
Just for reference, here's the NSMutableString method used for this comparison:
- (NSString *)reverseString:(NSString *)input {
NSUInteger len = [input length];
NSMutableString *result = [[NSMutableString alloc] initWithCapacity:len];
for (int i = len - 1; i >= 0; i--) {
[result appendFormat:#"%c", [input characterAtIndex:i]];
}
return result;
}
Use method with any objects: NSString,NSNumber,etc..:
NSLog(#"%#",[self reverseObject:#12345]);
NSLog(#"%#",[self reverseObject:#"Hello World"]);
Method:
-(NSString*)reverseObject:(id)string{
string = [NSString stringWithFormat:#"%#",string];
NSMutableString *endString = [NSMutableString new];
while ([string length]!=[endString length]) {
NSRange range = NSMakeRange([string length]-[endString length]-1, 1);
[endString appendString: [string substringWithRange:range]];
}
return endString;}
Log:
2014-04-16 11:20:25.312 TEST[23733:60b] 54321
2014-04-16 11:20:25.313 TEST[23733:60b] dlroW olleH
Swift 2.0:
1) let str = "Hello, world!"
let reversed = String(str.characters.reverse())
print(reversed)
In Short:
String("This is a test string.".characters.reverse())
2)
let string = "This is a test string."
let characters = string.characters
let reversedCharacters = characters.reverse()
let reversedString = String(reversedCharacters)
The short way :
String("This is a test string.".characters.reverse())
OR
let string = "This is a test string."
let array = Array(string)
let reversedArray = array.reverse()
let reversedString = String(reversedArray)
The short way :
String(Array("This is a test string.").reverse())
Tested on Play Ground:
import Cocoa
//Assigning a value to a String variable
var str = "Hello, playground"
//Create empty character Array.
var strArray:Character[] = Character[]()
//Loop through each character in the String
for character in str {
//Insert the character in the Array variable.
strArray.append(character)
}
//Create a empty string
var reversedStr:String = ""
//Read the array from backwards to get the characters
for var index = strArray.count - 1; index >= 0;--index {
//Concatenate character to String.
reversedStr += strArray[index]
}
The shorter version:
var str = “Hello, playground”
var reverseStr = “”
for character in str {
reverseStr = character + reverseStr
}
Would it be faster if you only iterated over half the string swapping the characters at each end? So for a 5 character string, you swap characters 1 + 5, then 2 + 4 and 3 doesn't need swapped with anything.
NSMutableString *reversed = [original mutableCopyWithZone:NULL];
NSUInteger i, length;
length = [reversed length];
for (i = 0; i < length / 2; i++) {
// Store the first character as we're going to replace with the character at the end
// in the example, it would store 'h'
unichar startChar = [reversed characterAtIndex:i];
// Only make the end range once
NSRange endRange = NSMakeRange(length - i, 1);
// Replace the first character ('h') with the last character ('i')
// so reversed now contains "ii"
[reversed replaceCharactersInRange:NSMakeRange(i, 1)
withString:[reversed subStringWithRange:endRange];
// Replace the last character ('i') with the stored first character ('h)
// so reversed now contains "ih"
[reversed replaceCharactersInRange:endRange
withString:[NSString stringWithFormat:#"%c", startChar]];
}
edit ----
Having done some tests, the answer is No, its about 6 times slower than the version that loops over everything. The thing that slows us down is creating the temporary NSStrings for the replaceCharactersInRange:withString method. Here is a method that creates only one NSString by manipulating the character data directly and seems a lot faster in simple tests.
NSUInteger length = [string length];
unichar *data = malloc(sizeof (unichar) * length);
int i;
for (i = 0; i < length / 2; i++) {
unichar startChar = [string characterAtIndex:i];
unichar endChar = [string characterAtIndex:(length - 1) - i];
data[i] = endChar;
data[(length - 1) - i] = startChar;
}
NSString *reversed = [NSString stringWithCharacters:data length:length];
free(data);
Reverse the string using recursion:
#implementation NSString (Reversed)
+ (NSString *)reversedStringFromString:(NSString *)string
{
NSUInteger count = [string length];
if (count <= 1) { // Base Case
return string;
} else {
NSString *lastLetter = [string substringWithRange:NSMakeRange(count - 1, 1)];
NSString *butLastLetter = [string substringToIndex:count - 1];
return [lastLetter stringByAppendingString:[self reversedStringFromString:butLastLetter]];
}
}
#end
Google is your friend:
-(NSString *) reverseString
{
NSMutableString *reversedStr;
int len = [self length];
// Auto released string
reversedStr = [NSMutableString stringWithCapacity:len];
// Probably woefully inefficient...
while (len > 0)
[reversedStr appendString:
[NSString stringWithFormat:#"%C", [self characterAtIndex:--len]]];
return reversedStr;
}
None of the answers seem to consider multibyte characters so here is my sample code. It assumes you only ever pass in a string longer than one character.
- (void)testReverseString:(NSString *)string
{
NSMutableString *rString = [NSMutableString new];
NSInteger extractChar = [string length] - 1;
while (extractChar >= 0)
{
NSRange oneCharPos = [string rangeOfComposedCharacterSequenceAtIndex:extractChar];
for (NSUInteger add = 0; add < oneCharPos.length; ++ add)
{
unichar oneChar = [string characterAtIndex:oneCharPos.location + add];
[rString appendFormat:#"%C", oneChar];
}
extractChar -= oneCharPos.length;
}
NSLog(#"%# becomes %#", string, encryptedString );
}
NSString into char utf32 (always 32 bits (unsigned int))
Reverse
char utf32 into NSString
+ (NSString *)reverseString3:(NSString *)str {
unsigned int *cstr, buf, len = [str length], i;
cstr = (unsigned int *)[str cStringUsingEncoding:NSUTF32LittleEndianStringEncoding];
for (i=0;i < len/2;i++) buf = cstr[i], cstr[i] = cstr[len -i-1], cstr[len-i-1] = buf;
return [[NSString alloc] initWithBytesNoCopy:cstr length:len*4 encoding:NSUTF32LittleEndianStringEncoding freeWhenDone:NO];
}
Example : Apple_is  --->  si_elppA
NSMutableString *result = [NSMutableString stringWithString:#""];
for (long i = self.length - 1; i >= 0; i--) {
[result appendFormat:#"%c", [self characterAtIndex:i]];
}
return (NSString *)result;
Here is a collection of categories in Objective-C that will reverse both NSStrings and NSAttributedStrings (while preserving character attributes): TextFlipKit
For example:
NSString *example = #"Example Text";
NSString *reversed = example.tfk_reversed;
NSLog(#"Reversed: %#", reversed);
//prints 'Reversed: txeT elpmaxE'
Swift:
let string = "reverse"
let reversedStringCollection = string.characters.reversed()
for character in reversedStringCollection {
reversedString.append(character)
print(reversedString)
}
We can also achieve the reverse string as follows.
NSString *originalString = #"Hello";
NSString *reverseString;
for (NSUInteger index = originalString.length; index > 0; index--) {
char character = [originalString characterAtIndex:index];
reverseString = [reverseString stringByAppendingString:[NSString stringWithFormat:#"%c", character]];
}
or
NSString *originalString = #"Hello";
NSString *reverseString;
for (NSUInteger index = originalString.length; index > 0; index--) {
char *character = [originalString characterAtIndex:index];
reverseString = [reverseString stringByAppendingString:[NSString stringWithFormat:#"%s", character]];
}
Add a category to NSString so you can call reverse on any NSString in the future like this:
#import "NSString+Reverse.h"
#implementation NSString (Reverse)
-(NSString*)reverse {
char* cstring = (char*)[self UTF8String];
int length = [self length]-1;
int i=0;
while (i<=length) {
unichar tmp = cstring[i];
cstring[i] = cstring[length];
cstring[length] = tmp;
i++;
length--;
}
return [NSString stringWithCString:cstring encoding:NSUTF8StringEncoding];
}
#end
str=#"india is my countery";
array1=[[NSMutableArray alloc] init];
for(int i =0 ;i<[str length]; i++) {
NSString *singleCharacter = [NSString stringWithFormat:#"%c", [str characterAtIndex:i]];
[array1 addObject:singleCharacter];
}
NSMutableString* theString = [NSMutableString string];
for (int i=[array1 count]-1; i>=0;i--){
[theString appendFormat:#"%#",[array1 objectAtIndex:i]];
}
I have written a category ove that one :D
//NSString+Reversed.h
#import
//
// NSString+Reversed.h
// HTMLPageFormatter
// Created by beit46 on 21.06.13.
//
#interface NSString (Reversed)
- (NSString *)reversedString;
#end
//NSString+Reversed.m
//
// NSString+Reversed.m
// HTMLPageFormatter
// Created by beit46 on 21.06.13.
#import "NSString+Reversed.h"
#implementation NSString (Reversed)
- (NSString *)reversedString {
NSMutableString *reversedString = [NSMutableString stringWithCapacity:[self length]];
[self enumerateSubstringsInRange:NSMakeRange(0,[self length])
options:(NSStringEnumerationReverse | NSStringEnumerationByComposedCharacterSequences)
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
[reversedString appendString:substring];
}];
return [reversedString copy];
}
#end
I have two simple solutions for that purpose:
+(NSString*)reverseString:(NSString *)str
{
NSMutableString* reversed = [NSMutableString stringWithCapacity:str.length];
for (int i = (int)str.length-1; i >= 0; i--){
[reversed appendFormat:#"%c", [str characterAtIndex:i]];
}
return reversed;
}
+(NSString*)reverseString2:(NSString *)str
{
char* cstr = (char*)[str UTF8String];
int len = (int)str.length;
for (int i = 0; i < len/2; i++) {
char buf = cstr[i];
cstr[i] = cstr[len-i-1];
cstr[len-i-1] = buf;
}
return [[NSString alloc] initWithBytes:cstr length:len encoding:NSUTF8StringEncoding];
}
Now, lets test it!
NSString* str = #"Objective-C is a general-purpose, object-oriented programming language that adds Smalltalk-style messaging to the C programming language";
NSLog(#"REV 1: %#", [Util reverseString:str]);
start = [NSDate date];
for (int i = 0 ; i < 1000; ++i)
[Util reverseString:str];
end = [NSDate date];
NSLog(#"Time per 1000 repeats: %f", [end timeIntervalSinceDate:start]);
NSLog(#"REV 2: %#", [Util reverseString2:str]);
start = [NSDate date];
for (int i = 0 ; i < 1000; ++i)
[Util reverseString2:str];
end = [NSDate date];
NSLog(#"Time per 1000 repeats: %f", [end timeIntervalSinceDate:start]);
Results:
ConsoleTestProject[68292:303] REV 1: egaugnal gnimmargorp C eht ot gnigassem elyts-klatllamS sdda taht egaugnal gnimmargorp detneiro-tcejbo ,esoprup-lareneg a si C-evitcejbO
ConsoleTestProject[68292:303] Time per 1000 repeats: 0.063880
ConsoleTestProject[68292:303] REV 2: egaugnal gnimmargorp C eht ot gnigassem elyts-klatllamS sdda taht egaugnal gnimmargorp detneiro-tcejbo ,esoprup-lareneg a si C-evitcejbO
ConsoleTestProject[68292:303] Time per 1000 repeats: 0.002038
And more chars result was:
ConsoleTestProject[68322:303] chars: 1982
ConsoleTestProject[68322:303] Time 1 per 1000 repeats: 1.014893
ConsoleTestProject[68322:303] Time 2 per 1000 repeats: 0.024928
The same text with above functions:
ConsoleTestProject[68366:303] Time 1 per 1000 repeats: 0.873574
ConsoleTestProject[68366:303] Time 2 per 1000 repeats: 0.019300
ConsoleTestProject[68366:303] Time 3 per 1000 repeats: 0.342735 <-Vladimir Gritsenko
ConsoleTestProject[68366:303] Time 4 per 1000 repeats: 0.584012 <- Jano
So, choose performance!

creating NSMutableString from two other NSMutableStrings

I have two strings:
#"--U" and #"-O-" and would like to create another NSMutableString that makes #"-OU" using the two givens. Does anyone know how I can do this?
Note, the following code assumes that s1 and s2 have the same length, otherwise it will throw an exception at some point, so do the checking :)
- (NSMutableString *)concatString:(NSString *)s1 withString:(NSString *)s2
{
NSMutableString *result = [NSMutableString stringWithCapacity:[s1 length]];
for (int i = 0; i < [s1 length]; i++) {
unichar c = [s1 characterAtIndex:i];
if ( c != '-' ) {
[result appendFormat:#"%c", c];
}
else {
[result appendFormat:#"%c", [s2 characterAtIndex:i]];
}
}
return result;
}
NSString *t1=#"-0-";
NSString *t2=#"--U";
NSString *temp1=[t1 substringWithRange:NSMakeRange(0, 2)];
NSString *temp2=[t2 substringFromIndex:2];
NSLog(#"%#",[NSString stringWithFormat:#"%#%#",temp1,temp2]);
This version is a bit more long-winded than Nick's, but breaks the thing down into C functions and tail recursion, so it may run faster. It also handles strings of different lengths, choosing to mirror the shorter string's length.
NOTE: I have not run this code yet, so it may be buggy or be missing something obvious.
void recursiveStringMerge(unichar* string1, unichar* string2, unichar* result) {
if (string1[0] == '\0' || string2[0] == '\0') {
result[0] = '\0'; //properly end the string
return; //no use in trying to add more to this string
}
else if (string1[0] != '-') {
result[0] = string1[0];
}
else {
result[0] = string2[0];
}
//move on to the next unichar in each array
recursiveStringMerge(string1+1, string2+1, result+1);
}
- (NSMutableString *)concatString:(NSString *)s1 withString:(NSString *)s2 {
NSUInteger resultLength;
NSUInteger s1Length = [s1 length]+1; //ensure space for NULL with the +1
NSUInteger s2Length = [s2 length]+1;
resultLength = (s1Length <= s2Length) ? s1Length : s2Length; //only need the shortest
unichar* result = malloc(resultLength*sizeof(unichar));
unichar *string1 = calloc(s1Length, sizeof(unichar));
[s1 getCharacters:buffer];
unichar *string2 = calloc(s2Length, sizeof(unichar));
[s2 getCharacters:buffer];
recursiveStringMerge(string1, string2, result);
return [NSString stringWithCharacters: result length: resultLength];
}

How do I convert an NSString from CamelCase to TitleCase, 'playerName' into 'Player Name'?

I'm looking for the easiest way to convert a string from camelback format to Title Case format.
How do I change 'playerName' into 'Player Name'?
NSString *str = #"playerName";
NSMutableString *str2 = [NSMutableString string];
for (NSInteger i=0; i<str.length; i++){
NSString *ch = [str substringWithRange:NSMakeRange(i, 1)];
if ([ch rangeOfCharacterFromSet:[NSCharacterSet uppercaseLetterCharacterSet]].location != NSNotFound) {
[str2 appendString:#" "];
}
[str2 appendString:ch];
}
NSLog(#"%#", str2.capitalizedString);
Here's a simpler Swift version. I've chucked it into an extension
extension String {
func stringFromCamelCase() -> String {
var string = self
string = string.stringByReplacingOccurrencesOfString("([a-z])([A-Z])", withString: "$1 $2", options: NSStringCompareOptions.RegularExpressionSearch, range: Range<String.Index>(start: string.startIndex, end: string.endIndex))
string.replaceRange(startIndex...startIndex, with: String(self[startIndex]).capitalizedString)
return string
}
}
Usage:
var str = "helloWorld"
str = str.stringFromCamelCase()
Try using a regex replace
NSString *modified = [input stringByReplacingOccurrencesOfString:#"([a-z])([A-Z])"
withString:#"$1 $2"
options:NSRegularExpressionSearch
range:NSMakeRange(0, input.length)];
A little shorter, using NSCharacterSet:
__block NSString *str = #"myVerySpecialPlayerName" ;
// split at uppercase letters
NSArray *splitString = [str componentsSeparatedByCharactersInSet:
[NSCharacterSet uppercaseLetterCharacterSet]] ;
// get the uppercase letters
NSArray *upperCaseLetters = [str componentsSeparatedByCharactersInSet:
[[NSCharacterSet uppercaseLetterCharacterSet] invertedSet]] ;
// join with two spaces
str = [splitString componentsJoinedByString:#" "] ;
__block NSInteger offset = 0 ;
// replace each second space with the missing uppercase letter
[upperCaseLetters enumerateObjectsUsingBlock:^(NSString *character, NSUInteger idx, BOOL *stop) {
if( [character length] > 0 ) {
str = [str stringByReplacingCharactersInRange:NSMakeRange(idx+offset+1, 1) withString:character] ;
offset += 2 ;
}
}] ;
// & capitalize the first one
str = [str capitalizedString] ;
NSLog(#"%#", str) ; // "My Very Special Player Name"
Trying to be more unicode compliant
extension String {
func camelCaseToTitleCase() -> String {
return unicodeScalars.map(replaceCaptialsWithSpacePlusCapital).joined().capitalized
}
private func replaceCaptialsWithSpacePlusCapital(unichar: UnicodeScalar) -> String {
if CharacterSet.uppercaseLetters.contains(unichar) {
return " \(unichar)"
}
return "\(unichar)"
}
}
I think you can tackle this problem with some Regular Expressions. Check out this similar question: iPhone dev: Replace uppercase characters in NSString with space and downcase
Although a little long, but this category for NSString should do the trick. It passed all my tests:
- (NSString *)splitOnCapital
{
// Make a index of uppercase characters
NSRange upcaseRange = NSMakeRange('A', 26);
NSIndexSet *upcaseSet = [NSIndexSet indexSetWithIndexesInRange:upcaseRange];
// Split our camecase word
NSMutableString *result = [NSMutableString string];
NSMutableString *oneWord = [NSMutableString string];
for (int i = 0; i < self.length; i++) {
char oneChar = [self characterAtIndex:i];
if ([upcaseSet containsIndex:oneChar]) {
// Found a uppercase char, now save previous word
if (result.length == 0) {
// First word, no space in beginning
[result appendFormat:#"%#", [oneWord capitalizedString]];
}else {
[result appendFormat:#" %#", oneWord];
}
// Clear previous word for new word
oneWord = [NSMutableString string];
}
[oneWord appendFormat:#"%c", oneChar];
}
// Add last word
if (oneWord.length > 0) {
[result appendFormat:#" %#", oneWord];
}
return result;
}
I had a similar issue, the answers here helped me create a solution. I had an array that had a list of labels I wanted to display within a UITableView, one label per row.
My issue was I parsed these labels out of an XML returned by a SOAP action and I had not idea over the format of the strings.
Firstly I implemented webstersx answer into a method. This was great but some of these labels began with a capital letter and some where camel case (e.g. some strings where exampleLabel and others where ExampleLabel. So this meant the ones beginning with a capital had a space inserted in front of the string.
I overcame this by trimming whitespaces from the beggining and end of the string using NSString's stringByTrimmingCharactersInSet.
The next issue was any abbreviations used, such as "ID" or "PNR Status", where being displayed as "I D" and "P N R Status" as the capital letters where, and quite rightly, being picked up and a space inserted before it.
I overcame this issue by implementing a regex similar to emdog4's answer into my new method.
Here is my completed solution:
- (NSString *)formatLabel:(NSString *)label
{
NSMutableString *str2 = [NSMutableString string];
for (NSInteger i=0; i<label.length; i++){
NSString *ch = [label substringWithRange:NSMakeRange(i, 1)];
if ([ch rangeOfCharacterFromSet:[NSCharacterSet uppercaseLetterCharacterSet]].location != NSNotFound) {
[str2 appendString:#" "];
}
[str2 appendString:ch];
}
NSString * formattedString = [str2 stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]].capitalizedString;
formattedString = [formattedString stringByReplacingOccurrencesOfString:#"([A-Z]) (?![A-Z][a-z])" withString:#"$1" options:NSRegularExpressionSearch range:NSMakeRange(0, formattedString.length)];
return formattedString;
}
I then simply call something like this, for example, that will return my nicely formatted string:
NSString * formattedLabel = [self formatLabel:#"PNRStatus"];
NSLog(#"Formatted Label: %#", formattedLabel);
Will output:
2013-10-10 10:44:39.888 Test Project[28296:a0b] Formatted Label: PNR Status
If anyone needs a Swift version:
func camelCaseToTitleCase(s: NSString) -> String {
var newString = ""
if s.length > 0 {
newString = s.substringToIndex(1).uppercaseString
for i in 1..<s.length {
let char = s.characterAtIndex(i)
if NSCharacterSet.uppercaseLetterCharacterSet().characterIsMember(char) {
newString += " "
}
newString += s.substringWithRange(NSRange(location: i, length: 1))
}
}
return newString
}
while technically shorter, more ineffecient
NSString *challengeString = #"playerName";
NSMutableString *rStr = [NSMutableString stringWithString:challengeString];
while ([rStr rangeOfCharacterFromSet:[NSCharacterSet uppercaseLetterCharacterSet]].location != NSNotFound) {
[rStr replaceCharactersInRange:[rStr rangeOfCharacterFromSet:[NSCharacterSet uppercaseLetterCharacterSet]] withString:[[NSString stringWithFormat:#" %#", [rStr substringWithRange:[rStr rangeOfCharacterFromSet:[NSCharacterSet uppercaseLetterCharacterSet]]]] lowercaseString]];
}
NSLog(#"%#", rStr.capitalizedString);
Not sure this is much shorter than websterx, but I find using characterIsMember easier to read and understand. Also added a length check to fix the space before if the string starts with a capital.
NSString *str = #"PlayerNameHowAboutALongerString";
NSMutableString *str2 = [NSMutableString string];
for (NSInteger i=0; i<str.length; i++){
unichar ch = [str characterAtIndex:i];
if ( [[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:ch]) {
if (str2.length > 0 ) {
[str2 appendString:#" "];
}
}
[str2 appendString:[NSString stringWithCharacters:&ch length:1]];
}
NSLog(#"--%#--", str2.capitalizedString);
The accepted answer didn't work for me because it doesn't capitalize the first letter, and if the first letter is already capitalized, it adds an extraneous space at the beginning. Here is my improved version:
- (NSString *)titleFromCamelCaseString:(NSString *)input
{
NSMutableString *output = [NSMutableString string];
[output appendString:[[input substringToIndex:1] uppercaseString]];
for (NSUInteger i = 1; i < [input length]; i++)
{
unichar character = [input characterAtIndex:i];
if ([[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:character])
{
[output appendString:#" "];
}
[output appendFormat:#"%C", character];
}
return output;
}
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)")
}
NSString *input = #"playerName";
NSString *modified = [input stringByReplacingOccurrencesOfString:#"(?<!^)[A-Z]" withString:#" $0" options:NSRegularExpressionSearch range:NSMakeRange(0, input.length)].capitalizedString;
Another solution under Swift 2.2
extension String {
var stringFromCamelCase:String {
return (self as NSString).replacingOccurrences(
of: "([a-z])([A-Z])",
with: "$1 $2",
options: CompareOptions.regularExpressionSearch,
range: NSMakeRange(0, self.characters.count)
).uppercaseFirst
}
var uppercaseFirst: String {
return String(characters.prefix(1)).uppercased() + String(characters.dropFirst()).lowercased()
}
}
try using:
string.Split()
then use the cap letter as token

Remove only first instance of a character from a list of characters

Here's what I want to do. I have 2 strings and I want to determine if one string is a permutation of another. I was thinking to simply remove the characters from string A from string B to determine if any characters are left. If no, then it passes.
However, I need to make sure that only 1 instance of each letter is removed (not all occurrences) unless there are multiple letters in the word.
An example:
String A: cant
String B: connect
Result: -o-nec-
Experimenting with NSString and NSScanner has yielded no results so far.
Hmmm, let's have a go:
NSString *stringA = #"cant";
NSString *stringB = #"connect";
NSUInteger length = [stringB length];
NSMutableCharacterSet *charsToRemove = [NSMutableCharacterSet characterSetWithCharactersInString:stringA];
unichar *buffer = calloc(length, sizeof(unichar));
[stringB getCharacters:buffer range:NSMakeRange(0, length)];
for (NSUInteger i = 0; i < length; i++)
{
if ([charsToRemove characterIsMember:buffer[i]])
{
[charsToRemove removeCharactersInRange:NSMakeRange(buffer[i], 1)];
buffer[i] = '-';
}
}
NSString *result = [NSString stringWithCharacters:buffer length:length];
free (buffer);
An inefficient yet simple way might be something like this (this is implemented as a category on NSString, but it could just as easily be a method or function taking two strings):
#implementation NSString(permutation)
- (BOOL)isPermutation:(NSString*)other
{
if( [self length] != [other length] ) return NO;
if( [self isEqualToString:other] ) return YES;
NSUInteger length = [self length];
NSCountedSet* set1 = [[[NSCountedSet alloc] initWithCapacity:length] autorelease];
NSCountedSet* set2 = [[[NSCountedSet alloc] initWithCapacity:length] autorelease];
for( int i = 0; i < length; i++ ) {
NSRange range = NSMakeRange(i, 1);
[set1 addObject:[self substringWithRange:range]];
[set2 addObject:[self substringWithRange:range]];
}
return [set1 isEqualTo:set2];
}
#end
This returns what your example asks for...
NSString* a = #"cant";
NSString* b = #"connect";
NSMutableString* mb = [NSMutableString stringWithString:b];
NSUInteger i;
for (i=0; i<[a length]; i++) {
NSString* theLetter = [a substringWithRange:NSMakeRange(i, 1)];
NSRange r = [mb rangeOfString:theLetter];
if (r.location != NSNotFound) {
[mb replaceCharactersInRange:r withString:#"-"];
}
}
NSLog(#"mb: %#", mb);
However, I wouldn't call that a permutation. To me a permutation would only hold true if all the characters from string "a" were contained by string "b". In your example, since the letter a in cant isn't in string b then I would say that cant is not a permutation of connect. With this definition I would use this:
-(BOOL)isString:(NSString*)firstString aPermutationOfString:(NSString*)secondString {
BOOL isPermutation = YES;
NSMutableString* mb = [NSMutableString stringWithString:secondString];
NSUInteger i;
for (i=0; i<[firstString length]; i++) {
NSString* theLetter = [firstString substringWithRange:NSMakeRange(i, 1)];
NSRange r = [mb rangeOfString:theLetter];
if (r.location != NSNotFound) {
[mb deleteCharactersInRange:r];
} else {
return NO;
}
}
return isPermutation;
}

Number of occurrences of a substring in an NSString?

How can I get the number of times an NSString (for example, #"cake") appears in a larger NSString (for example, #"Cheesecake, apple cake, and cherry pie")?
I need to do this on a lot of strings, so whatever method I use would need to be relatively fast.
Thanks!
This isn't tested, but should be a good start.
NSUInteger count = 0, length = [str length];
NSRange range = NSMakeRange(0, length);
while(range.location != NSNotFound)
{
range = [str rangeOfString: #"cake" options:0 range:range];
if(range.location != NSNotFound)
{
range = NSMakeRange(range.location + range.length, length - (range.location + range.length));
count++;
}
}
A regex like the one below should do the job without a loop interaction...
Edited
NSString *string = #"Lots of cakes, with a piece of cake.";
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"cake" options:NSRegularExpressionCaseInsensitive error:&error];
NSUInteger numberOfMatches = [regex numberOfMatchesInString:string options:0 range:NSMakeRange(0, [string length])];
NSLog(#"Found %i",numberOfMatches);
Only available on iOS 4.x and superiors.
was searching for a better method then mine but here's another example:
NSString *find = #"cake";
NSString *text = #"Cheesecake, apple cake, and cherry pie";
NSInteger strCount = [text length] - [[text stringByReplacingOccurrencesOfString:find withString:#""] length];
strCount /= [find length];
I would like to know which one is more effective.
And I made an NSString category for better usage:
// NSString+CountString.m
#interface NSString (CountString)
- (NSInteger)countOccurencesOfString:(NSString*)searchString;
#end
#implementation NSString (CountString)
- (NSInteger)countOccurencesOfString:(NSString*)searchString {
NSInteger strCount = [self length] - [[self stringByReplacingOccurrencesOfString:searchString withString:#""] length];
return strCount / [searchString length];
}
#end
simply call it by:
[text countOccurencesOfString:find];
Optional:
you can modify it to search case insensitive by defining options:
There are a couple ways you could do it. You could iteratively call rangeOfString:options:range:, or you could do something like:
NSArray * portions = [aString componentsSeparatedByString:#"cake"];
NSUInteger cakeCount = [portions count] - 1;
EDIT I was thinking about this question again and I wrote a linear-time algorithm to do the searching (linear to the length of the haystack string):
+ (NSUInteger) numberOfOccurrencesOfString:(NSString *)needle inString:(NSString *)haystack {
const char * rawNeedle = [needle UTF8String];
NSUInteger needleLength = strlen(rawNeedle);
const char * rawHaystack = [haystack UTF8String];
NSUInteger haystackLength = strlen(rawHaystack);
NSUInteger needleCount = 0;
NSUInteger needleIndex = 0;
for (NSUInteger index = 0; index < haystackLength; ++index) {
const char thisCharacter = rawHaystack[index];
if (thisCharacter != rawNeedle[needleIndex]) {
needleIndex = 0; //they don't match; reset the needle index
}
//resetting the needle might be the beginning of another match
if (thisCharacter == rawNeedle[needleIndex]) {
needleIndex++; //char match
if (needleIndex >= needleLength) {
needleCount++; //we completed finding the needle
needleIndex = 0;
}
}
}
return needleCount;
}
A quicker to type, but probably less efficient solution.
- (int)numberOfOccurencesOfSubstring:(NSString *)substring inString:(NSString*)string
{
NSArray *components = [string componentsSeparatedByString:substring];
return components.count-1; // Two substring will create 3 separated strings in the array.
}
Here is a version done as an extension to NSString (same idea as Matthew Flaschen's answer):
#interface NSString (my_substr_search)
- (unsigned) countOccurencesOf: (NSString *)subString;
#end
#implementation NSString (my_substring_search)
- (unsigned) countOccurencesOf: (NSString *)subString {
unsigned count = 0;
unsigned myLength = [self length];
NSRange uncheckedRange = NSMakeRange(0, myLength);
for(;;) {
NSRange foundAtRange = [self rangeOfString:subString
options:0
range:uncheckedRange];
if (foundAtRange.location == NSNotFound) return count;
unsigned newLocation = NSMaxRange(foundAtRange);
uncheckedRange = NSMakeRange(newLocation, myLength-newLocation);
count++;
}
}
#end
<somewhere> {
NSString *haystack = #"Cheesecake, apple cake, and cherry pie";
NSString *needle = #"cake";
unsigned count = [haystack countOccurencesOf: needle];
NSLog(#"found %u time%#", count, count == 1 ? #"" : #"s");
}
If you want to count words, not just substrings, then use CFStringTokenizer.
Here's another version as a category on NSString:
-(NSUInteger) countOccurrencesOfSubstring:(NSString *) substring {
if ([self length] == 0 || [substring length] == 0)
return 0;
NSInteger result = -1;
NSRange range = NSMakeRange(0, 0);
do {
++result;
range = NSMakeRange(range.location + range.length,
self.length - (range.location + range.length));
range = [self rangeOfString:substring options:0 range:range];
} while (range.location != NSNotFound);
return result;
}
Swift solution would be:
var numberOfSubstringAppearance = 0
let length = count(text)
var range: Range? = Range(start: text.startIndex, end: advance(text.startIndex, length))
while range != nil {
range = text.rangeOfString(substring, options: NSStringCompareOptions.allZeros, range: range, locale: nil)
if let rangeUnwrapped = range {
let remainingLength = length - distance(text.startIndex, rangeUnwrapped.endIndex)
range = Range(start: rangeUnwrapped.endIndex, end: advance(rangeUnwrapped.endIndex, remainingLength))
numberOfSubstringAppearance++
}
}
Matthew Flaschen's answer was a good start for me. Here is what I ended up using in the form of a method. I took a slightly different approach to the loop. This has been tested with empty strings passed to stringToCount and text and with the stringToCount occurring as the first and/or last characters in text.
I use this method regularly to count paragraphs in the passed text (ie. stringToCount = #"\r").
Hope this of use to someone.
- (int)countString:(NSString *)stringToCount inText:(NSString *)text{
int foundCount=0;
NSRange range = NSMakeRange(0, text.length);
range = [text rangeOfString:stringToCount options:NSCaseInsensitiveSearch range:range locale:nil];
while (range.location != NSNotFound) {
foundCount++;
range = NSMakeRange(range.location+range.length, text.length-(range.location+range.length));
range = [text rangeOfString:stringToCount options:NSCaseInsensitiveSearch range:range locale:nil];
}
return foundCount;
}
Example call assuming the method is in a class named myHelperClass...
int foundCount = [myHelperClass countString:#"n" inText:#"Now is the time for all good men to come to the aid of their country"];
for(int i =0;i<htmlsource1.length-search.length;i++){
range = NSMakeRange(i,search.length);
checker = [htmlsource1 substringWithRange:range];
if ([search isEqualToString:checker]) {
count++;
}
}
No built-in method. I'd suggest returning a c-string and using a common c-string style algorithm for substring counting... if you really need this to be fast.
If you want to stay in Objective C, this link might help. It describes the basic substring search for NSString. If you work with the ranges, adjust and count, then you'll have a "pure" Objective C solution... albeit, slow.
-(IBAction)search:(id)sender{
int maincount = 0;
for (int i=0; i<[self.txtfmainStr.text length]; i++) {
char c =[self.substr.text characterAtIndex:0];
char cMain =[self.txtfmainStr.text characterAtIndex:i];
if (c == cMain) {
int k=i;
int count=0;
for (int j = 0; j<[self.substr.text length]; j++) {
if (k ==[self.txtfmainStr.text length]) {
break;
}
if ([self.txtfmainStr.text characterAtIndex:k]==[self.substr.text characterAtIndex:j]) {
count++;
}
if (count==[self.substr.text length]) {
maincount++;
}
k++;
}
}
NSLog(#"%d",maincount);
}
}