154

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?

dakab
  • 5,379
  • 9
  • 43
  • 67
Ward
  • 3,318
  • 3
  • 30
  • 50

20 Answers20

319

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;
}
Jeff B
  • 29,943
  • 7
  • 61
  • 90
  • 1
    genRandStringLength should just return randomString. There's no reason to alloc and init (and not autorelease!) a whole new string. – kevingessner Apr 14 '10 at 00:49
  • Fixed. Should the return type be NSMutableString? Or is it fine with NSString? – Jeff B Apr 14 '10 at 01:08
  • This is a useful macro to get a very random number: `#define RKRandom(x) (arc4random() % ((NSUInteger)(x) + 1))` – Rob Keniger Apr 14 '10 at 02:59
  • 5
    Just going to put this out there. There's no reason to use an `NSString` for `letters` when a simple `char` array would work just fine. In fact, using `[letters characterAtIndex:(rand() % [letters length])]` seems to me to be less concise than just `letters[rand() % strlen(letters)]`. The Foundation classes are really helpful, but for the simplest things, they can serve to obfusticate our code rather than enhance it. – Jonathan Sterling Apr 14 '10 at 05:59
  • @kevingessner Technically, in general, you should probably return [[randomString copy] autorelease]. This is because you then get an immutable result which cannot be changed deliberately or accidentally by other code. – JeremyP Apr 14 '10 at 10:36
  • @JeremyP - I guess. I'm not that [randomString copy] will return an actually immutable string; that's an implementation detail. Since genRandStringLength is defined to return an NSString, it doesn't matter that it's "actually" an NSMutableString; any code that tries to change it is wrong. – kevingessner Apr 14 '10 at 15:20
  • @Jonathan: I actually started with exactly what you suggested in fact. I am trying to learn to do things with the Foundation classes, however, so I rewrote it the other way. I agree, it might be overkill in this case, but it is an example of how to go about it the "objective-c way". – Jeff B Apr 14 '10 at 15:41
  • @kevingessner -copy returns an immutable object "if the consideration “immutable vs. mutable” applies to the receiving object" - from the documentation of NSCopying. As regards mutable versus immutable, I agree that, in this case, the difference is marginal, but if other code does accidentally send -appendString to the return result, the resulting invalid selector exception is easier to debug than a mysterious and unexplained change in a string you weren't expecting. – JeremyP Apr 15 '10 at 21:38
  • 3
    you might want `%C` instead of `%c`, because `characterAtIndex:` returns a `unichar` – user102008 Mar 04 '11 at 21:01
  • 1
    rand() doesn't provide a very random number. Try changing it to arc4random(). – adjwilli May 05 '12 at 16:36
  • 8
    Using arc4random would generate a biased result when the array length is not a power of two. arc4random_uniform fixes this. – jlukanta May 21 '13 at 04:26
  • 1
    So, is it an updated answer based on the suggestion in comments? – S.Philip May 19 '14 at 10:25
  • 1
    It must be `arc4random_uniform([letters length])`. From the man pages `arc4random(void);` and `arc4random_uniform(u_int32_t upper_bound);` – knshn Jun 11 '14 at 08:43
  • 9
    oh the compiler would give a warning for losing precision, so better to cast to int `arc4random_uniform((int)[letters length])` – knshn Jun 12 '14 at 09:58
  • 1
    If you don't want to use a static `NSString*` variable you can replace `alphabet` inside of the for loop with `[NSCharacterSet alphanumericCharacterSet]` and change `[alphabet length]` to `[[NSCharacterSet alphanumericCharacterSet] count]` – rolling_codes Jul 05 '14 at 15:58
  • re: Daneil Rinser's apt point modulo bias, see http://eternallyconfuzzled.com/arts/jsw_art_rand.aspx for a nice description of the issue – dave adelson Aug 05 '14 at 18:52
  • `% [letters length]` seems unnecessary since `arc4random_uniform([letters length])` does the trick on its own. – Emmett Oct 20 '14 at 23:54
  • @Savagewood NSCharacterSet does not have a `-count` method bud. – pronebird Oct 22 '15 at 17:36
111

Not exactly what you ask, but still useful:

[[NSProcessInfo processInfo] globallyUniqueString]

Sample output:

450FEA63-2286-4B49-8ACC-9822C7D4356B-1376-00000239A4AC4FD5
myeyesareblind
  • 3,342
  • 3
  • 16
  • 9
  • 2
    This is by far the shortest and most straightforward way to address the question. – adib Jan 06 '15 at 14:02
  • Even if it has hyphens in it — if that's not a concern then awesome! – fatuhoku Jan 23 '15 at 20:57
  • Best answer by a mile – Rambatino Feb 09 '15 at 14:46
  • Perfect for my need to "generate a random alphanumeric string in cocoa". Not exactly what OP asks only because he adds the requirement to "pass it the length" which YAGNI ! – jkoreska Apr 09 '15 at 22:57
  • 5
    This is probably ok for most uses but DO NOT USE if you need a random string for security purposes. Unique != Random. The length is constant, the range of characters used is limited (0-9, A-F, - = 17, vs 62 for a-Z. 0-9). This string is unique but predictable. – amcc Jul 02 '15 at 11:16
67
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];
}
Melvin
  • 3,421
  • 2
  • 37
  • 41
47

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;
}
John Riselvato
  • 12,854
  • 5
  • 62
  • 89
30

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.

Adil Hussain
  • 30,049
  • 21
  • 112
  • 147
Abhi Beckert
  • 32,787
  • 12
  • 83
  • 110
24

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
keithyip
  • 985
  • 7
  • 21
7

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.

TechZen
  • 64,370
  • 15
  • 118
  • 145
  • I would strongly advise against this for anything security related. Pseudo-randomness is one of the biggest vunerabilities hackers use in penetrating systems, because they provide predictability. Use as close to real-random as possible. – Shayne Apr 01 '13 at 06:11
5

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)))]
    })
}
ma11hew28
  • 121,420
  • 116
  • 450
  • 651
4

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.

rjferguson
  • 603
  • 3
  • 12
3

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)
}
Gralex
  • 4,285
  • 7
  • 26
  • 47
2

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")
Wex
  • 4,434
  • 3
  • 33
  • 47
1

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.

orkoden
  • 18,946
  • 4
  • 59
  • 50
1

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;
}
Mr. T
  • 12,795
  • 5
  • 39
  • 47
1

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 ##
Patrick
  • 1,629
  • 5
  • 23
  • 44
emotality
  • 12,795
  • 4
  • 39
  • 60
1

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

Stan Tatarnykov
  • 691
  • 9
  • 17
1
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])]];
}
Peter Lapisu
  • 19,915
  • 16
  • 123
  • 179
1

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;
}
erkanyildiz
  • 13,044
  • 6
  • 50
  • 73
1

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
}
S1LENT WARRIOR
  • 11,704
  • 4
  • 46
  • 60
0
#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;
}
TtheTank
  • 332
  • 3
  • 8
0

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;
}
iutinvg
  • 3,479
  • 3
  • 21
  • 18