【问题标题】:Including regex on variable before matching string在匹配字符串之前包括变量的正则表达式
【发布时间】:2018-10-17 02:42:01
【问题描述】:

我正在尝试在文本文件中查找并提取从文本文件中读取的单词的出现。到目前为止,我只能找到单词何时正确写入而不是 munged(a 更改为 @ 或 i 更改为 1)。是否可以在我的字符串中添加正则表达式以进行匹配或类似的东西?到目前为止,这是我的代码:

sub getOccurrenceOfStringInFileCaseInsensitive
{
  my $fileName = $_[0];
  my $stringToCount = $_[1];
  my $numberOfOccurrences = 0;
  my @wordArray = wordsInFileToArray ($fileName);

  foreach (@wordArray)
  {
    my $numberOfNewOccurrences = () = (m/$stringToCount/gi);
    $numberOfOccurrences += $numberOfNewOccurrences;
  } 


  return $numberOfOccurrences;
}

例程接收文件名和要搜索的字符串。例程 wordsInFileToArray () 只是从文件中获取每个单词并返回一个包含它们的数组。 理想情况下,我想一次性直接从文件中读取执行此搜索,而不是将所有内容移动到数组并遍历它。但主要的问题是如何将一些东西硬编码到函数中,让我能够捕捉到单词。

示例:我想从文件中提取两行。 示例.txt:

russ1@anh@ck3r

俄罗斯黑客

# this variable also will be read from a blacklist file
$searchString = "russianhacker";
getOccurrenceOfStringInFileCaseInsensitive ("example.txt", $searchString);

提前感谢您的任何回复。

编辑:

可能的替换将由用户定义,并且正则表达式必须设置为适合。用户可以说常见的替换是将字母“a”更改为“@”甚至“1”。可能的改变是完全任意的。 在搜索特定单词(例如“俄语”)时,可以使用以下方式:

(m/russian/i); # would just match the word as it is
(m/russi[a@1]n/i); # would match the munged word

但如果我将要匹配的字符串存储在变量中,我不确定该怎么做,例如:

$stringToSearch = "russian";

【问题讨论】:

  • 用户选择的替换是全局的——它们适用于给定字符的每个出现,用任何词吗?如果不是(捕捉h@cker 会搞砸name@host.org?),用户提供修复(@a 等)它们适用的词(甚至那些他们被破坏了,所以要么hacker要么h@cker)?

标签: regex perl


【解决方案1】:

这是一个全文搜索问题,因此一种方法是在匹配文档字符串之前对其进行规范化。

use strict;
use warnings;
use Data::Munge 'list2re';
...
my %norms = (
  '@' => 'a',
  '1' => 'i',
  ...
);
my $re = list2re keys %norms;
s/($re)/$norms{$1}/ge for @wordArray;

这种方法只有在任何给定单词只有一个可能的“规范化形式”时才有效,并且如果您的文档足够大并且每次都重新计算它,那么效率可能低于尝试搜索字符串的所有可能变体你搜索一下。

请注意,您的正则表达式 m/$randomString/gi 应该是 m/\Q$randomString/gi,因为您不希望 $randomString 中的任何正则表达式元字符被这样解释。请参阅quotemeta 的文档。

【讨论】:

  • 遗憾的是,可能的替换是由用户任意定义的,我不能确定是否会有多个“规范化形式”。我想我会实现这个替换以防万一。很好地抓住了quotemeta。我的带有要搜索的字符串的测试文件不包含任何有问题的字符,所以我没有遇到问题。我会在我的代码中调整它。非常感谢。
  • 您能否编辑您的问题以包括(以及如何)替换是由用户定义的?
  • 我已经添加了用户如何定义它。我仍然需要在例程的主体中设置这些规则,而黑名单仍然只是一个包含一些单词的文本文件。
【解决方案2】:

问题的某些部分没有足够精确地指定(还)。

一些取决于细节的自己动手的方法是

  • 如果用户定义的替换是全局的(替换每个字符串中出现的每个字符),则用户可以提交映射,就像哈希一样,您可以将它们全部修复。该过程将识别单词的所有候选者(以及实际的、未损坏的单词,如果找到的话)。可能会有误报,所以也要计划一些后处理

  • 如果用户可以提供替换列表以及他们适用的单词(损坏的或相应的未损坏的),那么我们可以进行更有针对性的运行

在澄清之前,这里有另一种方法:使用模块进行近似(“模糊”)匹配。

String::Approx 似乎符合您的许多要求。

目标与给定字符串的匹配依赖于 Levenshtein 编辑距离的概念:将给定字符串放入、删除和替换(“编辑”)需要多少次寻求的目标。可以设置接受的最大编辑次数。

一个简单的例子:

use warnings;
use strict;
use feature 'say';

use String::Approx qw(amatch);

my $target = qq(russianhacker);

my @text = qw(that h@cker was a russ1@anh@ck3r);

my @matches = amatch($target, ["25%"], @text);

say for @matches;     #==>  russ1@anh@ck3r

请参阅文档了解该模块对我们的帮助,但至少有两个 cmets。

首先,请注意amatch 中的第二个参数指定了可接受的目标字符串的百分位偏差。对于这个特殊的例子,我们需要允许每四个字符被“编辑”。如此大的调整空间可能会导致意外匹配,然后需要将其过滤掉,因此需要进行一些后期处理。

第二个——我们没有找到更简单的,h@cker。该模块采用固定的“模式”(目标),而不是正则表达式,并且一次只能搜索一个。因此,原则上,您需要为每个目标字符串传递一次。这可以改进很多,但还有更多工作要做。

请研究文档;该模块提供的不仅仅是这个简单的示例。

【讨论】:

  • 这个扩展最终解决了我以后在项目中会遇到的问题。谢谢你。
  • @kaylani2 太好了。我想说,如果您的问题不是很明确,那么模糊匹配会更好,而如果您确实有明确和直接的要求,手动方法可能会更好。如果出现问题,或者您的要求是否“具体化”,请告诉我。
【解决方案3】:

我已经通过将正则表达式直接包含在我将用于匹配文件行的变量中来解决问题。它看起来像这样:

sub getOccurrenceOfMungedStringInFile
{
  my $fileName = $_[0];
  my $mungedWordToCount = $_[1];
  my $numberOfOccurrences = 0;

  open (my $inputFile, "<", $fileName) or die "Can't open file: $!";

  $mungedWordToCount =~ s/a/\[a\@4\]/gi;

  while (my $currentLine = <$inputFile>)
  {
    chomp ($currentLine);
    $numberOfOccurrences += () = ($currentLine =~ m/$mungedWordToCount/gi);
  }

  close ($inputFile) or die "Can't open file: $!";

  return $numberOfOccurrences;
}

所在行:

$mungedWordToCount =~ s/a/\[a\@4\]/gi;

只是需要的替换之一,其他的可以类似地添加。 我不知道 Perl 只会解释变量内部的正则表达式,因为我之前已经尝试过,并且只能得到想要的结果,使用单引号定义函数内部的变量。我一定是第一次做错了什么。

谢谢大家的建议。

【讨论】:

    猜你喜欢
    • 2020-06-30
    • 1970-01-01
    • 1970-01-01
    • 2012-02-09
    • 2014-02-04
    • 1970-01-01
    • 2019-01-05
    • 2016-10-22
    • 1970-01-01
    相关资源
    最近更新 更多