How do I iterate through an NSString in objective c? - objective-c

How can iterate through an NSString object in Objective c whiling maintaining an index for the character I am currently at?
I want to increment the ASCII value of every third character by 3, and then print this incremented character in a label in my user interface.

Wasn't clear whether you just wanted to print the incremented characters or all. If the former, here's is how you would do it:
NSString *myString = #"myString";
NSMutableString *newString = [NSMutableString string];
for (int i = 0; i < [myString length]; i++)
{
int ascii = [myString characterAtIndex:i];
if (i % 3 == 0)
{
ascii++;
[newString appendFormat:#"%c",ascii];
}
}
myLabel.text = newString;

Will this do the trick?
NSString *incrementString(NSString *input)
{
const char *inputUTF8 = [input UTF8String]; // notice we get the buffers so that we don't have to deal with the overhead of making many message calls.
char *outputUTF8 = calloc(input.length + 1, sizeof(*outputUTF8));
for (int i = 0; i < input.length; i++)
{
outputUTF8[i] = i % 3 == 0 ? inputUTF8[i] + 3 : inputUTF8[i];
}
NSString *ret = [NSString stringWithUTF8String:outputUTF8];
free(outputUTF8); // remember to free the buffer when done!
return ret;
}

Related

Best way to split integers in Objective C

If I have this:
int toSplit = 208;
What's the best way to split it so I get:
2
0
8
Method would be like this
- (NSMutableArray *) toCharArray : (NSString *) str
{
NSMutableArray *characters = [[NSMutableArray alloc] initWithCapacity:[str length]];
for (int i=0; i < [str length]; i++)
{
NSString *ichar = [NSString stringWithFormat:#"%c", [str characterAtIndex:i]];
[characters addObject:ichar];
}
return characters;
}
do {
int digit = toSplit % 10;
toSplit /= 10;
printf(#"%i", digit);
} while (toSplit > 0);
I hope it's not a homework.
EDIT: it's backwards so it's not a valid answer... However, leaving it here because it can still be useful for others.
Sort it into a string then pull it apart. So like this:
int toSplit = 208;
NSString *string = [NSString stringWithFormat: #"%i", toSplit];
NSMutableString *splitApartString = [[NSMutableString alloc] init];
for (int i = 0; i < [string length]; i++) {
NSString *substring = [string substringFromIndex: i];
[splitApartString appendFormat: #"%#\n", substring];
}
NSLog(#"%#", splitApartString); //YAY!
So what this does, is puts this int into a string, splits it apart, then iterates through each character and gets a string out of that character. Then it appends that substring to a NEW string.
Another alternative, is instead of getting a substring just get the char and use the %c operator. Also if you take a look at this code you will see this output:
> 2
> 0
> 8
> // extra space here
You could just add a condition to check if i is the string length - 1 and not add a space or you could just remove the last character!
I'm not familiar with Objective-C syntax, but something like:
void split(int toSplit)
{
if (!toSplit)
{
return;
}
split(toSplit / 10);
int digit = toSplit % 10;
printf(#"%i", digit);
}

Convert NSString to C string, increment and come back to NSString

I'm trying to develop a simple application where i can encrypt a message. The algorithm is Caesar's algorithm and for example, for 'Hello World' it prints 'KHOOR ZRUOG' if the increment is 3 (standard).
My problem is how to take each single character and increment it...
I've tried this:
NSString *text = #"hello";
int q, increment = 3;
NSString *string;
for (q = 0; q < [text length]; q++) {
string = [text substringWithRange:NSMakeRange(q, 1)];
const char *c = [string UTF8String] + increment;
NSLog(#"%#", [NSString stringWithUTF8String:c]);
}
very simple but it doesn't work.. My theory was: take each single character, transform into c string and increment it, then return to NSString and print it, but xcode print nothing, also if i print the char 'c' i can't see the result in console. Where is the problem?
First of all, incrementing byte by byte only works for ASCII strings. If you use UTF-8, you will get garbage for glyphs that have multi-byte representations.
With that in mind, this should work (and work faster than characterAtIndex: and similar methods):
NSString *foo = #"FOOBAR";
int increment = 3;
NSUInteger bufferSize = [foo length] + 1;
char *buffer = (char *)calloc(bufferSize, sizeof(char));
if ([foo getCString:buffer maxLength:bufferSize encoding:NSASCIIStringEncoding]) {
int bufferLen = strlen(buffer);
for (int i = 0; i < bufferLen; i++) {
buffer[i] += increment;
if (buffer[i] > 'Z') {
buffer[i] -= 26;
}
}
NSString *encoded = [NSString stringWithCString:buffer
encoding:NSASCIIStringEncoding];
}
free(buffer);
first of all replace your code with this:
for (q = 0; q < [text length]; q++) {
string = [text substringWithRange:NSMakeRange(q, 1)];
const char *c = [string UTF8String];
NSLog(#"Addr: 0x%X", c);
c = c + increment;
NSLog(#"Addr: 0x%X", c);
NSLog(#"%#", [NSString stringWithUTF8String:c]);
}
Now you can figure out your problem. const char *c is a pointer. A pointer saves a memory address.
When I run this code the first log output is this:
Addr: 0x711DD10
that means the char 'h' from the NSString named string with the value #"h" is saved at address 0x711DD10 in memory.
Now we increment this address by 3. Next output is this:
Addr: 0x711DD13
In my case at this address there is a '0x00'. But it doesn't matter what is actually there because a 'k' won't be there (unless you are very lucky).
If you are happy there is a 0x00 too. Because then nothing bad will happen. If you are unlucky there is something else. If there is something other than 0x00 (or the string delimiter or "end of string") NSString will try to convert it. It might crash while trying this, or it might open a huge security hole.
so instead of manipulating pointers you have to manipulate the values where they point to.
You can do this like this:
for (q = 0; q < [text length]; q++) {
string = [text substringWithRange:NSMakeRange(q, 1)];
const char *c = [string UTF8String]; // get the pointer
char character = *c; // get the character from this pointer address
character = character + 3; // add 3 to the letter
char cString[2] = {0, 0}; // create a cstring with length of 1. The second char is \0, the delimiter (the "end marker") of the string
cString[0] = character; // assign our changed character to the first character of the cstring
NSLog(#"%#", [NSString stringWithUTF8String:cString]); // profit...
}

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

Generate a random alphanumeric string in Cocoa

I want to call a method, pass it the length and have it generate a random alphanumeric string.
Are there any utility libraries out there that may have a bunch of these types of functions?
Here's a quick and dirty implementation. Hasn't been tested.
NSString *letters = #"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
-(NSString *) randomStringWithLength: (int) len {
NSMutableString *randomString = [NSMutableString stringWithCapacity: len];
for (int i=0; i<len; i++) {
[randomString appendFormat: #"%C", [letters characterAtIndex: arc4random_uniform([letters length])]];
}
return randomString;
}
Not exactly what you ask, but still useful:
[[NSProcessInfo processInfo] globallyUniqueString]
Sample output:
450FEA63-2286-4B49-8ACC-9822C7D4356B-1376-00000239A4AC4FD5
NSString *alphabet = #"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXZY0123456789";
NSMutableString *s = [NSMutableString stringWithCapacity:20];
for (NSUInteger i = 0U; i < 20; i++) {
u_int32_t r = arc4random() % [alphabet length];
unichar c = [alphabet characterAtIndex:r];
[s appendFormat:#"%C", c];
}
Surely you can make this shorter:
+(NSString*)generateRandomString:(int)num {
NSMutableString* string = [NSMutableString stringWithCapacity:num];
for (int i = 0; i < num; i++) {
[string appendFormat:#"%C", (unichar)('a' + arc4random_uniform(26))];
}
return string;
}
If you're willing to limit yourself to hex characters only, then the simplest option is to generate a UUID:
NSString *uuid = [NSUUID UUID].UUIDString;
Example output: 16E3DF0B-87B3-4162-A1A1-E03DB2F59654.
If you want a smaller random string then you can grab just the first 8 characters.
It's a version 4 UUID which means the first character in the 3rd and 4th group is not random (they will always be 4 and one of 8, 9, A or B).
Every other character in the string is fully random and you can generate millions of UUIDs every second for hundreds of years without much risk of the same UUID being generated twice.
A category version of Jeff B's answer.
NSString+Random.h
#import <Foundation/Foundation.h>
#interface NSString (Random)
+ (NSString *)randomAlphanumericStringWithLength:(NSInteger)length;
#end
NSString+Random.m
#import "NSString+Random.h"
#implementation NSString (Random)
+ (NSString *)randomAlphanumericStringWithLength:(NSInteger)length
{
NSString *letters = #"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
NSMutableString *randomString = [NSMutableString stringWithCapacity:length];
for (int i = 0; i < length; i++) {
[randomString appendFormat:#"%C", [letters characterAtIndex:arc4random() % [letters length]]];
}
return randomString;
}
#end
You could also just generate a UUID. While not truly random, they are complex and unique which makes them appear random for most uses. Generate one as a string and then take a range of characters equal to the passed length.
Swift
func randomStringWithLength(length: Int) -> String {
let alphabet = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
let upperBound = UInt32(count(alphabet))
return String((0..<length).map { _ -> Character in
return alphabet[advance(alphabet.startIndex, Int(arc4random_uniform(upperBound)))]
})
}
Here's a different way to tackle it. Instead of using a prepared string of characters, you can cast between integers and characters, and generate a dynamic list of characters to select. It's pretty lean and fast, but has a bit more code.
int charNumStart = (int) '0';
int charNumEnd = (int) '9';
int charCapitalStart = (int) 'A';
int charCapitalEnd = (int) 'Z';
int charLowerStart = (int) 'a';
int charLowerEnd = (int) 'z';
int amountOfChars = (charNumEnd - charNumStart) + (charCapitalEnd - charCapitalStart) + (charLowerEnd - charLowerStart); // amount of the characters we want.
int firstGap = charCapitalStart - charNumEnd; // there are gaps of random characters between numbers and uppercase letters, so this allows us to skip those.
int secondGap = charLowerStart - charCapitalEnd; // similar to above, but between uppercase and lowercase letters.
// START generates a log to show us which characters we are considering for our UID.
NSMutableString *chars = [NSMutableString stringWithCapacity:amountOfChars];
for (int i = charNumStart; i <= charLowerEnd; i++) {
if ((i >= charNumStart && i <= charNumEnd) || (i >= charCapitalStart && i <= charCapitalEnd) || (i >= charLowerStart && i <= charLowerEnd)) {
[chars appendFormat:#"\n%c", (char) i];
}
}
NSLog(#"chars: %#", chars);
// END log
// Generate a uid of 20 characters that chooses from our desired range.
int uidLength = 20;
NSMutableString *uid = [NSMutableString stringWithCapacity:uidLength];
for (int i = 0; i < uidLength; i++) {
// Generate a random number within our character range.
int randomNum = arc4random() % amountOfChars;
// Add the lowest value number to line this up with a desirable character.
randomNum += charNumStart;
// if the number is in the letter range, skip over the characters between the numbers and letters.
if (randomNum > charNumEnd) {
randomNum += firstGap;
}
// if the number is in the lowercase letter range, skip over the characters between the uppercase and lowercase letters.
if (randomNum > charCapitalEnd) {
randomNum += secondGap;
}
// append the chosen character.
[uid appendFormat:#"%c", (char) randomNum];
}
NSLog(#"uid: %#", uid);
// Generate a UID that selects any kind of character, including a lot of punctuation. It's a bit easier to do it this way.
int amountOfAnyCharacters = charLowerEnd - charNumStart; // A new range of characters.
NSMutableString *multiCharUid = [NSMutableString stringWithCapacity:uidLength];
for (int i = 0; i < uidLength; i++) {
// Generate a random number within our new character range.
int randomNum = arc4random() % amountOfAnyCharacters;
// Add the lowest value number to line this up with our range of characters.
randomNum += charNumStart;
// append the chosen character.
[multiCharUid appendFormat:#"%c", (char) randomNum];
}
NSLog(#"multiCharUid: %#", multiCharUid);
When I'm doing random character generation, I prefer to work directly with integers and cast them over, instead of writing out the list of chars that I want to draw from. Declaring the variables at the top makes it more system independent, but this code assumes that numbers will have a lower value than letters, and that uppercase letters will have a lower value than lowercase letters.
Alternative solution in Swift
func generateString(len: Int) -> String {
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
let lettersLength = UInt32(countElements(letters))
let result = (0..<len).map { _ -> String in
let idx = Int(arc4random_uniform(lettersLength))
return String(letters[advance(letters.startIndex, idx)])
}
return "".join(result)
}
Modification of a few ideas here, and in done Swift 4.0
extension String
{
subscript (i: Int) -> Character
{
return self[index(startIndex, offsetBy:i)]
}
static func Random(length:Int=32, alphabet:String="ABCDEF0123456789") -> String
{
let upperBound = UInt32(alphabet.count)
return String((0..<length).map { _ -> Character in
return alphabet[Int(arc4random_uniform(upperBound))]
})
}
}
Usage:
let myHexString = String.Random()
let myLongHexString = String.Random(length:64)
let myLettersString = String.Random(length:32, alphabet:"ABCDEFGHIJKLMNOPQRSTUVWXYZ")
If you want a random unicode string, you can create random bytes and then use the valid ones.
OSStatus sanityCheck = noErr;
uint8_t * randomBytes = NULL;
size_t length = 200; // can of course be variable
randomBytes = malloc( length * sizeof(uint8_t) );
memset((void *)randomBytes, 0x0, length);
sanityCheck = SecRandomCopyBytes(kSecRandomDefault, length, randomBytes);
if (sanityCheck != noErr) NSLog(#"Error generating random bytes, OSStatus == %ld.", sanityCheck);
NSData* randomData = [[NSData alloc] initWithBytes:(const void *)randomBytes length: length];
if (randomBytes) free(randomBytes);
NSString* dataString = [[NSString alloc] initWithCharacters:[randomData bytes] length:[randomData length]]; // create an NSString from the random bytes
NSData* tempData = [dataString dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES]; // remove illegal characters from string
NSString* randomString = [[NSString alloc] initWithData:tempData encoding:NSUTF8StringEncoding];
The conversion from NSString to NSData and back is necessary to get a valid UTF-8 string.
Be aware that length will not necessarily be the length of the the NSString created in the end.
I did this using a simple char[] instead of an NSString * for the alphabet. I added this to a NSString category.
static const char __alphabet[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
+ (NSString *)randomString:(int)length
{
NSMutableString *randomString = [NSMutableString stringWithCapacity:length];
u_int32_t alphabetLength = (u_int32_t)strlen(__alphabet);
for (int i = 0; i < length; i++) {
[randomString appendFormat:#"%c", __alphabet[arc4random_uniform(alphabetLength)]];
}
return randomString;
}
Adding to good answer given by Melvin, here is a function I made (in SWIFT!) to get a random string:
func randomStringOfLength(length:Int)->String{
var wantedCharacters:NSString="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXZY0123456789"
var s=NSMutableString(capacity: length)
for (var i:Int = 0; i < length; i++) {
let r:UInt32 = arc4random() % UInt32( wantedCharacters.length)
let c:UniChar = wantedCharacters.characterAtIndex( Int(r) )
s.appendFormat("%C", c)
}
return s
}
Here is a test result from calling randomStringOfLength(10): uXa0igA8wm
static NSUInteger length = 32;
static NSString *letters = #"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
NSMutableString * randomString = [NSMutableString stringWithCapacity:length];
for (NSInteger i = 0; i < length; ++i) {
[randomString appendFormat: #"%C", [letters characterAtIndex:(NSUInteger)arc4random_uniform((u_int32_t)[letters length])]];
}
Generates lowercase alphanumeric random string with given length:
-(NSString*)randomStringWithLength:(NSUInteger)length
{
NSMutableString* random = [NSMutableString stringWithCapacity:length];
for (NSUInteger i=0; i<length; i++)
{
char c = '0' + (unichar)arc4random()%36;
if(c > '9') c += ('a'-'9'-1);
[random appendFormat:#"%c", c];
}
return random;
}
Method to call:
NSString *string = [self stringWithRandomSuffixForFile:#"file.pdf" withLength:4]
Method:
- (NSString *)stringWithRandomSuffixForFile:(NSString *)file withLength:(int)length
{
NSString *alphabet = #"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
NSString *fileExtension = [file pathExtension];
NSString *fileName = [file stringByDeletingPathExtension];
NSMutableString *randomString = [NSMutableString stringWithFormat:#"%#_", fileName];
for (int x = 0; x < length; x++) {
[randomString appendFormat:#"%C", [alphabet characterAtIndex: arc4random_uniform((int)[alphabet length]) % [alphabet length]]];
}
[randomString appendFormat:#".%#", fileExtension];
NSLog(#"## randomString: %# ##", randomString);
return randomString;
}
Results:
## randomString: file_Msci.pdf ##
## randomString: file_xshG.pdf ##
## randomString: file_abAD.pdf ##
## randomString: file_HVwV.pdf ##
for Swift 3.0
func randomString(_ length: Int) -> String {
let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
let len = UInt32(letters.length)
var randomString = ""
for _ in 0 ..< length {
let rand = arc4random_uniform(len)
var nextChar = letters.character(at: Int(rand))
randomString += NSString(characters: &nextChar, length: 1) as String
}
return randomString
}
#define ASCII_START_NUMERS 0x30
#define ASCII_END_NUMERS 0x39
#define ASCII_START_LETTERS_A 0x41
#define ASCII_END_LETTERS_Z 0x5A
#define ASCII_START_LETTERS_a 0x61
#define ASCII_END_LETTERS_z 0x5A
-(NSString *)getRandomString:(int)length {
NSMutableString *result = [[NSMutableString alloc]init];
while (result.length != length) {
NSMutableData* data = [NSMutableData dataWithLength:1];
SecRandomCopyBytes(kSecRandomDefault, 1, [data mutableBytes]);
Byte currentChar = 0;
[data getBytes:&currentChar length:1];
NSString *s = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (currentChar > ASCII_START_NUMERS && currentChar < ASCII_END_NUMERS) { // 0 to 0
[result appendString:s];
continue;
}
if (currentChar > ASCII_START_LETTERS_A && currentChar < ASCII_END_LETTERS_Z) { // 0 to 0
[result appendString:s];
continue;
}
if (currentChar > ASCII_START_LETTERS_a && currentChar < ASCII_END_LETTERS_z) { // 0 to 0
[result appendString:s];
continue;
}
}
return result;
}
Modification for keithyip's answer:
+ (NSString *)randomAlphanumericStringWithLength:(NSInteger)length
{
static NSString * const letters = #"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
srand(time(NULL));
});
NSMutableString *randomString = [NSMutableString stringWithCapacity:length];
for (int i = 0; i < length; i++) {
[randomString appendFormat:#"%C", [letters characterAtIndex:arc4random() % [letters length]]];
}
return randomString;
}