我有一个很大的字母字符串文件seq.txt
,已解包,超过200,000个字符。没有空格,数字等,只有a-z。
我有第二个文件search.txt
,其中有50个唯一字母的行,它们在seq.txt
中匹配一次。有4000种样式可以匹配。
我希望能够找到每个模式(文件search.txt
中的行),然后获得模式匹配之前的100个字符和之后的100个字符。
我有一个脚本,该脚本使用grep可以工作,但是运行速度非常慢,只执行前100个字符,并以回显方式写出。我对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
最好的结果是将100 characters before \t matched pattern \t 100 characters after
的一个制表符分隔的文件。预先谢谢你!
答案 0 :(得分:4)
一种方式
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;
}
}
以program.pl seq.txt
的身份运行,或将seq.txt
的内容通过管道传递给它。†
模式.{0,$padding}
与任何字符(.
)匹配,最多 $padding
次(上面的3
),这与我使用的情况相同模式$patt
位于比$padding
更靠近字符串开头的位置(就像我在问题中提供的示例中添加的第一个bcd
一样)。 $patt
之后的填充也是如此。
如果遇到问题,请将$padding
替换为100
。在每个样式之前和之后使用100
宽的“填充”,如果在比100更靠近开始位置的位置找到样式,则如果\t
对齐方式小于比制表符的值大100(通常为8)。
这就是格式化打印(printf
)所在的行的用途,以确保每个字段的宽度,而不管所打印字符串的长度如何。 (由于我们被告知没有模式进入前100个或最后一个字符,因此已将其注释掉。)
如果确实没有匹配的样式突破前100个位置或最后一个位置的机会,则可以将正则表达式简化为
/(.{$padding}) ($patt) (.{$padding})/x
请注意,如果$patt
位于的第一个/最后一个字符内,则这将不匹配。
该程序为$padding
中的每一个启动正则表达式引擎,原则上可能会引起性能问题(不是针对极少数4000种模式的运行,但是这种要求往往会发生变化并且通常会增长)。但这是迄今为止最简单的方法
我们不知道模式如何在字符串中分布,并且
一个匹配项可能位于另一个匹配项的100个字符内(我们不会告诉其他人)
如果此方法存在性能问题,请更新。
†可以通过@patterns
使用命名的命令行参数以更好的方式组织程序的输入(和输出),以进行类似
Getopt::Long
这里每个参数都是可选的,文件中设置了默认值,参数名称可能会缩短和/或指定其他名称,等等。让我知道这是否有意义
答案 1 :(得分:2)
awk中的一个。 -v b=3
是上下文前后的长度-v a=3
是上下文前后的长度,-v n=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
输出:
cdefghijk
mnopqrstu
rstuvwxyz
答案 2 :(得分:1)
您可以告诉grep
一次性搜索所有模式。
sed 's/.*/.{0,100}&.{0,100}/' search.txt |
grep -zoEf - seq.txt |
sed G >do.grep
4000种模式应该很容易实现,尽管如果成千上万种,也许您将需要优化。
这里没有Perl正则表达式,因此我从非标准grep -P
切换到POSIX兼容且可能更高效的grep -E
。
周围的上下文将使用它打印的所有文本,因此与前一个字符相距100个字符以内的任何匹配项都不会被打印。
答案 3 :(得分:1)
您可以尝试以下解决问题的方法:
注意:由于缺少输入数据,因此未测试代码
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