【问题标题】:elixir String.split/3: how to tell which character was matchedelixir String.split/3:如何判断匹配了哪个字符
【发布时间】:2017-05-23 04:03:01
【问题描述】:

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

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

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

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

感谢您的阅读!

【问题讨论】:

  • 您是否只想拆分为两个二进制文件(在第一次出现 {} 时),或者这只是一个示例,您想在 每个处拆分> 出现{}
  • 是的,我只需要左右部分(第一次出现),我只需要知道匹配的是什么

标签: string elixir


【解决方案1】:

由于您只想在第一次出现时拆分,我建议在这里使用: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

【讨论】:

  • 就是这样!谢谢你。我也倾向于binary_part,因为“{”和“}”是ASCII字符。来自维基百科en.wikipedia.org/wiki/UTF-8“向后兼容性:单字节代码用于 ASCII 值 0 到 127,...此范围内的字节不用于其他任何地方...因为它不会意外看到中间的那些 ASCII 字符多字节字符。”
  • 是的,这不会以任何方式弄乱 UTF-8 编码的二进制文件。
  • 请查看我的编辑以获得更优雅的解决方案!如果您要对此进行基准测试,请告诉我这两个版本的比较情况。
  • 是的,打算对结果进行基准测试和博客 :)
  • 第二个版本除了更漂亮之外稍微快一点nerds.stoiximan.gr/2017/05/24/…
【解决方案2】:

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

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

【讨论】:

  • 感谢您的提示,但它使用下面的正则表达式引擎,我想避免使用它。不过很好,我完全错过了!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-10-09
  • 2015-03-29
  • 2010-11-02
  • 1970-01-01
  • 1970-01-01
  • 2021-04-18
  • 2018-04-19
相关资源
最近更新 更多