elixir String.split / 3:如何判断哪个字符匹配

时间:2017-05-22 12:28:17

标签: string elixir

我正在尝试使用String.split/3构建一个解析器作为练习,看看我是否可以比经典的递归模式匹配更快(通过有效地分配更少的字符串),但是我在制作它时遇到了麻烦正确的。

我需要能够在{}分成两部分的二进制文件,而String.split(str, ["{", "}"], parts: 2)当然就是这样。我的问题是结果只包含列表[part1, part2],但它不包含它匹配的字符,我需要它,因为它会影响解析器的行为。

我的第一直觉是制作我自己的index/2,但在阅读了更多内容之后seems就像bad idea

我的用例有更好的选择吗?我想尽可能少地浏览字符串,仅在{}边界创建新字符串。

感谢您阅读!

2 个答案:

答案 0 :(得分:3)

String.split docs中深入挖掘并使用include_captures选项。

iex> String.split("Foo{bar}", ~r({|}), [include_captures: true, trim: true])
["Foo", "{", "bar", "}"]

答案 1 :(得分:3)

由于您只想在第一次出现时进行拆分,因此建议您在此处使用:binary.match/3:binary.part/3:binary.match/3返回匹配索引的元组和成功匹配的长度,然后可以与:binary.part/2一起使用来分割二进制文件。

defmodule A do
  def split(binary) do
    case :binary.match(binary, ["{", "}"]) do
      {start, length} ->
        before = :binary.part(binary, 0, start)
        match = :binary.part(binary, start, length)
        after_ = :binary.part(binary, start + length, byte_size(binary) - (start + length))
        {before, match, after_}
      :nomatch -> nil
    end
  end
end

IO.inspect A.split("foo { bar") |> IO.inspect
IO.inspect A.split("foo } bar") |> IO.inspect
IO.inspect A.split("foo + bar") |> IO.inspect

输出:

{"foo ", "{", " bar"}
{"foo ", "{", " bar"}
{"foo ", "}", " bar"}
{"foo ", "}", " bar"}
nil
nil

此实现仅在:binary.match/3调用中通过字符串一次。使用的所有其他功能和操作均为O(1)

我在我正在使用的XML解析器中使用了类似的方法,与二进制文件上的递​​归模式匹配相比,它提供了巨大的速度。

编辑:您可以使用模式匹配来缩短此代码,并且我非常确定具有与上述完全相同的效果:

defmodule A do
  def split(binary) do
    case :binary.match(binary, ["{", "}"]) do
      {start, length} ->
        <<a::binary-size(start), b::binary-size(length), c::binary>> = binary
        {a, b, c}
      :nomatch -> nil
    end
  end
end