我在Elixir中生成一个随机字符串,如下所示:
len = 10
val = :crypto.strong_rand_bytes(len)
|> Base.url_encode64()
|> binary_part(0, len)
此代码的输出可以包含我不想要的连字符和下划线。将字母表限制为仅[0-9a-Z]
个字符的方法是什么?
答案 0 :(得分: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"
这将生成一个由[0-9A-Za-z]
组成的任意长度字符串,无需通过:crypto
生成随机字节并进行过滤,直到获得所需条件的足够随机字节为止,特别是因为我怀疑弱化随机性的强度显着,因此无论如何都使:crypto
成为一个有争议的点。
答案 1 :(得分:2)
这是一个解决方案,我会立即遵循为什么您可能不应该使用它或任何类似的解决方案:
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"
现在,为什么你可能不应该使用它。您尚未透露的是您首先生成随机字符串的原因。我假设您出于某种目的想要唯一字符串的极有可能的情况。无论如何,几乎所有解决各种版本的解决方案都需要随机字符串&#34;问题使用有缺陷的规范来处理解决方案,即字符串长度。如果你为随机字符串指定字符串长度,你无疑会猜测你的真正需要,唯一性。
有两种主要方法可以获得严格的唯一性:确定性(不是随机的)和存储/比较(这是繁重的)。该怎么办?放弃鬼魂。改为使用概率唯一性。也就是说,接受一些(但是很小的)风险,你的字符串不会是唯一的。这是理解碰撞概率和熵有用的地方。
例如,考虑长度为12的字符串。可以随机生成多少而不重复?这个问题实际上是不明确的。让我们改一下吧。有多少可以随机生成,重复几率不到1十亿?大约254万。为什么?因为每个弦的承载能力约为71.5比特的熵。
但是你没有指明你需要产生254万个随机字符串的潜力,其风险可能低于十亿分之一的重复。你也没有指定你需要长度为12的字符串。希望你能看到前一个规范比猜测字符串长度更明确。
计算我们真正需要的熵量可能有点麻烦。 EntropyString可以提供帮助的地方。让我们假设您需要产生多达50万个ID,并且重复小于1万亿的风险。
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"
哎呀,那里强调了你并不想要。 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"
琴弦稍长,但可能更具视觉吸引力。更重要的是,在指定数量的字符串中重复的风险是明确的。不再猜测字符串长度。
答案 2 :(得分:0)
alphabet =
?a..?z
|> Enum.concat(?A..?Z)
|> Enum.concat(?0..?9)
|> to_string
|> String.codepoints
len = 10
在这里,你有一个字母,字母a-z,A-Z,0-9作为代码点列表。
Enum.reduce((1..len), [], fn (_i, acc) -> [Enum.random(alphabet) | acc] end)
|> Enum.join()
现在Enum.reduce
会将字母表中的随机字符(Enum.random(alphabet)
)10次((1..len)
)添加到acc
的列表中。然后列表被加入,你有一个长度为len
的随机字符串。
答案 3 :(得分:0)
我会在编译时将可能的字符存储在二进制文件中,并在运行时从len
次中选择一个随机字节。
defmodule A do
@bytes Enum.concat([?a..?z, ?A..?Z, ?0..?9]) |> List.to_string
def random(length) do
for _ <- 1..length, into: <<>> do
index = :rand.uniform(byte_size(@bytes)) - 1
<<:binary.at(@bytes, index)>>
end
end
end
IO.inspect A.random(8)
IO.inspect A.random(16)
这应该是相当有效的,因为有效的字符集在编译时生成,:binary.at
比从列表中选择第n个值({(1)vs O(n))更高效(Enum.random
{{1}} 1}}列表)。