Regex.compile/2的不区分大小写的选项

men*_*sai 2 regex elixir

我正在尝试构建一个无壳的正则表达式二进制文件,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
Run Code Online (Sandbox Code Playgroud)

Kev*_*son 7

简而言之

根据您提供的链接,需要提供选项,list因为您可以提供多个选项.以下应该有效:

Regex.compile("^(foo):?", [:caseless])
Run Code Online (Sandbox Code Playgroud)

更详细

型号规格如下:

compile(source, options \\ "") 
compile(binary(), binary() | [term()]) :: {:ok, t()} | {:error, any()}
Run Code Online (Sandbox Code Playgroud)

第二行是类型规范dialyzer,基本上表示function compile接受两个参数:

  1. 第一个是二进制,对应于你的 "^(foo):?"
  2. 第二个是二进制文件,或者是包含多个文件的列表terms.

返回值将是{:ok, t()}成功的情况,其中t()%Regex{}结构,或者{:error, any()}在出现错误的情况下.

回到第二个参数的讨论,如果是列表,则需要利用此处提到的各种选项.

在这种情况下binary,您可以将第二个参数作为单个字母缩写提供.因此,以下将失败:

  • Regex.compile("^(foo):?", "caseless")

另一方面,以下成功:

  • Regex.compile("^(foo):?", "i")

您可以从我上面链接的各种模块修饰符的表中获得的映射.

这些方法之间的主要区别在于,Erlang Regex由标准:re之上的构建提供支持PCRE.根据该标准,各种module modifiers由单个小写字母处理,例如i,u等等.因此,您可以相应地将两个选项组合binary如下:

  • Regex.compile("^(foo):?", "iu")

从技术上讲,它应该给你相当于:

  • Regex.compile("^(foo):?", [:caseless, :unicode])

这允许您通过语言规范或规范Regex在Erlang中进行通信.ElixirPCRE


高级细节

由于OP正确地在评论中指出,有一些困惑,为什么Regex在两种不同的方式产生(例如,通过optionslistVS optionsbinary)看起来不同.

要更详细地解释这种差异,请考虑以下方案:

  • r0 = Regex.compile!("(foo):?") ---> ~r/(foo):?/
  • r1 = Regex.compile!("(foo):?", "i") ---> ~r/(foo):?/i
  • --->~r /(foo):?/ # ?????? WHERE IS THEi` ?????

当遇到这种情况时,人们可能会产生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(),那么Regexin Elixir就是一个struct引擎盖.

A 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.那些其他领域仅用于文档目的.

接下来,在源代码的基础上,您可以看到binary选项被转换为list选项的版本,因为这就是Erlang正则表达式引擎所:re期望的.

尽管本身并不困难,但Elixir核心团队已选择不提供相反的翻译,例如从实际的module modifier原子列表到等效PCRE binary选项,因此您最终opts会将字段保留为空并且在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并且r2可能看起来不同,但它们的行为完全相同.