在匹配字符串之前在变量上包含正则表达式

时间:2018-10-08 23:05:15

标签: regex perl

我正在尝试查找并提取从文本文件中的文本文件读取的单词。到目前为止,我只能找到该单词正确书写且未蒙蒙的时候(将其更改为@或将我更改为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()只是从文件中获取每个单词,并返回包含它们的数组。 理想情况下,我想一次执行直接从文件读取的搜索,而不是将所有内容移动到数组并遍历整个数组。但是主要的问题是如何将一些东西硬编码到该函数中,以使我能够捕获被蒙住的单词。

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

russ1 @ anh @ ck3r

russianhacker

# 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";

3 个答案:

答案 0 :(得分:2)

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

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的文档。

答案 1 :(得分: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

请参阅文档以了解该模块的用途,但至少有两个注释。

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

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

请研究文档;该模块提供的功能远远超过此简单示例。

答案 2 :(得分:1)

我结束了解决问题的方法,将正则表达式直接包含在将用于与文件行匹配的变量上。看起来像这样:

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只会在变量内部解释正则表达式,因为我之前已经尝试过,并且只能获得使用单引号定义函数内部变量的所需结果。第一次我一定做错了。

谢谢大家的建议。