JavaScript中的随机字母数字字符串?

295

在JavaScript中,生成一个随机的包含字母和数字(大写、小写字母以及数字)的字符串,并将其作为可能唯一的标识符,最短的合理方法是什么?


29
最短路?这是一道代码高尔夫问题吗? - Greg Hewgill
6
哈哈,不是的!这不是一个比谁能把代码压缩得更紧密的比赛。我曾经看到一些解决方案在字符串中列出了整个字符集,这似乎很浪费。只是希望找到长度不超过必要程度的解决方案。 - Pavel
5
@Pavel,这就是代码高尔夫的精髓。 - Naftali
2
@Pavel https://dev59.com/dnM_5IYBdhLWcg3wgja2@Pavel 在https://dev59.com/dnM_5IYBdhLWcg3wgja2上。 - Sony Mathew
11
去除冗余是良好的工程实践,代码高尔夫是编写最少量的代码(通常涉及单字符变量、副作用和其他不良实践)。它们相似但截然不同。 - mikemaccana
如果您正在使用LodashUnderscore,那么请参考https://dev59.com/dnM_5IYBdhLWcg3wgja2#36734713。 - vineet
24个回答

454

我刚刚发现这是一个非常好的优雅的解决方案:

Math.random().toString(36).slice(2)

这个实现的注意事项:

  • 由于浮点数字符串化会删除尾部零,因此它将产生长度在零到12个字符之间的字符串,通常为11个字符。
  • 它不会生成大写字母,只有小写字母和数字。
  • 由于随机性来自Math.random(),输出可能是可预测的,因此不一定是唯一的。
  • 即使假设是理想的实现,输出也最多具有52位熵,这意味着您可以在生成约70M个字符串后期望重复出现。

4
使用 slice(2) 替代 substr(2,16):http://www.jacklmoore.com/notes/substring-substr-slice-javascript - Web_Designer
1
@kimos 很好的发现。解决这个问题的一种方法是再试一次。例如:function rStr() { var s=Math.random().toString(36).slice(2); return s.length===16 ? s : rStr(); } - rescuecreative
9
您的网站很繁忙吗?那么有时它会生成小于0.000001的随机数。较小数字的toString()类似于3.4854687E-7,您的随机字母数字字符串将是4854687E-7,不再是字母数字。这不符合原贴作者的要求。 - Adrian Pronk
3
toString方法接受一个进制参数,即将整数转换为字符串的“基数”。我认为最容易理解的方式是 var a = 5;a.toString(2);将以二进制格式返回数字5,即101。完整文档请参见:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString - JAR.JAR.beans
1
可能有些人还没有意识到,您可以通过增加 .slice(2) 中提供的数字(例如 .slice(5))来获得更短的字符串。 - rinogo
显示剩余11条评论

368

如果您只想允许特定字符,您还可以按照以下方式进行:

function randomString(length, chars) {
    var result = '';
    for (var i = length; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)];
    return result;
}
var rString = randomString(32, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');

这里有一个 jsfiddle 来演示:http://jsfiddle.net/wSQBx/

另一种做法是使用特殊字符串告诉函数要使用哪些字符类型。可以像这样实现:

function randomString(length, chars) {
    var mask = '';
    if (chars.indexOf('a') > -1) mask += 'abcdefghijklmnopqrstuvwxyz';
    if (chars.indexOf('A') > -1) mask += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    if (chars.indexOf('#') > -1) mask += '0123456789';
    if (chars.indexOf('!') > -1) mask += '~`!@#$%^&*()_+-={}[]:";\'<>?,./|\\';
    var result = '';
    for (var i = length; i > 0; --i) result += mask[Math.floor(Math.random() * mask.length)];
    return result;
}

console.log(randomString(16, 'aA'));
console.log(randomString(32, '#aA'));
console.log(randomString(64, '#A!'));

Fiddle: http://jsfiddle.net/wSQBx/2/

或者,要使用下面描述的base36方法,您可以这样做:

function randomString(length) {
    return Math.round((Math.pow(36, length + 1) - Math.random() * Math.pow(36, length))).toString(36).slice(1);
}

1
@Matthew 这可能是你的浏览器在输出字符串中捕捉到了一个无意的错误标签。检查输出源代码,看看是否有一些不规则的 < 和 > 导致问题。 - Nimphious
3
这里有一个统计错误。Math.round(Math.random() * (chars.length - 1))将会给出比其它字符多一半的概率给[0][chars.length-1],因为为了舍入到这些数,需要分别使用区间[0, 0.5)[chars.length-2+0.5, chars.length-1)大小为0.5的数字,而其它字符需要使用大小为1的区间[index-0.5, index+0.5)。正确的索引函数应该是:parseInt(Math.random() * chars.length),因为随机数函数中的上限是被排除的,并且chars.length不会被达到:http://www.w3schools.com/jsref/jsref_random.asp - axelbrz
5
请提供你的参考资料。Math.random()永远不会返回1。它返回的值在[0,1)之间,这意味着0包括在内,1排除在外。以下是四个不同且重要的独立来源:Mozilla:http://goo.gl/BQGZiG;Microsoft MSDN(IE):http://goo.gl/MPuCJM;w3schools:http://goo.gl/qXrK7P;Stackoverflow:http://goo.gl/CUoyIT(`num`为`1`)和http://goo.gl/D9az3C和http://goo.gl/0mERDq。你有什么依据认为1是Math.random()可能的值?因为它不是。 - axelbrz
5
我已经自作主张修复了代码中的统计错误。因为这是谷歌搜索“随机JavaScript字符串”的排名第一结果,所以有一个有缺陷的答案很可惜。 - Iftah
当长度为11时,使用IE获取Z2K3TMUJW23(E+11)。实际上,对于10以上的每个数字都会得到科学计数法。在Chrome中没有问题。 - TheBakker
显示剩余10条评论

50

更新: 针对随机的20个字符(字母数字且为小写),提供一行解决方案:

Array.from(Array(20), () => Math.floor(Math.random() * 36).toString(36)).join('');

使用lodash可以更简短:

_.times(20, () => _.random(35).toString(36)).join('');

39

JAR.JAR.beans建议的另一种答案变体。

(Math.random()*1e32).toString(36)

通过更改乘数1e32,您可以更改随机字符串的长度。


2
好的回答,帮了我很多。使用1e32,我得到了19个字符长的字符串,而使用1e64,我得到了40个字符。非常感谢! - Bogdan Le
3
请注意,字符串长度并不保证。这意味着1e64并不总是会产生40个字符长度的字符串,但它会接近于40个字符。 - he-yaeh
我明白了!我正在寻找一种简单的解决方案,可以生成接近40个字符长的随机字符串。 - Bogdan Le
好的答案。然而,它不能生成大写字母。 - Abdennour TOUMI

26

这更清洁

Math.random().toString(36).substr(2, length)

例子

Math.random().toString(36).substr(2, 5)

5
长度大于16时似乎无法正常工作。 - thailey01
1
但你可以像这样编写逻辑... console.log(Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)) + Math.random().toString(36).substring(2, 15)); :-) - Parth Raval

24

或者根据Jar Jar的建议进行改进,这是我在最近的一个项目中使用的内容(用于克服长度限制):

var randomString = function (len, bits)
{
    bits = bits || 36;
    var outStr = "", newStr;
    while (outStr.length < len)
    {
        newStr = Math.random().toString(bits).slice(2);
        outStr += newStr.slice(0, Math.min(newStr.length, (len - outStr.length)));
    }
    return outStr.toUpperCase();
};

使用:

randomString(12, 16); // 12 hexadecimal characters
randomString(200); // 200 alphanumeric characters

23
function randomString(len) {
    var p = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    return [...Array(len)].reduce(a=>a+p[~~(Math.random()*p.length)],'');
}

摘要:

  1. 创建所需大小的数组(因为JavaScript中没有range(len)的等效物)。
  2. 对于数组中的每个元素:从p中选择一个随机字符并将其添加到字符串中。
  3. 返回生成的字符串。

一些解释:

[...Array(len)]

Array(len)new Array(len)会创建一个带有未定义指针的数组。单行代码可能更难实现。展开语法方便地定义了这些指针(现在它们指向未定义的对象!)。

.reduce(

将数组减少到一个字符串,在这种情况下,是单个字符串。减少功能在大多数语言中都很常见,值得学习。

a=>a+...

我们使用箭头函数

a累加器。在这种情况下,它是最终结果字符串,我们完成后要返回它(你知道它是一个字符串,因为reduce函数的第二个参数,initialValue,是一个空字符串:'')。所以基本上:将数组中的每个元素转换为p[~~(Math.random()*p.length)],将结果附加到a字符串中,并在完成时给我a

p[...]

p是我们从中选择的字符的字符串。你可以像使用索引一样访问字符串中的字符(例如,"abcdefg"[3]给我们"d"

~~(Math.random()*p.length)

Math.random()返回[0,1)之间的浮点数。Math.floor(Math.random()*max) 是在JavaScript中获取随机整数的事实标准~是JavaScript中的位NOT运算符。~~是一种更短、更快(有时)且更有趣的方式来表示Math.floor(这里有一些信息


1
虽然这段代码可能回答了问题,但提供有关它如何以及/或为什么解决问题的附加上下文将改善答案的长期价值。 - Badacadabra

21

我认为以下是最简单的解决方案,可以允许一个给定的长度:

Array(myLength).fill(0).map(x => Math.random().toString(36).charAt(2)).join('')

这取决于箭头函数的语法。


9

对于32个字符:

for(var c = ''; c.length < 32;) c += Math.random().toString(36).substr(2, 1)

7

随机字符:

String.fromCharCode(i); //where is an int

随机整数:

Math.floor(Math.random()*100);

将所有内容汇总:

function randomNum(hi){
    return Math.floor(Math.random()*hi);
} 
function randomChar(){
    return String.fromCharCode(randomNum(100));
}
function randomString(length){
   var str = "";
   for(var i = 0; i < length; ++i){
        str += randomChar();
   }
   return str;
}
var RandomString = randomString(32); //32 length string

Fiddle: http://jsfiddle.net/maniator/QZ9J2/


这包括许多非字母数字字符。一个例子(第一次尝试得到的):M&I56aP=H K?<T*, ;0'9_c5Tb - sveti petar
我得到了]4INBKJ\\_5.(/"__X,,K")]`,但有很多字符是无法使用的,并被 SO 剥离了。 - RozzA
空字节(String.fromCharCode(0))在许多语言/数据库/环境中会导致各种问题。代码33-126产生可见的ASCII字符。 - JasonWoof

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