如何限制仅生成[0-9a-Z]个字符的随机字符串?

Ko3*_*2mo 0 elixir

我在Elixir中生成一个随机字符串,如下所示:

  len = 10
  val = :crypto.strong_rand_bytes(len)
          |> Base.url_encode64()
          |> binary_part(0, len)
Run Code Online (Sandbox Code Playgroud)

此代码的输出可以包含我不想要的连字符和下划线.将字母表限制为仅限[0-9a-Z]字符的方法是什么?

bit*_*ker 7

我会用:

defmodule Generator do
  @alphabet Enum.concat([?0..?9, ?A..?Z, ?a..?z])

  def randstring(count) do
    # Technically not needed, but just to illustrate we're
    # relying on the PRNG for this in random/1
    :rand.seed(:exsplus, :os.timestamp())
    Stream.repeatedly(&random_char_from_alphabet/0)
    |> Enum.take(count)
    |> List.to_string()
  end
  defp random_char_from_alphabet() do
    Enum.random(@alphabet)
  end
end

iex> Generator.randstring(8)
"ydKPsdwP"
Run Code Online (Sandbox Code Playgroud)

这将生成一个任意长度的字符串,仅包含[0-9A-Za-z],而不需要生成随机字节通过:crypto和过滤,直到您获得所需标准的足够随机字节,特别是因为我怀疑显着削弱了随机性的强度,因此使用:crypto了无论如何都没有意义.


din*_*sky 5

这是一个解决方案,我将立即遵循该解决方案,说明为什么您可能不应该使用它或任何类似的解决方案:

defmodule RandomString do
  @chars "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
  @max String.length(@chars)-1
  defp random_char do
    ndx = Enum.random 0..@max
    String.slice @chars, ndx..ndx
  end
  def len(len) do
    list = for _ <- :lists.seq(1,len), do: random_char
    List.foldl(list, "", fn(e,acc) -> acc <> e end)
  end
end

iex> RandomString.len 12
"Z7Qb3xwzlKKj"
Run Code Online (Sandbox Code Playgroud)

现在,为什么你可能不应该使用它。您没有透露的是为什么您首先要生成随机字符串。我假设很可能的情况是您出于某种目的需要唯一的字符串。无论如何,几乎每个版本的“我需要随机字符串”问题的解决方案都使用有缺陷的规范(即字符串长度)来解决该问题。如果您为随机字符串指定字符串长度,那么您无疑会猜测您的真正需求和唯一性。

有两种主要方法可以获得严格的唯一性:确定性(不是随机的)和存储/比较(这是繁重的)。该怎么办?放弃鬼魂吧 相反,采用概率唯一性。也就是说,接受您的字符串存在一些(无论多么小)风险不是唯一的。这就是理解碰撞概率和熵很有帮助的地方。

例如,考虑上面长度为 12 的字符串。可以随机生成多少个而不重复?这个问题实际上没有明确说明。让我们重新表述一下。有多少可以以小于十亿分之一的重复机会随机生成?约254万。为什么?因为每个字符串的承载能力约为 71.5 位熵。

但您没有指定您需要生成 254 万个潜在的随机字符串,且重复风险低于十亿分之一。您也没有指定您需要长度为 12 的字符串。希望您可以看到前一个规范比猜测字符串长度更加明确。

计算我们真正需要的熵量可能有点麻烦。这就是EntropyString可以提供帮助的地方。假设您需要生成多达 50 万个 ID,重复风险低于万亿分之一。

iex> defmodule Id do
...>   use EntropyString, charset: charset64
...>   @bits entropy_bits(0.5e6, 1.0e12)
...>   def random, do: Id.random_string(@bits)
...> end
iex> Id.random
"tY0W9tyrq_P08"
Run Code Online (Sandbox Code Playgroud)

哎呀,这强调了你不想要的。charset64包含 URL 和文件系统安全字符。出于效率原因,EntropyString仅使用 2 个字符的幂的字符集。

iex> defmodule Id do
...>   use EntropyString, charset: charset32
...>   @bits entropy_bits(0.5e6, 1.0e12)
...>   def random, do: Id.random_string(@bits)
...> end
iex> Id.random
"dTPmjTq7pgPjqBjT"
Run Code Online (Sandbox Code Playgroud)

琴弦稍长,但也许更具视觉吸引力。更重要的是,指定数量的字符串重复的风险是明确的。不再猜测字符串长度。