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

I'm looking for the easiest way to convert a string from camelback format to Title Case format.
How do I change 'playerName' into 'Player Name'?

NSString *str = #"playerName";
NSMutableString *str2 = [NSMutableString string];
for (NSInteger i=0; i<str.length; i++){
NSString *ch = [str substringWithRange:NSMakeRange(i, 1)];
if ([ch rangeOfCharacterFromSet:[NSCharacterSet uppercaseLetterCharacterSet]].location != NSNotFound) {
[str2 appendString:#" "];
}
[str2 appendString:ch];
}
NSLog(#"%#", str2.capitalizedString);

Here's a simpler Swift version. I've chucked it into an extension
extension String {
func stringFromCamelCase() -> String {
var string = self
string = string.stringByReplacingOccurrencesOfString("([a-z])([A-Z])", withString: "$1 $2", options: NSStringCompareOptions.RegularExpressionSearch, range: Range<String.Index>(start: string.startIndex, end: string.endIndex))
string.replaceRange(startIndex...startIndex, with: String(self[startIndex]).capitalizedString)
return string
}
}
Usage:
var str = "helloWorld"
str = str.stringFromCamelCase()

Try using a regex replace
NSString *modified = [input stringByReplacingOccurrencesOfString:#"([a-z])([A-Z])"
withString:#"$1 $2"
options:NSRegularExpressionSearch
range:NSMakeRange(0, input.length)];

A little shorter, using NSCharacterSet:
__block NSString *str = #"myVerySpecialPlayerName" ;
// split at uppercase letters
NSArray *splitString = [str componentsSeparatedByCharactersInSet:
[NSCharacterSet uppercaseLetterCharacterSet]] ;
// get the uppercase letters
NSArray *upperCaseLetters = [str componentsSeparatedByCharactersInSet:
[[NSCharacterSet uppercaseLetterCharacterSet] invertedSet]] ;
// join with two spaces
str = [splitString componentsJoinedByString:#" "] ;
__block NSInteger offset = 0 ;
// replace each second space with the missing uppercase letter
[upperCaseLetters enumerateObjectsUsingBlock:^(NSString *character, NSUInteger idx, BOOL *stop) {
if( [character length] > 0 ) {
str = [str stringByReplacingCharactersInRange:NSMakeRange(idx+offset+1, 1) withString:character] ;
offset += 2 ;
}
}] ;
// & capitalize the first one
str = [str capitalizedString] ;
NSLog(#"%#", str) ; // "My Very Special Player Name"

Trying to be more unicode compliant
extension String {
func camelCaseToTitleCase() -> String {
return unicodeScalars.map(replaceCaptialsWithSpacePlusCapital).joined().capitalized
}
private func replaceCaptialsWithSpacePlusCapital(unichar: UnicodeScalar) -> String {
if CharacterSet.uppercaseLetters.contains(unichar) {
return " \(unichar)"
}
return "\(unichar)"
}
}

I think you can tackle this problem with some Regular Expressions. Check out this similar question: iPhone dev: Replace uppercase characters in NSString with space and downcase

Although a little long, but this category for NSString should do the trick. It passed all my tests:
- (NSString *)splitOnCapital
{
// Make a index of uppercase characters
NSRange upcaseRange = NSMakeRange('A', 26);
NSIndexSet *upcaseSet = [NSIndexSet indexSetWithIndexesInRange:upcaseRange];
// Split our camecase word
NSMutableString *result = [NSMutableString string];
NSMutableString *oneWord = [NSMutableString string];
for (int i = 0; i < self.length; i++) {
char oneChar = [self characterAtIndex:i];
if ([upcaseSet containsIndex:oneChar]) {
// Found a uppercase char, now save previous word
if (result.length == 0) {
// First word, no space in beginning
[result appendFormat:#"%#", [oneWord capitalizedString]];
}else {
[result appendFormat:#" %#", oneWord];
}
// Clear previous word for new word
oneWord = [NSMutableString string];
}
[oneWord appendFormat:#"%c", oneChar];
}
// Add last word
if (oneWord.length > 0) {
[result appendFormat:#" %#", oneWord];
}
return result;
}

I had a similar issue, the answers here helped me create a solution. I had an array that had a list of labels I wanted to display within a UITableView, one label per row.
My issue was I parsed these labels out of an XML returned by a SOAP action and I had not idea over the format of the strings.
Firstly I implemented webstersx answer into a method. This was great but some of these labels began with a capital letter and some where camel case (e.g. some strings where exampleLabel and others where ExampleLabel. So this meant the ones beginning with a capital had a space inserted in front of the string.
I overcame this by trimming whitespaces from the beggining and end of the string using NSString's stringByTrimmingCharactersInSet.
The next issue was any abbreviations used, such as "ID" or "PNR Status", where being displayed as "I D" and "P N R Status" as the capital letters where, and quite rightly, being picked up and a space inserted before it.
I overcame this issue by implementing a regex similar to emdog4's answer into my new method.
Here is my completed solution:
- (NSString *)formatLabel:(NSString *)label
{
NSMutableString *str2 = [NSMutableString string];
for (NSInteger i=0; i<label.length; i++){
NSString *ch = [label substringWithRange:NSMakeRange(i, 1)];
if ([ch rangeOfCharacterFromSet:[NSCharacterSet uppercaseLetterCharacterSet]].location != NSNotFound) {
[str2 appendString:#" "];
}
[str2 appendString:ch];
}
NSString * formattedString = [str2 stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]].capitalizedString;
formattedString = [formattedString stringByReplacingOccurrencesOfString:#"([A-Z]) (?![A-Z][a-z])" withString:#"$1" options:NSRegularExpressionSearch range:NSMakeRange(0, formattedString.length)];
return formattedString;
}
I then simply call something like this, for example, that will return my nicely formatted string:
NSString * formattedLabel = [self formatLabel:#"PNRStatus"];
NSLog(#"Formatted Label: %#", formattedLabel);
Will output:
2013-10-10 10:44:39.888 Test Project[28296:a0b] Formatted Label: PNR Status

If anyone needs a Swift version:
func camelCaseToTitleCase(s: NSString) -> String {
var newString = ""
if s.length > 0 {
newString = s.substringToIndex(1).uppercaseString
for i in 1..<s.length {
let char = s.characterAtIndex(i)
if NSCharacterSet.uppercaseLetterCharacterSet().characterIsMember(char) {
newString += " "
}
newString += s.substringWithRange(NSRange(location: i, length: 1))
}
}
return newString
}

while technically shorter, more ineffecient
NSString *challengeString = #"playerName";
NSMutableString *rStr = [NSMutableString stringWithString:challengeString];
while ([rStr rangeOfCharacterFromSet:[NSCharacterSet uppercaseLetterCharacterSet]].location != NSNotFound) {
[rStr replaceCharactersInRange:[rStr rangeOfCharacterFromSet:[NSCharacterSet uppercaseLetterCharacterSet]] withString:[[NSString stringWithFormat:#" %#", [rStr substringWithRange:[rStr rangeOfCharacterFromSet:[NSCharacterSet uppercaseLetterCharacterSet]]]] lowercaseString]];
}
NSLog(#"%#", rStr.capitalizedString);

Not sure this is much shorter than websterx, but I find using characterIsMember easier to read and understand. Also added a length check to fix the space before if the string starts with a capital.
NSString *str = #"PlayerNameHowAboutALongerString";
NSMutableString *str2 = [NSMutableString string];
for (NSInteger i=0; i<str.length; i++){
unichar ch = [str characterAtIndex:i];
if ( [[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:ch]) {
if (str2.length > 0 ) {
[str2 appendString:#" "];
}
}
[str2 appendString:[NSString stringWithCharacters:&ch length:1]];
}
NSLog(#"--%#--", str2.capitalizedString);

The accepted answer didn't work for me because it doesn't capitalize the first letter, and if the first letter is already capitalized, it adds an extraneous space at the beginning. Here is my improved version:
- (NSString *)titleFromCamelCaseString:(NSString *)input
{
NSMutableString *output = [NSMutableString string];
[output appendString:[[input substringToIndex:1] uppercaseString]];
for (NSUInteger i = 1; i < [input length]; i++)
{
unichar character = [input characterAtIndex:i];
if ([[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:character])
{
[output appendString:#" "];
}
[output appendFormat:#"%C", character];
}
return output;
}

Here is Swift Code (objective c code by webstersx), Thanks !
var str: NSMutableString = "iLoveSwiftCode"
var str2: NSMutableString = NSMutableString()
for var i:NSInteger = 0 ; i < str.length ; i++ {
var ch:NSString = str.substringWithRange(NSMakeRange(i, 1))
if(ch .rangeOfCharacterFromSet(NSCharacterSet.uppercaseLetterCharacterSet()).location != NSNotFound) {
str2 .appendString(" ")
}
str2 .appendString(ch)
}
println("\(str2.capitalizedString)")
}

NSString *input = #"playerName";
NSString *modified = [input stringByReplacingOccurrencesOfString:#"(?<!^)[A-Z]" withString:#" $0" options:NSRegularExpressionSearch range:NSMakeRange(0, input.length)].capitalizedString;

Another solution under Swift 2.2
extension String {
var stringFromCamelCase:String {
return (self as NSString).replacingOccurrences(
of: "([a-z])([A-Z])",
with: "$1 $2",
options: CompareOptions.regularExpressionSearch,
range: NSMakeRange(0, self.characters.count)
).uppercaseFirst
}
var uppercaseFirst: String {
return String(characters.prefix(1)).uppercased() + String(characters.dropFirst()).lowercased()
}
}

try using:
string.Split()
then use the cap letter as token

Related

How do I format a string to Credit card format

What is the easiest way to format a string "1234567890123456789" to "1234 5678 9012 3456 789" in IOS?
Try this:
-(NSString *) correctString:(NSString *) anyStr {
NSMutableString *str=[NSMutableString stringWithString:anyStr];
int indx=4;
while (indx<str.length) {
[str insertString:#" " atIndex:indx];
indx +=5;
}
anyStr=str;
return anyStr;
}
For that particular format, you could do something like the following, which extracts the individual substrings:
NSString *string = #"1234567890123456789";
NSMutableArray *array = [NSMutableArray array];
for (NSInteger i = 0; i < [string length]; i += 4)
[array addObject:[string substringWithRange:NSMakeRange(i, MIN(4, [string length] - i))]];
NSString *result = [array componentsJoinedByString:#" "];
The thing is, not all credit cards conform to the xxxx xxxx xxxx xxxx format. E.g., Amex uses a xxxx xxxxxx xxxxx format. You really should look at the first digits of the card, determine the type of card, and format it accordingly.
You asked if you could do it with a regular expression. Consider this regex:
NSString *result = [string stringByReplacingOccurrencesOfString:#"^[\\s-]*([0-9]{4})[\\s-]*([0-9]{4})[\\s-]*([0-9]{4})[\\s-]*([0-9]{4})[\\s-]*([0-9]{3})[\\s-]*$"
withString:#"$1 $2 $3 $4 $5"
options:NSRegularExpressionSearch
range:NSMakeRange(0, [string length])];
That will convert any of the following:
#"1234567890123456789"
#"1234-5678-9012-3456-789"
#" 1234567890123456789 "
into:
#"1234 5678 9012 3456 789"
While you could use regular expression, it's sufficiently opaque that I wouldn't particularly advise it. But it can be done.
I wrote code.
NSMutableString *string = #"1234567890123456789";
NSInteger *ip = 4;
for (NSInteger i = 0; i*4 < [string length] ; i++)
{
[string insertString:#" " atIndex:ip];
ip = ip+5;
}
I propose to use NSString category. In not ARC just add autorelease after self copy.
My variant will not add spaces after last digits quarter if it is not nescessary.
Applicable to use in UITextField.
- (NSString *)creditCardNumberFormatedString {
NSString *string = [self copy];
NSUInteger length = string.length;
if (length >= 17) {
string = [string substringToIndex:16];
length = 16;
}
BOOL isSpaceRequired = YES;
if (length == 4) {
isSpaceRequired = NO;
}
NSString *newString = [NSString new];
while (string.length > 0) {
NSString *subString = [string substringToIndex:MIN(string.length, 4)];
newString = [newString stringByAppendingString:subString];
if (subString.length == 4 && isSpaceRequired) {
newString = [newString stringByAppendingString:#" "];
}
string = [string substringFromIndex:MIN(string.length, 4)];
if (string.length <= 4) {
isSpaceRequired = NO;
}
}
return newString;
}
Here is a Swift extension:
extension String {
var pairs: [String] {
var result: [String] = []
let chars = Array(characters)
for index in 0.stride(to: chars.count, by: 4) {
result.append(String(chars[index..<min(index+4, chars.count)]))
}
return result
}
}
To use:
let string : String = "1234567890123456789"
let finalString = string.pairs.joinWithSeparator(" ") //1234 5678 9012 3456 789
print(finalString)
For Swift 3:
extension String {
var pairs: [String] {
var result: [String] = []
let chars = Array(characters)
for index in stride(from: 0, to: chars.count, by: 4){
result.append(String(chars[index..<min(index+4, chars.count)]))
}
return result
}
}

Reverse NSString text

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

creating NSMutableString from two other NSMutableStrings

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

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

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