我尝试使用Regex.compile/2
构建无壳正则表达式二进制文件,但似乎无法找到有关如何设置该选项的示例。
Regex.compile("^(foo):?", :caseless)
** (FunctionClauseError) no function clause matching in Regex.compile/3
The following arguments were given to Regex.compile/3:
# 1
"^(foo):?"
# 2
:caseless
# 3
"8.41 2017-07-05"
(elixir) lib/regex.ex:140: Regex.compile/3
答案 0 :(得分:5)
根据您提供的链接,options需要配置为list
,因为您可以提供多种选项。以下应该有效:
Regex.compile("^(foo):?", [:caseless])
类型规范如下:
compile(source, options \\ "")
compile(binary(), binary() | [term()]) :: {:ok, t()} | {:error, any()}
第二行是dialyzer
中的类型规范,基本上表明function
compile
接受两个参数:
"^(foo):?"
terms
的列表。如果成功,返回值将为{:ok, t()}
,如果出现错误,则返回t()
is a %Regex{}
struct或{:error, any()}
。
回到第二个参数的讨论,如果是列表,则需要利用上面提到的各种选项here。
对于binary
,您可以将第二个参数作为单个字母缩写提供。因此,以下内容将失败:
Regex.compile("^(foo):?", "caseless")
另一方面,以下成功:
Regex.compile("^(foo):?", "i")
您可以从the table of the various module modifiers I linked to above获取映射。
这些方法之间的主要区别在于,由Erlang
驱动的Regex
:re
建立在PCRE
标准之上。根据该标准,各种module modifiers
由单个小写字母处理,例如i
,u
等。因此,您可以将这两个选项与binary
相结合如下:
Regex.compile("^(foo):?", "iu")
从技术上讲,应该给你相当于:
Regex.compile("^(foo):?", [:caseless, :unicode])
这样,您就可以通过语言规范或Regex
规范在Erlang和Elixir
中与PCRE
进行沟通。
正如OP
在评论中正确指出的那样,对于为什么Regex
以两种不同的方式产生(例如通过options
作为list
vs,存在一些混淆options
binary
视为不同。
要更详细地解释这种差异,请考虑以下情况:
r0 = Regex.compile!("(foo):?")
---> ~r/(foo):?/
r1 = Regex.compile!("(foo):?", "i")
---> ~r/(foo):?/i
--->
~r /(foo):?/ # ?????? WHERE IS THE
i` ????? 遇到这种情况时,可能会产生Elixir
Regex
被打破的印象。 r0
和r2
与r1
相同且不同。
但是,功能明智,r2
的行为与r1
相似,而不像r0
,请考虑以下示例,这些示例受到OP评论的无耻启发:
Regex.replace(r0, "Foo: bar", "")
---> "Foo: bar"
Regex.replace(r1, "Foo: bar", "")
---> " bar"
Regex.replace(r2, "Foo: bar", "")
---> " bar"
那怎么可能呢?
如果你从上面回忆起来,例如与t()
类型的解释有关,Regex
中的Elixir
只不过是struct
。
Regex
可以通过以下方式精美呈现:~r/(foo):?/
,但实际上它只不过是这样的:
%Regex{ opts: opts, re_pattern: re_pattern, re_version: re_version, source: source }
现在,在所有这些struct
字段中,在一天结束时唯一重要的是:re_pattern
。这将包含具有所有选项的完全编译的Regex
。所以我们相应地找到了:
r1.re_pattern == r2.re_pattern
但r0.re_pattern != r2.re_pattern
就opts
字段而言,这是一个仅为binary
格式的选项保留的容器。所以你会发现:
- r0.opts == r2.opts == ""
鉴于:
- r1.opts == "i"
这些相同的opts
字段用于在Regex
结尾处精美地显示选项,因此您将看到:
~r/(foo):?/
同时为r0
以及r2
但你会看到:~r/(foo):?/i
的r1
由于opts
字段彼此不同。
出于这个原因,您可以手动更新Regex
,如果您希望它看起来更加一致,例如:%{r2 | opts: "i"}
---> ~r/(foo):?/i
除了字段re_pattern
之外,其他任何字段都不会对实际Regex
产生任何影响。那些其他领域仅用于文档目的。
接下来,根据source code,您可以看到binary
个选项已转换为选项的list
版本because that is how Erlang
regex engine, :re
expects them to be.
尽管Elixir
核心团队本身并不困难,但他们选择不提供相反的翻译,例如从实际的module modifier
原子列表到等效的PCRE
binary
选项,最终导致opts
字段保持为空并且丧失了{{1}中相应的选项}} PCRE
格式因此,您最终会得到binary
的有缺陷的渲染,如上面的差异所示。
上面我只研究了解释这种差异的机制,然而,这种差异是否合理本身就是另一个问题。如果有一个比我更有洞察力的人能够澄清是否有任何方法来捍卫这种差异,我将非常感激。
Regex
---> r0 = Regex.compile!("(foo):?")
~r/(foo):?/
---> r1 = Regex.compile!("(foo):?", "i")
~r/(foo):?/i
---> r2 = Regex.compile!("(foo):?", [:caseless])
~r/(foo):?/
和r1
可能看起来不一样,但它们的行为完全相同。