compare characters from nsstring to different charactersets - objective-c

I want to compare each character in an nsstring one by one to different nscharactersets and perform different operations according to the characterset it matches.
I'm able to assign each character to a substring for comparison using a for loop.
- (void) compareCharactersOfWord: (NSString *) word {
for (int i = 0; i<[word length]; i++) {
NSString *substring = [word substringWithRange:NSMakeRange(i,1)];
//need to compare the substring to characterset here
}
}
I also have my two charactersets
setOne = [[NSCharacterSet characterSetWithCharactersInString:#"EAIONRTLSU"]invertedSet];
setTwo = [[NSCharacterSet characterSetWithCharactersInString:#"DG"] invertedSet];
I'm a bit lost on the comparison part. I tried different methods like "rangeOfCharacterFromSet" but I kept getting erros. in pseudocode I would need something like
if (setOne containsCharacterFrom substring) {
//do stuff here
} else if (setTwo containsCharacterFrom substring) {
//do other stuff here
}

To see if your 'substring' variable in one of your sets you would do:
if ([substring rangeOfCharacterFromSet:setOne].location != NSNotFound) {
// substring is in setOne
} else if ([substring rangeOfCharacterFromSet:setTwo].location != NSNotFound) {
// substring is in setTwo
}
Another option is to work with characters.
for (int i = 0; i<[word length]; i++) {
unichar ch = [word characterAtIndex:i];
if ([setOne characterIsMember:ch]) {
// in setOne
} else if ([setTwo characterIsMember:ch]) {
// in setTwo
}
}
There one big limitation to the second option. It doesn't work with Unicode characters higher than 0xFFFF.

You need to extract each character (unichar) from the string and use [NSCharacterSet characterIsMember:] to determine if it's part of either NSCharacterSet:
- (void) compareCharactersOfWord: (NSString *)word
{
// These could be initialised globally to speed things up a little...
NSCharacterSet *setOne = [[NSCharacterSet characterSetWithCharactersInString:#"EAIONRTLSU"] invertedSet];
NSCharacterSet *setTwo = [[NSCharacterSet characterSetWithCharactersInString:#"DG"] invertedSet];
for (NSUInteger index = 0; index < [word length]; index++)
{
unichar c = [word characterAtIndex:index];
if ([setOne characterIsMember:c])
{
// c is a member of character set #1
}
else if ([setTwo characterIsMember:c])
{
// c is a member of character set #2
}
else
{
// c is a member of neither character set
}
}
}

Related

Check if NSString contains all or some characters

I have an NSString called query which contains ~10 characters.
I would like to check to see if a second NSString called word contains all of the characters in query, or some characters, but no other characters which aren't specified in query.
Also, if there is only one occurrence of the character in the query, there can only be one occurrence of the character in the word.
Please could you tell me how to do this?
NSString *query = #"ABCDEFJAKSUSHFKLAFIE";
NSString *word = #"fearing"; //would pass as NO as there is no 'n' in the query var.
The following answers the first half:
NSCharacterSet *nonQueryChars = [[NSCharacterSet characterSetWithCharactersInString:[query lowercaseString]] invertedSet];
NSRange badCharRange = [[word lowercaseString] rangeOfCharacterFromSet:nonQueryChars];
if (badCharRange.location == NSNotFound) {
// word only has characters in query
} else {
// found unwanted characters in word
}
I need to think about the second half of the requirement.
Ok, the following code should fulfill both requirements:
- (NSCountedSet *)wordLetters:(NSString *)text {
NSCountedSet *res = [NSCountedSet set];
for (NSUInteger i = 0; i < text.length; i++) {
[res addObject:[text substringWithRange:NSMakeRange(i, 1)]];
}
return res;
}
- (void)checkWordAgainstQuery {
NSString *query = #"ABCDEFJAKSUSHFKLAFIE";
NSString *word = #"fearing";
NSCountedSet *queryLetters = [self wordLetters:[query lowercaseString]];
NSCountedSet *wordLetters = [self wordLetters:[word lowercaseString]];
BOOL ok = YES;
for (NSString *wordLetter in wordLetters) {
int wordCount = [wordLetters countForObject:wordLetter];
// queryCount will be 0 if this word letter isn't in query
int queryCount = [queryLetters countForObject:wordLetter];
if (wordCount > queryCount) {
ok = NO;
break;
}
}
if (ok) {
// word matches against query
} else {
// word has extra letter or too many of a matching letter
}
}

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);
}
}