在Cocoa中生成一个随机的字母数字字符串

154

我想调用一个方法,将长度传递给它,并让它生成一个随机的字母数字字符串。

是否有任何实用库可以提供这些类型的函数?

20个回答

319

这是一个快速而简单的实现。尚未经过测试。

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

5
我来翻译了。不妨说一下,使用一个简单的 char 数组很好地解决了问题,完全没有必要用 NSString。事实上,使用 [letters characterAtIndex:(rand() % [letters length])] 似乎比仅仅使用 letters[rand() % strlen(letters)] 更加冗长。Foundation 类库确实很有帮助,但对于最简单的任务,它们可能会让我们的代码变得晦涩难懂而非使其更清晰易读。 - Jonathan Sterling
3
由于characterAtIndex:返回的是一个unichar,所以你可能需要使用%C而不是%c - user102008
8
当数组长度不是 2 的幂时,使用 arc4random 会生成偏倚的结果。arc4random_uniform 解决了这个问题。 - jlukanta
1
那么,这是基于评论中建议更新的答案吗? - S.Philip
9
编译器会因为精度丢失而发出警告,所以最好将其强制转换为整数:arc4random_uniform((int)[letters length]) - knshn
显示剩余13条评论

111

虽然不完全符合您的要求,但仍然有用:

[[NSProcessInfo processInfo] globallyUniqueString]

样例输出:

450FEA63-2286-4B49-8ACC-9822C7D4356B-1376-00000239A4AC4FD5

2
这绝对是回答问题最短、最直接的方式。 - adib
即使它有连字符——如果这不是一个问题,那太棒了! - fatuhoku
一英里之外的最佳答案 - Rambatino
非常适合我的需求,即在Cocoa中“生成随机的字母数字字符串”。不完全是OP所要求的,因为他添加了“传递长度”的要求,而YAGNI! - jkoreska
5
这对于大多数情况可能都可以,但如果您需要用于安全目的的随机字符串,请勿使用。独特≠随机。长度是恒定的,使用的字符范围有限(0-9、A-F、- = 17,而a-Z、0-9的范围是62)。该字符串是独特的但是可预测的。 - amcc

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

13
你真的需要每次查询 alphabet 的长度吗?它是常量,不会随循环而改变。 - jv42
1
通过生成每个密码长度为10个字符的100万个密码进行测试,结果非常好。 - NaXir
1
NSArray 缓存了它的 length,不应该成为性能瓶颈。 - Pascal
我同意。这是一个简单的属性访问,它不会在每次请求时进行计数。 - devios1

47

你肯定可以把这个缩短一点:

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

2
为什么是-1?这完全没有问题。这就像所有答案中最小、最优化的答案一样。 - John Riselvato
5
这个没问题。这是一个不错且简洁的解决方案。(我希望 Stack Overflow 能够强制要求在投票反对时添加评论。) - alvi
1
最佳解决方案。我唯一的意见是将其设为静态方法。 - Andrei Tchijov
10
唯一的缺点是它不是真正的字母数字混合体,只是小写字母。;-) - PrimaryFeather
@MarcusAdams,那是不正确的。在这种情况下,arc4random_uniform 是从 0 到上限(25) 。Z 是第 25 个字母,A 是零。 - John Riselvato
显示剩余3条评论

30

如果您只愿意使用十六进制字符,那么最简单的选择是生成UUID:

NSString *uuid = [NSUUID UUID].UUIDString;

示例输出:16E3DF0B-87B3-4162-A1A1-E03DB2F59654

如果您想要一个更小的随机字符串,可以只取前8个字符。

这是一个版本4的UUID,这意味着第3组和第4组中的第一个字符不是随机的(它们将始终是489AB中的一个)。

字符串中的其他每个字符都是完全随机的,您可以每秒生成数百万个UUID,数百年而不用太担心重复生成相同的UUID。


1
你可以简单地使用 NSString *uuid = [UUID UUID] - orkoden
@orkoden 谢谢,我的代码是从一些iOS 5的代码中得来的。我会更新我的答案,使用更新的iOS 6 / OS X 10.8 API。 - Abhi Beckert
@AbhiBeckert 是否可以只使用前8个字符而不会出现相同的前8个字符,这样做安全吗? - Vishal Singh
1
@VishalSingh 是的,它是安全的,但显然你把它变得越短,冲突的风险就越高。 - Abhi Beckert
3
将代码中的 "UUID" 改为 "NSUUID" 可以解决“使用未声明的标识符 UUID”的错误,修改后的代码为:NSString *uuid = [NSUUID UUID].UUIDString; - Abbas Mulani

24

Jeff B的回答的一个分类版本。

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

1
这是如何使用分类的绝佳示例! - ElmerCat

7

你也可以生成UUID。虽然不是真正的随机数,但它们复杂而独特,使它们在大多数情况下看起来像随机数。将其生成为字符串,然后取一个字符范围等于传递的长度。


我强烈建议在任何安全相关的事情上都不要这样做。伪随机性是黑客用来渗透系统的最大漏洞之一,因为它们提供可预测性。尽可能接近真正的随机性。 - Shayne

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

1
简短而有效。对我来说很管用,除非我更改了字母表,因为字母表长度是硬编码的。我用UInt32(count(alphabet))替换了64。 - scootermg
好的。我用计算出来的 upperBound 替换了硬编码的 64。我在块外计算 upperBound,因为我认为这样性能更好。 - ma11hew28
无法使用 '(String)' 参数列表调用 'count',可以通过使用 'alphabet.characters.count' 来处理。 - Zaporozhchenko Oleksandr
1
返回alphabet[alphabet.startIndex.advancedBy(Int(arc4random_uniform(upperBound)))] - Zaporozhchenko Oleksandr

4

这里有一种不同的解决方法。不使用预定义的字符字符串,而是可以在整数和字符之间进行转换,并生成一个动态的字符列表来选择。它非常简洁和快速,但需要更多的代码。

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

在进行随机字符生成时,我更喜欢直接使用整数并将其转换,而不是编写我想要抽取的字符列表。在顶部声明变量使其更具系统独立性,但此代码假定数字的值低于字母,并且大写字母的值低于小写字母。


3

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接