ChatGPT解决这个技术问题 Extra ChatGPT

在 JavaScript 中生成随机字符串/字符

我想要一个由从集合 [a-zA-Z0-9] 中随机挑选的字符组成的 5 个字符的字符串。

使用 JavaScript 执行此操作的最佳方法是什么?

警告:没有一个答案有 true-random 结果!它们只有 pseudo-random。当使用随机字符串进行保护或安全时,不要使用其中任何一个!!!尝试以下 api 之一:random.org
Math.random().toString(36).replace(/[^az]+/g, '')
请把解决方案放在解决方案中。
Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5);
注意 HTML5 webcrypto randomness API 提供了真正的随机性。

S
Sergey

我认为这对你有用:

函数 makeid(length) { var 结果 = ''; var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; var charactersLength = characters.length; for ( var i = 0; i < length; i++ ) { 结果 += characters.charAt(Math.floor(Math.random() * charactersLength)); } 返回结果; } console.log(makeid(5));


@dan_waterworth 在几乎任何的情况下这可能都无关紧要:codinghorror.com/blog/2009/01/…
它可能看起来不对,但不需要 floor:for(var text=''; text.length < 5;) text += possible.charAt(Math.random() * possible.length)
@dan_waterworth,实际上,出于某种原因,+= 通常更快,甚至在循环内部使用 - jsperf.com/join-vs-concatenation
@JonathanPaulson 给我看数字。请参阅带有 jsperf 链接或 jsperf.com/sad-tragedy-of-microoptimizationsitepen.com/blog/2008/05/09/string-performance-an-analysis 等的先前评论。此外,此问题仅涉及 5 个连接。
@codenamejames 请不要在您的密码加盐中使用它。它只是伪随机不安全。假设您使用的是 Node(如果您在客户端加盐,我什至不确定从哪里开始),请改用 crypto
A
Andrew

//可以将 7 更改为 2 以获得更长的结果。让 r = (Math.random() + 1).toString(36).substring(7); console.log("随机", r);

注意:上述算法有以下弱点:

由于在对浮点进行字符串化时会删除尾随零,因此它将生成 0 到 6 个字符之间的任何字符。

它在很大程度上取决于用于对浮点数进行字符串化的算法,这非常复杂。 (请参阅论文“如何准确打印浮点数”。)

Math.random() 可能会根据实现产生可预测的(“随机的”但不是真正随机的)输出。当您需要保证唯一性或不可预测性时,生成的字符串不适合。

即使它生成了 6 个均匀随机、不可预测的字符,由于生日悖论,您可能会在仅生成大约 50,000 个字符串后看到重复的字符。 (sqrt(36^6) = 46656)


Math.random().toString(36).substr(2, 5),因为 .substring(7) 导致它超过 5 个字符。满分,还是!
@Scoop JavaScript 中数字类型的 toString 方法采用可选参数将数字转换为给定的基数。例如,如果您传递两个,您将看到以二进制表示的数字。类似于十六进制(以 16 为底),以 36 为底使用字母来表示 9 以外的数字。通过将随机数转换为以 36 为底,您将得到一堆看似随机的字母和数字。
看起来很漂亮,但在少数情况下会生成空字符串!如果 random 返回 0, 0.5, 0.25, 0.125... 将导致空字符串或短字符串。
@gertas 这可以通过(Math.random() + 1).toString(36).substring(7);来避免
@hacklikecrack,重复发生是因为 substring(7) 从 base-36 字符串的最低有效部分获取数字。 toString(36) 似乎将随机数转换为 16 位 base-36 字符串,但所需的精度为 36^16 = 7.958e24 个可能的数字,其中 Math.random() 的精度仅为 4.5e15。用 .slice(2,5) 从最重要的一端取数字可以解决这个问题:5 digit example
M
Mulan

Math.random is bad for this kind of thing

选项1

如果您能够做到这一点 server 端,只需使用 crypto 模块 -

var crypto = require("crypto");
var id = crypto.randomBytes(20).toString('hex');

// "bb5dc8842ca31d4603d6aa11448d1654"

结果字符串将是您生成的随机字节的两倍;编码为十六进制的每个字节是 2 个字符。 20 个字节将是 40 个十六进制字符。

选项 2

如果您必须在客户端执行此操作,也许可以尝试 uuid 模块 -

var uuid = require("uuid");
var id = uuid.v4();

// "110ec58a-a0f2-4ac4-8393-c866d813b8d1"

选项 3

如果您必须在客户端执行此操作并且您不必支持旧浏览器,则可以在没有依赖关系的情况下执行此操作 -

// dec2hex :: Integer -> String // 即 0-255 -> '00'-'ff' function dec2hex (dec) { return dec.toString(16).padStart(2, "0") } // generateId :: Integer -> String function generateId (len) { var arr = new Uint8Array((len || 40) / 2) window.crypto.getRandomValues(arr) return Array.from(arr, dec2hex).join('') } console.log(generateId()) // "82defcf324571e70b0521d79cce2bf3fffccd69" console.log(generateId(20)) // "c1a050a4cd1556948d41"

有关 crypto.getRandomValues 的更多信息 -

crypto.getRandomValues() 方法可让您获得加密的强随机值。作为参数给出的数组填充了随机数(在其密码学意义上是随机的)。

这是一个小控制台示例 -

> var arr = new Uint8Array(4) # make array of 4 bytes (values 0-255)
> arr
Uint8Array(4) [ 0, 0, 0, 0 ]

> window.crypto
Crypto { subtle: SubtleCrypto }

> window.crypto.getRandomValues()
TypeError: Crypto.getRandomValues requires at least 1 argument, but only 0 were passed

> window.crypto.getRandomValues(arr)
Uint8Array(4) [ 235, 229, 94, 228 ]

对于 IE11 支持,您可以使用 -

(window.crypto || window.msCrypto).getRandomValues(arr)

有关浏览器覆盖范围,请参阅 https://caniuse.com/#feat=getrandomvalues


确切地。虽然 UUID 可以很好地为事物分配 ID,但出于这个(可能还有其他)原因,将其用作随机字符串并不是一个好主意。
选项 3 中不需要 .map()Array.from(arr, dec2hex).join('') === Array.from(arr).map(dec2hex).join('')。感谢您向我介绍这些功能 :-)
您应该在答案中提到选项 2 也需要 node.js 才能工作,它不是纯 JavaScript。
虽然在密码学上更安全,但这实际上并不能满足问题的要求,因为它只输出 0-9 和 af(十六进制),而不是 0-9、az、AZ。
@AneesAhmed777 为 dec2hex 提供了一个示例编码器。无论您选择如何表示字节,都由您决定。我用你的建议更新了帖子。
R
Robin van Baalen

简短、简单、可靠

正好返回 5 个随机字符,而不是此处找到的一些评分最高的答案。

Math.random().toString(36).slice(2, 7);

如果 Math.random().toString(36) 返回一个少于 5 个字符的数字怎么办?
嗯,这是来自@Aperçu 的一个有趣的控诉,我并不是说我发明了这个解决方案,但多年来我一直在我的项目中使用它。这与您提到的评论无关。而且我很确定,在 SO 中,最重要的是在正确的地方提供最有用的信息,即使它已经存在于其他地方也是如此。幸运的是,似乎至少有 51 人发现这个答案很有用,不客气!
@rinogo Math.random() 可以返回 0 但不能返回 1 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
我运行了这段代码 1,000,000,000 次,但仍然没有得到一个空字符串:jsfiddle.net/mtp5730r 我会说你得到一个空字符串是很安全的。
统计上不太可能发生的事件并不意味着它是安全的。如果有人将其用于任何与安全相关的活动,他们就是在赌 Math.random 不返回 0 的可能性。希望您永远不必调试此事件
C
Community

这是对 doubletap's excellent answer 的改进。原版有两个缺点,这里要解决:

首先,正如其他人所提到的,它产生短字符串甚至是空字符串(如果随机数为0)的可能性很小,这可能会破坏您的应用程序。这是一个解决方案:

(Math.random().toString(36)+'00000000000000000').slice(2, N+2)

其次,原始和上述解决方案都将字符串大小 N 限制为 16 个字符。以下将为任何 N 返回一个大小为 N 的字符串(但请注意,使用 N > 16 不会增加随机性或降低碰撞概率):

Array(N+1).join((Math.random().toString(36)+'00000000000000000').slice(2, 18)).slice(0, N)

解释:

在 [0,1) 范围内选择一个随机数,即介于 0(包括)和 1(不包括)之间。将数字转换为 base-36 字符串,即使用字符 0-9 和 az。用零填充(解决第一个问题)。切掉前面的“0”。前缀和额外的填充零。将字符串重复足够多的时间以使其中至少包含 N 个字符(通过将空字符串与用作分隔符的较短随机字符串连接起来)。从字符串中精确切出 N 个字符。

进一步的想法:

此解决方案不使用大写字母,但在几乎所有情况下(没有双关语)都没有关系。

原始答案中 N = 16 处的最大字符串长度是在 Chrome 中测量的。在 Firefox 中,它是 N = 11。但正如所解释的,第二个解决方案是关于支持任何请求的字符串长度,而不是关于添加随机性,所以它没有太大的区别。

所有返回的字符串都具有相同的返回概率,至少在 Math.random() 返回的结果是均匀分布的范围内(无论如何,这不是加密强度的随机性)。

并非所有可能的大小为 N 的字符串都可以返回。在第二种解决方案中,这很明显(因为只是复制了较小的字符串),但在原始答案中也是如此,因为在转换为 base-36 时,最后几位可能不是原始随机位的一部分。具体来说,如果您查看 Math.random().toString(36) 的结果,您会注意到最后一个字符不是均匀分布的。同样,在几乎所有情况下都没有关系,但是我们从随机字符串的开头而不是结尾对最终字符串进行切片,这样短字符串(例如 N=1)就不会受到影响。

更新:

这是我想出的其他几个函数式单行代码。它们与上述解决方案的不同之处在于:

他们使用明确的任意字母(更通用,并且适合要求大写和小写字母的原始问题)。

所有长度为 N 的字符串都具有相同的返回概率(即字符串不包含重复)。

它们基于 map 函数,而不是 toString(36) 技巧,这使得它们更简单易懂。

所以,假设你选择的字母表是

var s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

那么这两个是等价的,所以你可以选择对你来说更直观的一个:

Array(N).join().split(',').map(function() { return s.charAt(Math.floor(Math.random() * s.length)); }).join('');

Array.apply(null, Array(N)).map(function() { return s.charAt(Math.floor(Math.random() * s.length)); }).join('');

编辑:

我似乎 qubyteMartijn de Milliano 提出了与后者类似的解决方案(赞!),我不知何故错过了。因为它们一目了然,所以我还是把它留在这里,以防有人真的想要单线:-)

此外,在所有解决方案中将 'new Array' 替换为 'Array' 以减少更多字节。


为什么不加1? (Math.random()+1).toString(36).substring(7);
因为加 1 并不能解决这里讨论的两个问题。例如,(1).toString(36).substring(7) 产生一个空字符串。
使用 Math.random().toString(36).substring(2,7) 会产生一个更像 .substring(2, n+2) 的预期结果
Array.apply(null, {length: 5}).map(function() { return s.charAt(Math.floor(Math.random() * s.length)); }).join('')
这很棒,但是当在 N=16 的 Firefox 中执行时,最后约 6 位数字最终为零……(但它确实满足了 OP 对 5 个随机字符的需求。)
V
Valentin Podkamennyi

最紧凑的解决方案,因为 slicesubstring 短。从字符串的末尾减去可以避免 random 函数生成的浮点符号:

Math.random().toString(36).slice(-5);

甚至

(+new Date).toString(36).slice(-5);

更新: 使用 btoa 方法添加了另一种方法:

btoa(Math.random()).slice(0, 5);
btoa(+new Date).slice(-7, -2);
btoa(+new Date).substr(-7, 5);

// 使用 Math.random 和 Base 36:console.log(Math.random().toString(36).slice(-5)); // 使用新日期和基数 36:console.log((+new Date).toString(36).slice(-5)); // 使用 Math.random 和 Base 64 (btoa): console.log(btoa(Math.random()).slice(0, 5)); // 使用 new Date 和 Base 64 (btoa): console.log(btoa(+new Date).slice(-7, -2)); console.log(btoa(+new Date).substr(-7, 5));


Math.random().toString(36).slice(-5); - 如果 Math.random() 返回 0.0 怎么办?
@x-ray,你会得到 "0" ;)
确切地。 ;) 如果 Math.random() 返回 0.5,则结果为 "0.i"。不确定是否还有其他边缘情况。只是想指出这不是问题的正确答案(来自 [a-zA-Z0-9] 的 5 个字符)。
@x-ray,我不会说这是正确的答案,我只是说这是上面@doubletap 答案的紧凑版本。我个人使用 (+new Date + Math.random()) 来防止这种情况。无论如何,谢谢你的注意。
为什么根本不使用 bota?我评论了其他答案,但使用这个测试用例你可以看到你将只使用 base64 必须提供的 64 个字符中的 14 个:[...new Set([...Array(100000)].map(()=>btoa(Math.random()).substr(0, 5)).join(""))].sort()
O
Or Duan

es6 spread operator 的较新版本:

[...Array(30)].map(() => Math.random().toString(36)[2]).join('')

30 是一个任意数字,你可以选择任何你想要的令牌长度

36 是您可以传递给 numeric.toString() 的最大基数,这意味着所有数字和 az 小写字母

用于从如下所示的随机字符串中选择第三个索引:“0.mfbiohx64i”,我们可以取 0 之后的任何索引。


你能解释一下吗?尤其是为什么将 36 传递给 toString() 以及为什么选择第三个元素?
很好,但这个解决方案不包括 [AZ] 字符作为问题要求,只有 [a-z0-9]
@NahuelGreco 你可以做 [...Array(30)].map(() => Math.random().toString(36)[2]).join('').toUpperCase()
@tam.teixeira 我认为不,原始问题要求混合大写,每个字符必须从 [a-zA-Z0-9] 集中随机选择。这个答案不能满足这一点。
Array(30).fill().map(() => Math.random().toString(36).slice(2)).join('') 生成一个巨大的随机字符串
C
CaffGeek

像这样的东西应该工作

function randomString(len, charSet) {
    charSet = charSet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    var randomString = '';
    for (var i = 0; i < len; i++) {
        var randomPoz = Math.floor(Math.random() * charSet.length);
        randomString += charSet.substring(randomPoz,randomPoz+1);
    }
    return randomString;
}

使用默认字符集 [a-zA-Z0-9] 调用或发送您自己的:

var randomValue = randomString(5);

var randomValue = randomString(5, 'PICKCHARSFROMTHISSET');

可以在此处找到示例的变体:mediacollege.com/internet/javascript/number/random.html
还不如直接在 while 循环中减少 len
谢谢,我刚刚在下面发布了这个示例的咖啡脚本变体:stackoverflow.com/a/26682781/262379
如果这些要公开使用,那么您可能应该删除元音。熵少一点,但更安全,因为你不能生成任何可能冒犯人们的词。这就是我喜欢这个的原因,因为我可以发送自己的字符串。好工作。
请注意,在 Node 服务器上,它很简单:crypto.randomBytes(3).toString('hex')(例如,3 个字节给出 6 个字符)
P
Priya

函数随机字符串(L){ var s = ''; var randomchar = function() { var n = Math.floor(Math.random() * 62);如果 (n < 10) 返回 n; //1-10 if (n < 36) return String.fromCharCode(n + 55); //AZ return String.fromCharCode(n + 61); //az } while (s.length < L) s += randomchar();返回 s; } 控制台日志(随机字符串(5));


我最喜欢这个。您可以轻松修改以接受额外参数并仅返回数字、小写字母或大写字母。我认为不需要超压缩样式while(s.length< L) s+= randomchar();
while(L--) 也会这样做
最好使用更健壮的 'A'.charCodeAt(0) 而不是幻数 55(同样适用于 61)。特别是因为无论如何在我的平台上,返回的幻数是 65。该代码也将更好地自我记录。
我在这里为您的实现创建了一个缩小优化(小 46 个字节)的变体:stackoverflow.com/a/52412162
R
Roko C. Buljan

随机字符串生成器(字母数字 | 字母 | 数字)

/** * 伪随机字符串生成器 * http://stackoverflow.com/a/27872144/383904 * 默认值:返回随机字母数字字符串 * * @param {Integer} len 所需长度 * @param {String} an可选(字母数字),“a”(字母),“n”(数字) * @return {String} */ function randomString(len, an) { an = an && an.toLowerCase(); var str = "", i = 0, min = an == "a" ? 10:0,最大 = 一个 ==“n”? 10:62; for (; i++ < len;) { var r = Math.random() * (max - min) + min << 0; str += String.fromCharCode(r += r > 9 ? r < 36 ? 55 : 61 : 48); } 返回字符串; } console.log(randomString(10)); // ie: "4Z8iNQag9v" console.log(randomString(10, "a")); // ie: "aUkZuHNcWw" console.log(randomString(10, "n")); // 即:“9055739230”

虽然上面对所需的 A/N、A、N 输出使用了额外的检查,但让我们将其分解为基本要素(仅限字母数字)以便更好地理解:

创建一个接受参数的函数(随机字符串结果的所需长度)

创建一个空字符串,如 var str = "";连接随机字符

在循环内创建一个从 0 到 61 (0..9+A..Z+a..z = 62) 的 rand 索引号

创建一个条件逻辑来调整/修复 rand(因为它是 0..61),将其增加一些数字(参见下面的示例)以取回正确的 CharCode 数字和相关的字符。

在循环内连接到 str a String.fromCharCode( incremented rand )

让我们想象一下 ASCII 字符表的范围:

_____0....9______A..........Z______a..........z___________  Character
     | 10 |      |    26    |      |    26    |             Tot = 62 characters
    48....57    65..........90    97..........122           CharCode ranges

Math.floor( Math.random * 62 ) 给出了从 0..61 开始的范围(我们需要的)。
让我们修复随机数以获得正确的 charCode 范围

      |   rand   | charCode |  (0..61)rand += fix            = charCode ranges |
------+----------+----------+--------------------------------+-----------------+
0..9  |   0..9   |  48..57  |  rand += 48                    =     48..57      |
A..Z  |  10..35  |  65..90  |  rand += 55 /*  90-35 = 55 */  =     65..90      |
a..z  |  36..61  |  97..122 |  rand += 61 /* 122-61 = 61 */  =     97..122     |

上表中的 conditional operation 逻辑

   rand += rand>9 ? ( rand<36 ? 55 : 61 ) : 48 ;
// rand +=  true  ? (  true   ? 55 else 61 ) else 48 ;

根据上面的解释,这是生成的字母数字片段:

函数 randomString(len) { var str = ""; // String result for (var i = 0; i < len; i++) { // 循环 `len` 次 var rand = Math.floor(Math.random() * 62); // 随机:0..61 var charCode = rand += rand > 9 ? (兰特 < 36 ? 55 : 61) : 48; // 获取正确的 charCode str += String.fromCharCode(charCode); // 将字符添加到 str } return str; // 所有循环完成后,返回连接的字符串 } console.log(randomString(10)); // 即:“7GL9F0ne6t”

或者,如果您愿意:

const randomString = (n, r='') => { while (n--) r += String.fromCharCode((r=Math.random()*62|0, r+=r>9?(r<36 ?55:61):48));返回 r; };控制台日志(随机字符串(10))


由于我无法确定的原因,当我比较具有不同 ES 版本的两个应用程序的输出时,x.toString(36) --- 如上述答案中所使用的 --- 并没有可靠地生成相同的字符,并且这个函数为我解决了这个问题,因为它不使用 .toString()
S
Sergio Cabral

为了满足要求 [a-zA-Z0-9] 和 length=5 使用

对于浏览器:

btoa(Math.random().toString()).substr(10, 5);

对于 NodeJS:

Buffer.from(Math.random().toString()).toString("base64").substr(10, 5);

会出现小写字母、大写字母和数字。

(它与打字稿兼容)


我猜, Math.random() 可能会导致 base64 字符串的字符太少。
添加@Leif 评论,这只会生成 1-5 之间的数字,并且只有 52 个字母中的 19 个!(大写和小写)要测试,只需运行多次,Set 看看独特的字符,然后排序。一个班轮:[...new Set([...Array(100000)].map(()=>btoa(Math.random()).substr(5, 5)).join(""))].sort()
为什么我们在浏览器示例中需要 toString
你是对的。不需要 toString()。但是因为我想做一些“它与打字稿兼容”。所以我就这样离开了。
Math.random().toString() 将只使用数字 0-9,这不是随机的。你不能只是 base64 它并期望它是随机的。您需要随机种子数据来创建随机输出。因此,此答案无法回答 OPs 问题。
M
MasqueradeCircus

最简单的方法是:

(new Date%9e6).toString(36)

这会根据当前时间生成 5 个字符的随机字符串。示例输出为 4mtxj4mv904mwp1

这样做的问题是,如果您在同一秒内调用它两次,它将生成相同的字符串。

更安全的方法是:

(0|Math.random()*9e6).toString(36)

这将生成一个 4 或 5 个字符的随机字符串,总是不同的。示例输出类似于 30jzm1r5914su1a

在这两种方式中,第一部分都会生成一个随机数。 .toString(36) 部分将数字转换为它的 base36(字母十进制)表示。


我不太确定这是如何回答这个问题的;这是一个 7 岁的问题,已经有许多有效的答案。如果您选择提供新答案,您应该格外小心,以确保您的答案得到很好的解释和记录。
如果您使用日期,为什么不直接使用它:(+new Date).toString(36)
我喜欢您的随机数解决方案,但 9e6 仅提供 900 万种可能性,而不是 5 位数 (36^5) 的 6040 万种可能性,因此您可以用 (0|Math.random()*6.04e7).toString(36) 替换它来覆盖它。
我想要一个较长的随机字符串和一个尽可能短的例程(用于代码演示),不需要在密码学上令人惊叹,只需产生一些漂亮的随机视觉“绒毛”。我最喜欢这个答案(你的第二个),所以谢谢。我的两分钱:我可以用更少的击键击败它,它通常会产生 13 个随机字符(没有句点):(Math.random()*1e20).toString(36)
我对这个答案的一个疑虑是它不会使用恰好在原始问题中的 [AZ]。
b
bendytree

这里有一些简单的衬线。更改 new Array(5) 以设置长度。

包括0-9a-z

new Array(5).join().replace(/(.|$)/g, function(){return ((Math.random()*36)|0).toString(36);})

包括0-9a-zA-Z

new Array(5).join().replace(/(.|$)/g, function(){return ((Math.random()*36)|0).toString(36)[Math.random()<.5?"toString":"toUpperCase"]();});

P
Priya

我知道每个人都已经做对了,但我想以最轻量级的方式尝试这个(代码上的灯光,而不是 CPU):

函数 rand(length, current) { current = current ?当前的 : '';返回长度? rand(--length, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz".charAt(Math.floor(Math.random() * 60)) + current) : 当前; } console.log(rand(5));

这需要一些时间来理解,但我认为它确实显示了 javascript 的语法是多么的棒。


如果您想保持代码简短,为什么在可以写 current = current || '' 时还要写 current = current ? current : '';
因为我们在这里讨论的是微优化,所以我实际上建议在没有必要的情况下完全跳过变量赋值:current || (current = '');
在使用“不错”但浪费资源的递归算法时尝试微优化是没有意义的:-) for 循环在 Javascript 中仍然是最好的,并且在很大程度上击败了递归,直到在运行时的优化代码中取得了真正的重大进步。
g
gogo

生成一个安全的随机字母数字 Base-62 字符串:

函数 generateUID(length) { return window.btoa(Array.from(window.crypto.getRandomValues(new Uint8Array(length * 2))).map((b) => String.fromCharCode(b)).join("" )).replace(/[+/]/g, "").substring(0, length); } console.log(generateUID(22)); // "yFg3Upv2cE9cKOXd7hHwWp" console.log(generateUID(5)); // "YQGzP"


C
Community

如果有人对一次性分配内存(但请注意,对于小字符串确实无关紧要)的单行器(尽管为了您的方便没有格式化)感兴趣,这里是如何做到的:

Array.apply(0, Array(5)).map(function() {
    return (function(charset){
        return charset.charAt(Math.floor(Math.random() * charset.length))
    }('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'));
}).join('')

您可以将 5 替换为所需字符串的长度。感谢 this post 中的 @AriyaHidayat 解决了 map 函数不适用于 Array(5) 创建的稀疏数组的解决方案。


如果您将其格式化,则每个 javascript 程序都是“单行”
v
vineet

如果您使用的是 LodashUnderscore,那么它就这么简单:

var randomVal = _.sample('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', 5).join('');

Lodash 使用 _.sampleSize('asdfgh',5).join('')
这实际上不是一个好的解决方案,因为每个文档的每个字符都来自一个唯一的索引。这意味着它不是真正随机的,因为没有角色可以/永远不会重复。
f
frodeborli

没有最好的方法来做到这一点。只要结果符合您的要求,您就可以按照您喜欢的任何方式进行操作。为了说明,我创建了许多不同的示例,所有这些示例都应该提供相同的最终结果

此页面上的大多数其他答案都忽略了大写字符的要求。

这是我最快的解决方案,也是最易读的。它基本上与公认的解决方案相同,只是速度更快一些。

function readableRandomStringMaker(length) {
  for (var s=''; s.length < length; s += 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'.charAt(Math.random()*62|0));
  return s;
}
console.log(readableRandomStringMaker(length));
// e3cbN

这是一个紧凑的递归版本,可读性要差得多:

const compactRandomStringMaker = (length) => length-- && "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".charAt(Math.random()*62|0) + (compactRandomStringMaker(length)||"");
console.log(compactRandomStringMaker(5));
// DVudj

更紧凑的单线:

Array(5).fill().map(()=>"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".charAt(Math.random()*62)).join("")
// 12oEZ

上述变体:

"     ".replaceAll(" ",()=>"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".charAt(Math.random()*62))

最紧凑的单行代码,但效率低且不可读 - 它添加随机字符并删除非法字符,直到长度为 l:

((l,f=(p='')=>p.length<l?f(p+String.fromCharCode(Math.random()*123).replace(/[^a-z0-9]/i,'')):p)=>f())(5)

一个加密安全的版本,为了紧凑而浪费熵,而且不管因为生成的字符串太短,都是一种浪费:

[...crypto.getRandomValues(new Uint8Array(999))].map((c)=>String.fromCharCode(c).replace(/[^a-z0-9]/i,'')).join("").substr(0,5)
// 8fzPq

或者,没有长度参数,它甚至更短:

((f=(p='')=>p.length<5?f(p+String.fromCharCode(Math.random()*123).replace(/[^a-z0-9]/i,'')):p)=>f())()
// EV6c9

然后更具挑战性 - 使用无名递归箭头函数:

((l,s=((l)=>l--&&"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".charAt(Math.random()*62|0)+(s(l)||""))) => s(l))(5);
// qzal4

这是一个“魔法”变量,每次访问它时都会提供一个随机字符:

const c = new class { [Symbol.toPrimitive]() { return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".charAt(Math.random()*62|0) } };
console.log(c+c+c+c+c);
// AgMnz

上面的一个更简单的变体:

const c=()=>"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".charAt(Math.random()*62|0);
c()+c()+c()+c()+c();
// 6Qadw

N
Nahuel Greco
const c = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
const s = [...Array(5)].map(_ => c[~~(Math.random()*c.length)]).join('')

A
Adam

这是我创建的方法。它将创建一个包含大写和小写字符的字符串。此外,我还包含了将创建一个字母数字字符串的函数。

工作示例:
http://jsfiddle.net/greatbigmassive/vhsxs/(仅限字母)
http://jsfiddle.net/greatbigmassive/PJwg8/(字母数字)

function randString(x){
    var s = "";
    while(s.length<x&&x>0){
        var r = Math.random();
        s+= String.fromCharCode(Math.floor(r*26) + (r>0.5?97:65));
    }
    return s;
}

2015 年 7 月升级 这做同样的事情,但更有意义,包括所有字母。

var s = "";
while(s.length<x&&x>0){
    v = Math.random()<0.5?32:0;
    s += String.fromCharCode(Math.round(Math.random()*((122-v)-(97-v))+(97-v)));
}

使用此函数,您永远无法获得大于“M”的大写字母或小于“n”的小写字母。
真的吗?哦!届时可能会对其进行更多测试。嗯,但是,鉴于它是一个随机字符串,我们并不真正关心其中的字母(只要它们是随机的),那么它仍然会做我们想要它做的事情,这很重要,但是是的,将提供一个升级,谢谢。
Z
Zee

改进了@Andrew 上面的回答:

Array.from({ length : 1 }, () => Math.random().toString(36)[2]).join('');

随机数的基数 36 转换不一致,因此选择单个索引可以解决该问题。您可以将字符串的长度更改为所需的确切长度。


t
tiktak

假设您使用 underscorejs ,只需两行就可以优雅地生成随机字符串:

var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var random = _.sample(possible, 5).join('');

这将使返回的值具有所有唯一字符。此外,字符串的长度被限制为 var 可能的长度。
i
iamarkadyt

一个班轮:

Array(15).fill(null).map(() => Math.random().toString(36).substr(2)).join('')
// Outputs: 0h61cbpw96y83qtnunwme5lxk1i70a6o5r5lckfcyh1dl9fffydcfxddd69ada9tu9jvqdx864xj1ul3wtfztmh2oz2vs3mv6ej0fe58ho1cftkjcuyl2lfkmxlwua83ibotxqc4guyuvrvtf60naob26t6swzpil

为了使其更短,将 Array(15) 中的参数更改为更小的值。例如:Array(4)
我真的很喜欢这个解决方案,简单明了。它正在生成 0 和 1 之间的 15 个随机数,生成十六进制,然后删除前两个字符。最后,它将所有这些合并在一起。
错误的解决方案,不会生成大写字符。
y
yaroslav

快速和改进的算法。不保证统一(见评论)。

function getRandomId(length) {
    if (!length) {
        return '';
    }

    const possible =
        'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    let array;

    if ('Uint8Array' in self && 'crypto' in self && length <= 65536) {
        array = new Uint8Array(length);
        self.crypto.getRandomValues(array);
    } else {
        array = new Array(length);

        for (let i = 0; i < length; i++) {
            array[i] = Math.floor(Math.random() * 62);
        }
    }

    let result = '';

    for (let i = 0; i < length; i++) {
        result += possible.charAt(array[i] % 62);
    }

    return result;
}

很好的答案,但概率并不统一。有 62 个可能的字符,crypto.getRandomValues 返回 256 个唯一值之一。因为 256 没有除以 62,所以你最终获得字符 AH 的概率略高。我认为最好的解决方案是做 YouTube 所做的,只需在字符集中添加 2 个额外的字符(可能是 -_)。无论如何,干得好——这个答案需要更多的爱:)
添加 - 和 _ 是个好主意,因为它将为您提供完整的 url 安全 base64 集
q
qubyte

这个紧凑的小技巧怎么样?

var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var stringLength = 5;

function pickRandom() {
    return possible[Math.floor(Math.random() * possible.length)];
}

var randomString = Array.apply(null, Array(stringLength)).map(pickRandom).join('');

您需要那里的 Array.apply 来欺骗空数组成为未定义数组。

如果您正在为 ES2015 编写代码,那么构建数组会更简单一些:

var randomString = Array.from({ length: stringLength }, pickRandom).join('');

P
Priya

您可以遍历一组项目并递归地将它们添加到字符串变量中,例如,如果您想要一个随机 DNA 序列:

函数 randomDNA(len) { len = len || 100 var nuc = new Array("A", "T", "C", "G") var i = 0 var n = 0 s = '' while (i <= len - 1) { n = Math.floor (Math.random() * 4) s += nuc[n] i++ } return s } console.log(randomDNA(5));


A
Ali

不区分大小写的字母数字字符:

函数 randStr(len) { 让 s = ''; while (s.length < len) s += Math.random().toString(36).substr(2, len - s.length);返回 s; } // 用法 console.log(randStr(50));

这个函数的好处是可以得到不同长度的随机字符串,并且保证了字符串的长度。

区分大小写的所有字符:

函数 randStr(len) { 让 s = ''; while (len--) s += String.fromCodePoint(Math.floor(Math.random() * (126 - 33) + 33));返回 s; } // 用法 console.log(randStr(50));

自定义字符

函数 randStr(len, chars='abc123') { 让 s = ''; while (len--) s += chars[Math.floor(Math.random() * chars.length)];返回 s; } // 用法 console.log(randStr(50)); console.log(randStr(50, 'abc')); console.log(randStr(50, 'aab')); // a 大于 b


这个答案更适合我的用例。如果您可以在接受的答案中添加类似 var possible 会很酷,因此该函数的结果更具可配置性。
F
Fattie
function randomString (strLength, charSet) {
    var result = [];
    
    strLength = strLength || 5;
    charSet = charSet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    
    while (strLength--) { // (note, fixed typo)
        result.push(charSet.charAt(Math.floor(Math.random() * charSet.length)));
    }
    
    return result.join('');
}

这是尽可能干净的。它也很快,http://jsperf.com/ay-random-string


对我来说,这总是会生成带有 strLength - 1 的随机字符串:-/
--strLength 切换到 strLength-- 为我解决了这个问题。
@Gajus 我冒昧地修复了明显的错字。它是 strLength-- 后缀,而不是前缀。
d
dingo sky

回答“我需要随机字符串”问题(无论使用何种语言)的问题实际上是每个解决方案都使用有缺陷的字符串长度主要规范。这些问题本身很少揭示为什么需要随机字符串,但我会挑战你很少需要长度的随机字符串,比如 8。你总是需要一些唯一的字符串,例如,用作某些目的的标识符。

获取严格唯一字符串的主要方法有两种:确定性(非随机)和存储/比较(繁重)。我们做什么?我们放弃了幽灵。我们使用概率唯一性。也就是说,我们接受存在一些(尽管很小)我们的字符串不是唯一的风险。这就是理解 collision probabilityentropy 有帮助的地方。

因此,我将把不变的需求重新表述为需要一些重复风险很小的字符串。举个具体的例子,假设您想生成 500 万个 ID 的潜力。您不想存储和比较每个新字符串,并且希望它们是随机的,因此您接受一些重复的风险。例如,假设重复的风险小于万亿分之一。那么你需要多长的字符串呢?好吧,这个问题没有详细说明,因为它取决于所使用的字符。但更重要的是,它被误导了。您需要的是字符串熵的规范,而不是它们的长度。熵可以直接与一些字符串中重复的概率相关。字符串长度不能。

这就是像 EntropyString 这样的库可以提供帮助的地方。要使用 entropy-string 生成在 500 万个字符串中重复概率小于万亿分之一的随机 ID:

import {Random, Entropy} from 'entropy-string'

const random = new Random()
const bits = Entropy.bits(5e6, 1e12)

const string = random.string(bits)

“44hTNghjNHGGRHqH9”

entropy-string 默认使用 32 个字符的字符集。还有其他预定义的字符集,您也可以指定自己的字符。例如,生成具有与上述相同熵但使用十六进制字符的 ID:

import {Random, Entropy, charSet16} from './entropy-string'

const random = new Random(charSet16)
const bits = Entropy.bits(5e6, 1e12)

const string = random.string(bits)

“27b33372ade513715481f”

请注意由于使用的字符集中字符总数的不同而导致的字符串长度的差异。在指定数量的潜在字符串中重复的风险是相同的。字符串长度不是。最重要的是,重复的风险和潜在的字符串数量是明确的。不再猜测字符串长度。


谢谢!您的评论让我意识到我不应该随机生成 ID。看起来 nanoidentropy-string 更受欢迎,并且似乎对我的目的同样适用。
K
Kamil Kiełczewski

加密强

如果您想获得符合您要求的 crypto-strong 字符串(我看到使用此但给出无效答案的答案)使用

let pass = n=> [...crypto.getRandomValues(new Uint8Array(n))]
   .map((x,i)=>(i=x/255*61|0,String.fromCharCode(i+(i>9?i>35?61:55:48)))).join``

let pass = n=> [...crypto.getRandomValues(new Uint8Array(n))] .map((x,i)=>(i=x/255*61|0,String.fromCharCode(i+(i> 9?i>35?61:55:48)))).join``console.log(pass(5));

更新:感谢 Zibri 评论,我更新了代码以获得任意长的密码