Check if string is palindrome in objective c - objective-c

I'm trying to check if a string is palindrome or not using objective c. I'm new to programming without any experience in other programming languages so bear with me please. I get stuck at my if condition I want it to say that if the first position in the string is equal to the last one the string is a palindrome.
What im a doing wrong?
int main (int argc, const char * argv[])
{
NSString *p = #"121" ;
BOOL palindrome = TRUE;
for (int i = 0 ; i<p.length/2+1 ; i++)
{
if (p[i] != p [p.Length - i - 1])
palindrome = false;
}
return (0);
}

You're trying to use an NSString as an NSArray (or probably, like a C string), which won't work. Instead, you need to use the NSString method characterAtIndex: to get the character to test.

Apart from the unbalanced braces, accessing a character from NSString is more complicated than using array notation. You need to use the method characterAtIndex: You can optimise your code, by breaking out of the loop if a palindrome is impossible and taking the length call outside of the for loop.
NSString *p = #"121";
NSInteger length = p.length;
NSInteger halfLength = (length / 2);
BOOL isPalindrome = YES;
for (int i = 0; i < halfLength; i++) {
if ([p characterAtIndex:i] != [p characterAtIndex:length - i - 1]) {
isPalindrome = NO;
break;
}
}
It may be desirable to check case insensitively. To do this, make the string be all lowercase before looping, using the lowercaseString method.
As pointed out by Nikolai in the comments, this would only work for strings containing 'normal' unicode characters, which is often not true — such as when using UTF8 for foreign languages. If this is a possibility, use the following code instead, which checks composed character sequences rather than individual characters.
NSString *p = #"121";
NSInteger length = p.length;
NSInteger halfLength = length / 2;
__block BOOL isPalindrome = YES;
[p enumerateSubstringsInRange:NSMakeRange(0, halfLength) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
NSRange otherRange = [p rangeOfComposedCharacterSequenceAtIndex:length - enclosingRange.location - 1];
if (![substring isEqualToString:[p substringWithRange:otherRange]]) {
isPalindrome = NO;
*stop = YES;
}
}];

var str: NSString = "123321"
var length = str.length
var isPalindrome = true
for index in 0...length/2{
if(str.characterAtIndex(index) != str.characterAtIndex(length-1 - index)){
print("\(index )not palindrome")
isPalindrome = false
break
}
}
print("is palindrome: \(isPalindrome)")

As it seems there's no answer yet that handles composed character sequences correctly I'm adding my two cents:
NSString *testString = #"\u00E0 a\u0300"; // "à à"
NSMutableArray *logicalCharacters = [NSMutableArray array];
[testString enumerateSubstringsInRange:(NSRange){0, [testString length]}
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop)
{
[logicalCharacters addObject:substring];
}];
NSUInteger count = [logicalCharacters count];
BOOL isPalindrome = YES;
for (NSUInteger idx = 0; idx < count / 2; ++idx) {
NSString *a = logicalCharacters[idx];
NSString *b = logicalCharacters[count - idx - 1];
if ([a localizedCaseInsensitiveCompare:b] != NSOrderedSame) {
isPalindrome = NO;
break;
}
}
NSLog(#"isPalindrome: %d", isPalindrome);
This splits the string into an array of logical characters (elements of a string that a normal user would call a "character").

#import Foundation;
BOOL isPalindrome(NSString * str)
{
if (!str || str.length == 0) return NO;
if (str.length == 1) return YES;
for(unsigned i = 0; i < str.length / 2; ++i)
if ([str characterAtIndex:i] != [str characterAtIndex:str.length - i - 1]) return NO;
return YES;
}
int main() {
#autoreleasepool {
NSLog(#"%s", isPalindrome(#"applelppa") ? "YES" : "NO");
} return 0;
}

Recursive
- (BOOL)isPaliRec:(NSString*)str :(int)start :(int)end{
if(start >= end)
return YES;
else if([str characterAtIndex:start] != [str characterAtIndex:end])
return NO;
else
return [self isPaliRec:str :++start :--end];
}
Non Recursive
- (BOOL)isPali:(NSString*)str{
for (int i=0; i<str.length/2; i++)
if([str characterAtIndex:i] != [str characterAtIndex:(str.length-i-1)])
return NO;
return YES;
}
you can call:
NSString *str = #"arara";
[self isPaliRec:str :0 :(int)str.length-1];
[self isPali:str];
Swift 3:
// Recursive
func isPaliRec(str: String, start: Int = 0, end: Int = str.characters.count-1) -> Bool {
if start >= end {
return true
} else if str[str.index(str.startIndex, offsetBy: start)] != str[str.index(str.startIndex, offsetBy: end)] {
return false
} else {
return isPaliRec(str: str, start: start+1, end: end-1)
}
}
// Non Recursive
func isPali(str: String) -> Bool {
for i in 0..<str.characters.count/2 {
let endIndex = str.characters.count-i-1
if str[str.index(str.startIndex, offsetBy: i)] != str[str.index(str.startIndex, offsetBy: endIndex)] {
return false
}
}
return true
}
// Using
let str = "arara"
isPaliRec(str: str)
isPali(str: str)
Also, you can use swift 3 methods like a string extension... It's more elegant. extension sample

NSString *str=self.txtFld.text;
int count=str.length-1;
for (int i=0; i<count; i++) {
char firstChar=[str characterAtIndex:i];
char lastChar=[str characterAtIndex:count-i];
NSLog(#"first=%c and last=%c",firstChar,lastChar);
if (firstChar !=lastChar) {
break;
}
else
NSLog(#"Pailndrome");
}

We can also do this using NSRange like this...
enter code NSString *fullname=#"123321";
NSRange rangeforFirst=NSMakeRange(0, 1);
NSRange rangeforlast=NSMakeRange(fullname.length-1, 1);
BOOL ispalindrome;
for (int i=0; i<fullname.length; i++) {
if (![[fullname substringWithRange:rangeforFirst] isEqualToString:[fullname substringWithRange:rangeforlast]]) {
NSLog(#"not match");
ispalindrome=NO;
return;
}
i++;
rangeforFirst=NSMakeRange(i, 1);
rangeforlast=NSMakeRange(fullname.length-i-1, 1);
}
NSLog(#"no is %#",(ispalindrome) ? #"matched" :#"not matched");

NSString *str1 = #"racecar";
NSMutableString *str2 = [[NSMutableString alloc] init];
NSInteger strLength = [str1 length]-1;
for (NSInteger i=strLength; i>=0; i--)
{
[str2 appendString:[NSString stringWithFormat:#"%C",[str1 characterAtIndex:i]]];
}
if ([str1 isEqual:str2])
{
NSLog(#"str %# is palindrome",str1);
}

-(BOOL)checkPalindromeNumber:(int)number{
int originalNumber,reversedNumber = 0,remainder;
originalNumber=number;
while (number!=0) {
remainder=number%10;
reversedNumber=(reversedNumber*10)+remainder;
number=number/10;
}
if (reversedNumber==originalNumber) {
NSLog(#"%d is Palindrome Number",originalNumber);
return YES;
}
else{
NSLog(#"%d is Not Palindrome Number",originalNumber);
return NO;
}
}

Related

Anagram algorithm objective C

i have written the following code to check anagram want to know is this perfect & is there any better way to implement the same in objective C
-(BOOL) findAnagram :(NSString *) string1 :(NSString *) string2
{
int len = string1.length;
if (len != string2.length)
{
return false;
}
for (int i=0; i < len; i++)
{
int h = 0;
int q = 0;
for (int k = 0; k < len ; k ++)
{
if ([string1 characterAtIndex:i] == [string1 characterAtIndex:k])
{
h++;
}
if ([string1 characterAtIndex:i] == [string2 characterAtIndex:k])
{
q++;
}
}
if (h!=q)
{
return false;
}
}
return TRUE;
}
A better performing version than yours, which is a O(n ^ 2) algorithm, is a O(n) algorithm:
BOOL anagrams(NSString *a, NSString *b)
{
if (a.length != b.length)
return NO;
NSCountedSet *aSet = [[NSCountedSet alloc] init];
NSCountedSet *bSet = [[NSCountedSet alloc] init];
for (int i = 0; i < a.length; i++)
{
[aSet addObject:#([a characterAtIndex:i])];
[bSet addObject:#([b characterAtIndex:i])];
}
return [aSet isEqual:bSet];
}
You want to know if two strings contain exactly the same characters? Easiest way would probably be to sort both of them and compare the sorted version.
Another way would be to count the number of appearances of each letter (how many As, how many Bs, and so forth), then compare those counts.
(Note: The second way is just a variation of the first one, it's one efficient way to sort a string)
It looks fine to me. But the code style is slightly odd. I would write it like this:
- (BOOL)isStringAnagram:(NSString *)string1 ofString:(NSString *)string2 {
int len = string1.length;
if (len != string2.length) {
return NO;
}
for (int i=0; i < len; i++) {
int h = 0;
int q = 0;
for (int k = 0; k < len; k++) {
if ([string1 characterAtIndex:i] == [string1 characterAtIndex:k]) {
h++;
}
if ([string1 characterAtIndex:i] == [string2 characterAtIndex:k]) {
q++;
}
}
if (h != q) {
return NO;
}
}
return YES;
}
The main issue I have is with the method name. While it's possible to have parameters that have nothing before them in the name, it is not advisable. i.e. you had findAnagram:: as the name whereas I've used isStringAnagram:ofString:.
This is an implementation on #zmbq suggestion of sorting and comparing.
You should consider the requirements of deleting spaces and being case insensitive.
- (BOOL)isAnagram:(NSString *)leftString and:(NSString *)rightString {
NSString *trimmedLeft = [[leftString stringByReplacingOccurrencesOfString:#" " withString:#""] lowercaseString];
NSString *trimmedRight = [[rightString stringByReplacingOccurrencesOfString:#" " withString:#""] lowercaseString];
return [[self stringToCharArraySorted:trimmedLeft] isEqual:[self stringToCharArraySorted:trimmedRight]];
}
- (NSArray *)stringToCharArraySorted:(NSString *)string {
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0 ; i < string.length ; i++) {
[array addObject:#([string characterAtIndex:i])];
}
return [[array sortedArrayUsingSelector:#selector(compare:)] copy];
}
called like this
BOOL isAnagram = [self isAnagram:#"A BC" and:#"cba"];
Check the following method which check Anagram strings.
-(BOOL)checkAnagramString:(NSString*)string1 WithAnotherString:(NSString*)string2{
NSCountedSet *countSet1=[[NSCountedSet alloc]init];
NSCountedSet *countSet2=[[NSCountedSet alloc]init];
if (string1.length!=string2.length) {
NSLog(#"NOT ANAGRAM String");
return NO;
}
for (int i=0; i<string1.length; i++) {
[countSet1 addObject:#([string1 characterAtIndex:i])];
[countSet2 addObject:#([string2 characterAtIndex:i])];
}
if ([countSet1 isEqual:countSet2]) {
NSLog(#"ANAGRAM String");
return YES;
} else {
NSLog(#"NOT ANAGRAM String");
return NO;
}
}
Another run of the mill algorithm:
- (BOOL) testForAnagramWithStrings:(NSString *)stringA andStringB: (NSString *)stringB{
stringA = [stringA lowercaseString];
stringB = [stringB lowercaseString];
int counter = 0;
for (int i=0; i< stringA.length; i++){
for (int j=0; j<stringB.length;j++){
if ([stringA characterAtIndex:i]==[stringB characterAtIndex:j]){
counter++;
}
}
}
if (counter!= stringA.length){
return false;
}
return true;
}

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!

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. :/