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

IT技术 javascript string random
2021-01-16 00:38:10

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

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

6个回答

我认为这对你有用:

function makeid(length) {
    var result           = '';
    var characters       = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    var charactersLength = characters.length;
    for ( var i = 0; i < length; i++ ) {
      result += characters.charAt(Math.floor(Math.random() * 
 charactersLength));
   }
   return result;
}

console.log(makeid(5));

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

//Can change 7 to 2 for longer results.
let r = (Math.random() + 1).toString(36).substring(7);
console.log("random", r);

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

  • 由于在对浮点进行字符串化时会删除尾随零这一事实,它将生成 0 到 6 个字符之间的任何位置。
  • 它在很大程度上取决于用于对浮点数进行字符串化的算法,该算法极其复杂。(请参阅论文“如何准确打印浮点数”。)
  • Math.random()可能会产生可预测的(“随机外观”但不是真正随机的)输出,具体取决于实现。当您需要保证唯一性或不可预测性时,结果字符串不适合。
  • 即使它生成了 6 个均匀随机的、不可预测的字符,由于生日悖论,您也可以在生成大约 50,000 个字符串后看到重复的字符(平方(36^6) = 46656)
看起来很漂亮,但在少数情况下会生成空字符串!如果 random 返回 0, 0.5, 0.25, 0.125... 将导致空字符串或短字符串。
2021-03-10 00:38:10
@hacklikecrack,出现重复是因为substring(7)从 base-36 字符串的最低有效部分中提取数字。toString(36)似乎将随机数转换为 16 位 base-36 字符串,但此操作所需的精度为 36^16 = 7.958e24 个可能的数字,其中 Math.random() 的精度仅为4.5e15从最重要的一端取数字可以.slice(2,5)解决这个问题:5 位数字示例
2021-03-17 00:38:10
@Scoop toStringjavascript中数字类型方法需要一个可选参数将数字转换为给定的基数。例如,如果您传递两个,您将看到您的数字以二进制表示。与十六进制(基数 16)类似,基数 36 使用字母来表示 9 以外的数字。通过将随机数转换为基数 36,您最终会得到一堆看似随机的字母和数字。
2021-03-22 00:38:10
Math.random().toString(36).substr(2, 5), 因为.substring(7)导致它超过 5 个字符。满分,还是!
2021-04-01 00:38:10
@gertas 这可以通过以下方式避免 (Math.random() + 1).toString(36).substring(7);
2021-04-02 00:38:10

Math.random不适合这种事情

选项1

如果您能够在服务器执行此操作,只需使用加密module -

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

// "bb5dc8842ca31d4603d6aa11448d1654"

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


选项 2

如果您必须执行此客户端,请尝试使用 uuid module -

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

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

选项 3

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

// dec2hex :: Integer -> String
// i.e. 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

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

简短、简单、可靠

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

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

这是对doubletap 的出色回答的改进原版有两个缺点,在这里解决:

首先,正如其他人提到的,它产生短字符串甚至空字符串(如果随机数为 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)

解释:

  1. 在 [0,1) 范围内选择一个随机数,即介于 0(含)和 1(不含)之间。
  2. 将数字转换为 base-36 字符串,即使用字符 0-9 和 az。
  3. 用零填充(解决第一个问题)。
  4. 切掉前导“0”。前缀和额外的填充零。
  5. 重复该字符串足够多的次数以使其中至少包含 N 个字符(通过将空字符串与用作分隔符的较短随机字符串连接起来)。
  6. 从字符串中切出 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 不能解决这里讨论的两个问题中的任何一个。例如, (1).toString(36).substring(7) 产生一个空字符串。
2021-03-22 00:38:10
为什么不加1? (Math.random()+1).toString(36).substring(7);
2021-03-23 00:38:10
Array.apply(null, {length: 5}).map(function() { return s.charAt(Math.floor(Math.random() * s.length)); }).join('')
2021-03-23 00:38:10
这很好,但是当在 N=16 的 Firefox 中执行时,最后约 6 位数字最终为零......(但它确实满足了 OP 对 5 个随机字符的需求。)
2021-04-05 00:38:10
使用Math.random().toString(36).substring(2,7)给出了更像的预期结果.substring(2, n+2)
2021-04-07 00:38:10