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