【问题标题】:Is it possible to interpolate Array values in token?是否可以在令牌中插入数组值?
【发布时间】:2020-01-06 23:27:32
【问题描述】:

我正在研究homoglyphs module,我必须构建正则表达式,它可以找到对应于 ASCII 等价物的同形文字。

例如,我有一个没有同形文字替代品的字符:

my $f = 'f';

和可以混淆的字符:

my @o = 'o', 'о', 'ο'; # ASCII o, Cyrillic o, Greek omicron

我可以轻松构建正则表达式来检测同形词短语“foo”:

say 'Suspicious!' if $text ~~ / $f @o @o /;

但是如果我不知道在编译时要检测的值,我应该如何编写这样的正则表达式?假设我想检测邮件中包含同形字“现金”字样的网络钓鱼。我可以使用所有替代方案构建序列:

my @lookup = ['c', 'с', 'ϲ', 'ς'], ['a', 'а', 'α'], 's', 'h'; # arbitrary runtime length

现在显然以下解决方案无法将数组元素“解包”到正则表达式中:

/ @lookup / # doing LTM, not searching elements in sequence

我可以通过手动引用每个元素并编写替代项的文本表示来解决此问题,以获得可以作为正则表达式评估的字符串。并使用字符串插值构建令牌:

my $regexp-ish = textualize( @lookup ); # string "[ 'c' | 'с' | 'ϲ' | 'ς' ] [ 'a' | 'а' | 'α' ] 's' 'h'"
my $token = token { <$regexp-ish> }

但这很容易出错。 是否有任何更简洁的解决方案可以从编译时未知的任意数量的元素动态组合正则表达式

【问题讨论】:

  • 顺便说一句 - 我知道我也可以递归地构建它,将每个查找部分嵌套在子令牌中:my $token1 = token { @lookup[0] }; $token2 = token { &lt;$token1&gt; @lookup[1] };...(当然这是语法错误,只是说明这个想法)。然而,这不会产生“健康”的语法。

标签: raku


【解决方案1】:

Unicode::Security 模块通过使用Unicode consortium tables 实现可混淆。它实际上没有使用正则表达式,只是在这些表中查找不同的字符。

【讨论】:

  • 谢谢,我会在我的模块文档中链接它。这是不同的方法(我的重点是展开,因为它起源于国际域名网络钓鱼检查工具)但对于快速检查字符串与字符串,你的方法是更优雅的解决方案。我肯定会从中汲取一些映射灵感。
  • 哦,嘿,你已经有了。考虑到 Unicode 保存数据的位置(我认为它在 CLDR 中,但事实并非如此),Unicode:: 是它的完美命名空间。是的,我要少做一件事:-)
【解决方案2】:

我不确定这是最好的使用方法。

我还没有在 Intl:: 中实现 confusables1 模块,尽管我确实计划最终解决它,这里有两种不同的方式我可以想象一个令牌的外观。 2

my token confusable($source) {
  :my $i = 0;                                    # create a counter var
  [
    <?{                                          # succeed only if
      my $a = self.orig.substr: self.pos+$i, 1;  #   the test character A
      my $b = $source.substr: $i++, 1;           #   the source character B and

      so $a eq $b                                #   are the same or
      || $a eq %*confusables{$b}.any;            #   the A is one of B's confusables
    }> 
    .                                            # because we succeeded, consume a char
  ] ** {$source.chars}                           # repeat for each grapheme in the source
}

在这里,我使用了动态哈希 %*confusables,它会以某种方式填充——这取决于您的模块,甚至可能不一定是动态的(例如,具有签名 :($source, %confusables) 或引用模块变量等.

然后您可以让您的代码按如下方式工作:

say $foo ~~ /<confusable: 'foo'>/

这可能是处理事情的最佳方式,因为它会给你更多的控制权——我在你的模块中达到了顶峰,很明显你想要启用 2 对 1 字形关系,最终你可能会想要直接在字符上运行代码。

如果您只接受一对一的关系,您可以使用更简单的令牌:

my token confusable($source) {
  :my @chars = $source.comb;            # split the source 
  @(                                    # match the array based on 
     |(                                 #   a slip of
        %confusables{@chars.head}       #     the confusables 
        // Empty                        #     (or nothing, if none)
     ),                                 #
     @a.shift                           #   and the char itself
   )                                    #
   ** {$source.chars}                   # repeating for each source char
}

@(…) 结构让您可以有效地创建一个要插值的临时数组。在这种情况下,我们只需将可混淆的内容与原始内容一起滑入,仅此而已。不过你必须小心,因为不存在的哈希项将返回类型对象(Any),这会在这里搞砸(因此// Empty

在任何一种情况下,您都需要在令牌中使用参数,因为动态构建正则表达式充满了潜在的陷阱和插值错误。


1Unicode 将同形异义词称为“视觉上相似的字符”和“易混淆的字符”。

2这里的动态哈希 %confusables 可以通过多种方式填充,并且不一定需要是动态的,因为它可以通过参数填充(使用像 @987654332 这样的签名@ 或引用模块变量。

【讨论】:

  • 手动检查令牌中的 eval 块,然后人为地消耗等于该匹配项的字符数量(这样令牌的行为就像外部世界的令牌)就可以解决问题。最终的代码会比你写的更复杂,因为在多对多匹配中会有不止一个路径。例如,如果您有 ioo 的同形文字,则检查 kiosk 将有 k [ io homoglyphs ] s kk i [ o homoglyphs ] s k 进行检查。但这在 regexp eval 块中是可行的。谢谢详细解答,真的很有帮助!
  • %confusables{@chars.head} with %confusables{@chars.head} 可以写成%confusables{@chars.head} // ()。 (() 可以是Empty。)
  • raiph:呃,这就是我在深夜写答案的结果。谢谢!
  • @PawelPabianbbkr 使用[ . { code } ]+ 可以做很多有趣的事情。如果您希望将其打包到一个模块中,您可能需要查看我关于模糊匹配的出现日帖子:rakuadventcalendar.wordpress.com/2019/12/15/… 关于向匹配对象添加一些附加属性的方法(例如,您可能想要确保Match 对象包含三个值:匹配的字符串、实际字符串,以及为方便起见,一个布尔值,指示它是相同匹配还是通过可混淆匹配)
  • @user0721090601 Regex::FuzzyToken 是我向模块添加令牌的主要灵感。并看到 sub 包裹在令牌方法中以应用角色来扩展匹配对象...哇 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-19
相关资源
最近更新 更多