Perl 从awk中的文件中将前后字符提取为匹配的字符串

Perl 从awk中的文件中将前后字符提取为匹配的字符串,perl,awk,grep,Perl,Awk,Grep,我有一个很大的字符串文件seq.txt,里面有超过200000个字符。没有空格、数字等,只有a-z 我还有第二个文件search.txt,其中有50行唯一的字母,它们将在seq.txt中匹配一次。有4000种模式需要匹配 我希望能够找到每个模式(文件search.txt中的行),然后在模式匹配之前获得100个字符,在模式匹配之后获得100个字符 我有一个脚本,使用grep并运行,但运行速度非常慢,只运行前100个字符,并用echo写出。我在awk或perl方面的知识还不够渊博,无法在线解释可能适

我有一个很大的字符串文件
seq.txt
,里面有超过200000个字符。没有空格、数字等,只有a-z

我还有第二个文件
search.txt
,其中有50行唯一的字母,它们将在
seq.txt
中匹配一次。有4000种模式需要匹配

我希望能够找到每个模式(文件
search.txt
中的行),然后在模式匹配之前获得100个字符,在模式匹配之后获得100个字符

我有一个脚本,使用grep并运行,但运行速度非常慢,只运行前100个字符,并用echo写出。我在awk或perl方面的知识还不够渊博,无法在线解释可能适用的脚本,所以我希望这里有人能够理解

cat search.txt | while read p; do echo "grep -zoP '.{0,100}$p' seq.txt | sed G"; done > do.grep
具有所需输出的简单示例:

>head seq.txt    
abcdefghijklmnopqrstuvwxyz

>head search.txt
fgh
pqr
uvw

>head desiredoutput.txt
cdefghijk
mnopqrstu
rstuvwxyz
最好的结果是在匹配模式前\t 100个字符后\t 100个字符的
标签分隔文件。提前谢谢你

单向

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

my $string;

# Read submitted files line by line (or STDIN if @ARGV is empty)
while (<>) {
    chomp;
    $string = $_;    
    last;          # just in case, as we need ONE line
}
# $string = q(abcdefghijklmnopqrstuvwxyz);   # test

my $padding = 3;  # for the given test sample

my @patterns = do { 
    my $search_file = 'search.txt';
    open my $fh, '<', $search_file or die "Can't open $search_file: $!";
    <$fh>;
};
chomp @patterns;
# my @patterns = qw(bcd fgh pqr uvw);  # test

foreach my $patt (@patterns) {
    if ( $string =~ m/(.{0,$padding}) ($patt) (.{0,$padding})/x ) {
        say "$1\t$2\t$3";
        # or
        # printf "%-3s\t%3s%3s\n", $1, $2, $3;
    }
}
请注意,如果一个
$patt
在第一个/最后一个
$padding
字符中,那么它就是不匹配的

该程序为每个
@模式启动regex引擎,原则上可能会引起性能问题(不是一次运行4000个模式的情况,但这种需求往往会发生变化,并且通常会增加)。但这是迄今为止最简单的方法

  • 我们不知道这些模式是如何在字符串中分布的,并且

  • 一个匹配可能在另一个的100字符缓冲区内(我们没有被告知其他情况)

如果此方法存在性能问题,请更新


†对于类似于

program.pl --sequence seq.txt --search search.txt --padding 100

其中每个参数在这里都是可选的,在文件中设置默认值,参数名称可以缩短和/或指定其他名称,等等。请告诉我是否对awk中的参数感兴趣
-vb=3
是前上下文长度
-va=3
是后上下文长度,
-vn=3
是始终不变的匹配长度。它将
seq.txt
的所有子字符串散列到内存中,因此它会根据
seq.txt
的大小使用它,您可能希望使用
top
来跟踪消耗,例如:
abcdefghij
->
s[“def”]=“abcdefghi”
s[“efg”]=“bcdefghij”

$ awk -v b=3 -v a=3 -v n=3 '
NR==FNR {
    e=length()-(n+a-1)
    for(i=1;i<=e;i++) {
        k=substr($0,(i+b),n)
        s[k]=s[k] (s[k]==""?"":ORS) substr($0,i,(b+n+a))
    }
    next
}
($0 in s) {
    print s[$0]
}' seq.txt search.txt

您可以告诉
grep
一次性搜索所有模式

sed's/*/.{0100}&.{0100}/'search.txt|
grep-zoEf-seq.txt|
sed G>do.grep
4000个模式应该很简单,但是如果你有几十万个,也许你会想要优化

这里没有Perl正则表达式,所以我从非标准的
grep-p
切换到与POSIX兼容并且可能更高效的
grep-E


周围的上下文将使用它打印的任何文本,因此不会打印上一个文本中100个字符以内的任何匹配项。

您可以尝试以下方法解决问题:

  • 加载字符串输入数据
  • 加载到阵列模式中
  • 循环遍历每个模式并在字符串中查找它
  • 从找到的匹配项中形成数组
  • 循环匹配数组并打印结果
注:由于缺少输入数据,代码未经测试

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

my $fname_s = 'seq.txt';
my $fname_p = 'search.txt';
    
open my $fh, '<', $fname_s
    or die "Couldn't open $fname_s";
my $data = do { local $/; <$fh> };
close $fh;

open my $fh, '<', $fname_p
    or die "Couln't open $fname_p";
my @patterns = <$fh>;
close $fh;

chomp @patterns;

for ( @patterns ) {
    my @found = $data =~ s/(.{100}$_.{100})/g;
    s/(.{100})(.{50})(.{100})/$1 $2 $3/ && say for @found;
}

如果图案前(或后)没有100个字符怎么办?(如在字符串中的位置50处或结尾前的位置60处发现图案。)使用任何填充?如果有多个相同的匹配,则打印一个或全部或计数?@zdim,它们都保证在距离ends@JamesBrown我的案子里没有完全相同的匹配,但是如果有打印,那么只有一行吗?谢谢!有没有办法从文件中输入字符串而不是键入它们?我的真实示例中的字符串有数十万个字符input@geri添加了从文件读取。在健壮性方面,存在一个问题,即如何处理文件中可能有多行的可能性;我只是在一读之后就跳出了循环。还有其他问题,如何处理可能的意外输入,所以请考虑生产使用。@ geri Forgot,你也需要从文件中的模式(当然!),谢谢!它完全符合我的要求,不会出现错误。@geri Great:)如果出现问题,请告诉我您可以测试我的解决方案使用了多少内存吗?
cdefghijk
mnopqrstu
rstuvwxyz
use strict;
use warnings;
use feature 'say';

my $fname_s = 'seq.txt';
my $fname_p = 'search.txt';
    
open my $fh, '<', $fname_s
    or die "Couldn't open $fname_s";
my $data = do { local $/; <$fh> };
close $fh;

open my $fh, '<', $fname_p
    or die "Couln't open $fname_p";
my @patterns = <$fh>;
close $fh;

chomp @patterns;

for ( @patterns ) {
    my @found = $data =~ s/(.{100}$_.{100})/g;
    s/(.{100})(.{50})(.{100})/$1 $2 $3/ && say for @found;
}
use strict;
use warnings;
use feature 'say';

my @pat  = qw/fgh pqr uvw/;
my $data = do { local $/; <DATA> }; 

for( @pat ) {
    say $1 if $data =~ /(.{3}$_.{3})/;
}

__DATA__
abcdefghijklmnopqrstuvwxyz
cdefghijk
mnopqrstu
rstuvwxyz