我正在尝试使用String.split/3
构建一个解析器作为练习,看看我是否可以比经典的递归模式匹配更快(通过有效地分配更少的字符串),但是我在制作它时遇到了麻烦正确的。
我需要能够在{
或}
分成两部分的二进制文件,而String.split(str, ["{", "}"], parts: 2)
当然就是这样。我的问题是结果只包含列表[part1, part2]
,但它不包含它匹配的字符,我需要它,因为它会影响解析器的行为。
我的第一直觉是制作我自己的index/2
,但在阅读了更多内容之后seems就像bad idea
我的用例有更好的选择吗?我想尽可能少地浏览字符串,仅在{
和}
边界创建新字符串。
感谢您阅读!
答案 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