[英]Elixir - How do I split a string into a list by every 3 characters
如果我有字符串"UGGUGUUAUUAAUGGUUU"
,我如何将它变成一个列表,每 3 个字符分成["UGG", "UGU", "UAU", "UAA", "UGG", "UUU"]
?
如果您的字符串仅包含 ASCII 字符,并且您的字符串的byte_size
是 3 的倍数,那么使用鲜为人知的 Elixir 功能有一个非常优雅的解决方案:二进制byte_size
:
iex(1)> string = "UGGUGUUAUUAAUGGUUU"
"UGGUGUUAUUAAUGGUUU"
iex(2)> for <<x::binary-3 <- string>>, do: x
["UGG", "UGU", "UAU", "UAA", "UGG", "UUU"]
这将字符串拆分为 3 个字节的块。 这将比在代码点或字素上拆分要快得多,但如果您的字符串包含非 ASCII 字符,则将无法正常工作。 (在那种情况下,我会选择@michalmuskala 的回答。)
编辑:Patrick Oscity 的回答提醒我这也适用于代码点:
iex(1)> string = "αβγδεζηθικλμνξοπρςστυφχψ"
"αβγδεζηθικλμνξοπρςστυφχψ"
iex(2)> for <<a::utf8, b::utf8, c::utf8 <- string>>, do: <<a::utf8, b::utf8, c::utf8>>
["αβγ", "δεζ", "ηθι", "κλμ", "νξο", "πρς", "στυ", "φχψ"]
"UGGUGUUAUUAAUGGUUU"
|> String.codepoints
|> Enum.chunk_every(3)
|> Enum.map(&Enum.join/1)
我也想知道有没有更优雅的版本
这可以使用Stream.unfold/2
函数来实现。 在某种程度上,它与reduce
相反——reduce 允许我们将一个集合折叠成一个单一的值,展开是将一个单一的值扩展到一个集合中。
作为Stream.unfold/2
的生成器,我们需要一个返回元组的函数 - 第一个元素是生成的集合的下一个成员,第二个是我们将传递到下一次迭代的累加器。 这完全描述了函数String.split_at/2
。 最后,我们需要一个终止条件 - String.split_at("", 3)
将返回{"", ""}
。 我们对空字符串不感兴趣,所以在我们遇到空字符串之前处理我们生成的流应该就足够了——这可以通过Enum.take_while/2
来实现。
string
|> Stream.unfold(&String.split_at(&1, 3))
|> Enum.take_while(&(&1 != ""))
另一种可能性是使用Regex.scan/2
:
iex> string = "abcdef"
iex> Regex.scan(~r/.{3}/, string)
[["abc"], ["def"]]
# In case the number of characters is not evenly divisible by 3
iex> string = "abcdefg"
iex> Regex.scan(~r/.{1,3}/, string)
[["abc"], ["def"], ["g"]]
# If you need to handle unicode characters, you can add the `u` modifier
iex> string = "🙈🙉🙊abc"
iex> Regex.scan(~r/.{1,3}/u, string)
[["🙈🙉🙊"], ["abc"]]
或者使用递归函数,这有点冗长,但 IMO 应该是使用急切评估的最佳解决方案:
defmodule Split do
def tripels(string), do: do_tripels(string, [])
defp do_tripels(<<x::utf8, y::utf8, z::utf8, rest::binary>>, acc) do
do_tripels(rest, [<<x::utf8, y::utf8, z::utf8>> | acc])
end
defp do_tripels(_rest, acc) do
Enum.reverse(acc)
end
end
# in case you actually want the rest in the result, change the last clause to
defp do_tripels(rest, acc) do
Enum.reverse([rest | acc])
end
如何使用String.split :
iex> String.split("UGGUGUUAUUAAUGGUUU", ~r/.{3}/, include_captures: true, trim: true)
["UGG", "UGU", "UAU", "UAA", "UGG", "UUU"]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.