如何在 Elixir 中连接字符串?

问题描述 投票:0回答:10

如何用空格连接列表中的两个字符串,例如:

["StringA", "StringB"]

成为

"StringA StringB"
elixir
10个回答
284
投票

如果您只想加入某个任意列表:

"StringA" <> " " <> "StringB"

或者只使用字符串插值:

 "#{a} #{b}"

如果您的列表大小是任意的:

Enum.join(["StringA", "StringB"], " ")

...以上所有解决方案都会返回

"StringA StringB"

65
投票

如果你有一个任意列表,那么你可以使用

Enum.join
,但如果只有两个或三个,显式字符串连接应该更容易阅读

"StringA" <> " " <> "StringB"

但是,如果您要通过例如将其输出,通常不需要将其作为单个字符串存储在内存中。网络。在这种情况下,使用 iolist(深度列表的特定类型)可能会很有利,它可以使您免于复制数据。例如,

iex(1)> IO.puts(["StringA", " ", "StringB"])
StringA StringB
:ok

由于您将这些字符串作为变量放在某处,因此通过使用深层列表,您可以避免分配一个全新的字符串只是为了将其输出到其他地方。 Elixir/erlang 中的许多函数都支持 iolist,因此您通常不需要做额外的工作。


12
投票

每种方法如何处理
nil

方法有很多种,但是了解它如何处理 nil 值可以决定您应该选择哪种方法。

这会引发错误

iex(4)> "my name is " <> "adam"
"my name is adam"

iex(1)> "my name is " <> nil
** (ArgumentError) expected binary argument in <> operator but got: nil
    (elixir) lib/kernel.ex:1767: Kernel.wrap_concatenation/3
    (elixir) lib/kernel.ex:1758: Kernel.extract_concatenations/2
    (elixir) lib/kernel.ex:1754: Kernel.extract_concatenations/2
    (elixir) expanding macro: Kernel.<>/2
    iex:1: (file)

这只会插入一个空白的“”字符串:

iex(1)> "my name is #{nil}"
"my name is "

这也一样:

iex(3)> Enum.join(["my name is", nil], " ")
"my name is "

还要考虑类型。使用

<>
,您不会获得任何免费选角:

iex(5)> "my name is " <> 1
** (ArgumentError) expected binary argument in <> operator but got: 1
    (elixir) lib/kernel.ex:1767: Kernel.wrap_concatenation/3
    (elixir) lib/kernel.ex:1758: Kernel.extract_concatenations/2
    (elixir) lib/kernel.ex:1754: Kernel.extract_concatenations/2
    (elixir) expanding macro: Kernel.<>/2
    iex:5: (file)

iex(5)> "my name is #{1}"
"my name is 1"

iex(7)> Enum.join(["my name is", 1], " ")
"my name is 1"

实践中的表现似乎大致相同:

iex(22)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is " <> "adam" end) end)
{8023855, :ok}
iex(23)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is " <> "adam" end) end)
{8528052, :ok}
iex(24)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is " <> "adam" end) end)
{7778532, :ok}
iex(25)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is #{"adam"}" end) end)
{7620582, :ok}
iex(26)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is #{"adam"}" end) end)
{7782710, :ok}
iex(27)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is #{"adam"}" end) end)
{7743727, :ok}

所以,这实际上取决于当插值是

nil
或错误类型时您是否想要崩溃。


10
投票

为了完整回答,您还可以使用字符串插值

iex(1)> [a, b] = ["StringA", "StringB"]
iex(2)> "#{a} #{b}"
"StringA StringB"

7
投票

这取决于您想要做什么。如果您只是尝试写入新变量,则只需使用:

  • 字符串插值

    a = "StringA"
    b = "StringB"
    "#{a} #{b}"
    
  • 字符串连接:

    "StringA" <> " " <> "StringB

  • Enum.join()
    ["StringA", "StringB"] |> Enum.join(" ")

但是,正如 Uri 提到的,IOLists 也可以使用:

["StringA", " ", "StringB"] |> IO.iodata_to_binary

如果您需要关心资源消耗,IOList 实际上将是性能最高的。 Big Nerd Ranch 对 IOLists 的性能提升有一篇很好的文章


6
投票

如果您同意在列表中添加空格,您可以将其视为 iolist:

["StringA", " ", "StringB"] |> IO.iodata_to_binary # "StringA StringB"

这会给您带来一些性能提升,因为您不会复制内存中的任何字符串。


3
投票

Enum.reduce 也适用于您的示例,不是吗?

iex(4)> Enum.reduce(["StringA", "StringB"], fn(x, acc) -> x <> " " <> acc end)
"StringB StringA"


1
投票

考虑使用 IO 列表,如果你有 ["String1", "string2"] 并且你在上面使用 iolist_to_binary/1 那么你将把这些字符串复制到一个新字符串中。如果你有一个 IO 列表,在大多数情况下你可以直接输出它,它会在端口上连接它。这是关键,运行时不需要复制数据,因此它比串联效率更高。


0
投票

其他任何答案中都没有提到的另一个选项:位字符串运算符

iex(1)> <<"StringA", " ", "StringB">>
"StringA StringB"

-1
投票

你也可以这样做

'string A' ++ ' ' ++ 'string B'

© www.soinside.com 2019 - 2024. All rights reserved.