Reverse NSString text - objective-c

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!

Related

Substring to nth character

I need to substring to the 2nd comma in an NSString.
Input:
NSString *input = #"title, price, Camry, $19798, active";
Desired Output:
NSString *output = #"title, price";
Thanks!
UPDATE:
I have the following but the problem is it needs to skip the last comma:
NSString *output = [input rangeOfString:#"," options:NSBackwardsSearch];
Try this:
- (NSString *)substringOfString:(NSString *)base untilNthOccurrence:(NSInteger)n ofString:(NSString *)delim
{
NSScanner *scanner = [NSScanner scannerWithString:base];
NSInteger i;
for (i = 0; i < n; i++)
{
[scanner scanUpToString:delim intoString:NULL];
[scanner scanString:delim intoString:NULL];
}
return [base substringToIndex:scanner.scanLocation - delim.length];
}
this code should do what you need:
NSString *input = #"title, price, Camry, $19798, active";
NSArray *array = [input componentsSeparatedByString:#","];
NSArray *subArray = [array subarrayWithRange:NSMakeRange(0, 2)];
NSString *output = [subArray componentsJoinedByString:#","];
NSLog(output);
You could split -> splice -> join that string like this in objc:
NSString *input = #"title, price, Camry, $19798, active";
// split by ", "
NSArray *elements = [input componentsSeparatedByString: #", "];
// grab the subarray
NSArray *subelements = [elements subarrayWithRange: NSMakeRange(0, 2)];
// concat by ", " again
NSString *output = [subelements componentsJoinedByString:#", "];
You can try something like this:
NSArray *items = [list componentsSeparatedByString:#", "];
NSString result = #"";
result = [result stringByAppendingString:[items objectAtIndex:0]];
result = [result stringByAppendingString:#", "];
result = [result stringByAppendingString:[items objectAtIndex:1]];
You have to check you have at least two items if you want avoid an exception.
There's really nothing wrong with simply writing the code to do what you want. Eg:
int commaCount = 0;
int i;
for (i = 0; i < input.count; i++) {
if ([input characterAtIndex:i] == (unichar) ',') {
commaCount++;
if (commaCount == 2) break;
}
}
NSString output = nil;
if (commaCount == 2) {
output = [input substringToIndex:i];
}
You could create an NSString category to handle finding nth occurrences of any string. This is example is for ARC.
//NSString+MyExtension.h
#interface NSString(MyExtension)
-(NSString*)substringToNthOccurrence:(NSUInteger)nth
ofString:(NSString*)string;
-(NSString*)substringToNthOccurrence:(NSUInteger)nth
ofString:(NSString*)string
options:(NSStringCompareOptions)options;
#end
#implementation NSString(MyExtension)
-(NSString*)substringToNthOccurrence:(NSUInteger)nth
ofString:(NSString*)string
{
return [self substringToNthOccurrence:nth ofString:string options:0];
}
-(NSString*)substringToNthOccurrence:(NSUInteger)nth
ofString:(NSString*)string
options:(NSStringCompareOptions)options
{
NSUInteger location = 0,
strlength = [string length],
mylength = [self length];
NSRange range = NSMakeRange(location, mylength);
while(nth--)
{
location = [self rangeOfString:string
options:options
range:range].location;
if(location == NSNotFound || (location + strlength) > mylength)
{
return [self copy]; //nth occurrence not found
}
if(nth == 0) strlength = 0; //This prevents the last occurence from being included
range = NSMakeRange(location + strlength, mylength - strlength - location);
}
return [self substringToIndex:location];
}
#end
//main.m
#import "NSString+MyExtension.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
NSString *output = [#"title, price, Camry, $19798, active" substringToNthOccurrence:2 ofString:#","];
NSLog(#"%#", output);
}
}
*I'll leave it as an exercise for someone to implement the mutable versions.

Count number of newlines in a file

What is the smallest code I can use to count the number of occurrences of the newline character in a file with objective-c / cocoa touch?
Thanks!
Both of the other answers are correct, but with the caveat that they require loading the entire file into memory to work.
The way around that is to load the file incrementally using an NSFileHandle. Something like this:
NSFileHandle * file = [NSFileHandle fileHandleForReadingAtPath:pathToFile];
NSUInteger chunkSize = 1024;
NSData * chunk = [file readDataOfLength:chunkSize];
NSUInteger numberOfNewlines = 0;
while ([chunk length] > 0) {
const unichar * bytes = (const unichar *)[chunk bytes];
for (int index = 0; index < [chunk length]; ++index) {
unichar character = (unichar)bytes[index];
if ([[NSCharacterSet newlineCharacterSet] characterIsMember:character]) {
numberOfNewlines++;
}
}
chunk = [file readDataOfLength:chunkSize];
}
This should get you going:
NSString *fileContents = [NSString stringWithContentsOfFile:file encoding:encoding error:&error];
NSUInteger newlineCount = [fileContents numberOfOccurrencesOfString:#"\n"];
#interface NSString ()
- (NSUInteger)numberOfOccurrencesOfString:(NSString *)aString;
- (NSUInteger)numberOfOccurrencesOfChar:(char)aChar;
#end
#implementation NSString ()
- (NSUInteger)numberOfOccurrencesOfString:(NSString *)aString {
NSRange range = [self rangeOfString:aString];
NSUInteger length = [self length];
NSUInteger count = 0;
while (range.location != NSNotFound) {
range = [self rangeOfString:aString options:0 range:NSMakeRange(range.location + range.length, length - range.location - range.length)];
count++;
}
return count;
}
- (NSUInteger)numberOfOccurrencesOfChar:(char)aChar {
const char *cString = [self cStringUsingEncoding:NSUTF8StringEncoding];
NSUInteger stringLength = strlen(cString);
NSUInteger count = 0;
for (int i = 0; i < stringLength; i++) {
if (cString[i] == aChar) {
count++;
}
}
return count;
}
#end
While "numberOfOccurrencesOfString:" allocates no additional memory and supports string needles,
"numberOfOccurrencesOfChar:" allocates an autoreleased c-string copy of the NSString and searches for a single char. ""
As you were asking for a count of newlines (hence single chars) I figured a quick benchmark might be good for this particular purpose:
So I took a test string of length 2486813 containing total of 78312 '\n'. (I basically took a variation of OSX's words file) and…
…ran [testString numberOfOccurrencesOfString:#"\n"] 100 times: 19.35s
…ran [testString numberOfOccurrencesOfChar:'\n'] 100 times: 6.91s
(Setup: 2.2GHz Core 2 Duo MacBook Pro, run on a single thread)
[Edit: small bug fixed; made second snippet into category string method.]
You can scan through the string using SubstringWithRange:
Count up the number of times \n appears.
smallest you say? That automatically turns this question into code golf
FILE*f=fopen(path,"r");
int i,c;
while(1+c)i+=(c=fgetc(f))==10;
printf("%i",i);
(please don't ever actually use this code)
If you want to stay within Cocoa/CocoaTouch, you can use NSRegularExpression for this:
NSString *theString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"\\n" options:NSRegularExpressionCaseInsensitive error:&error];
NSUInteger numLines = [regex numberOfMatchesInString:theString options:0 range:NSMakeRange(0, [theString length])] + 1;

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

CamelCase to underscores and back in 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. :/