如何使用Perl搜索和替换hash

如何使用Perl搜索和替换hash,perl,hash,Perl,Hash,我是Perl新手,恐怕我被卡住了,想问问是否有人能帮助我 我有一个文件,有两列选项卡,分别是oldname和newname 我想使用oldname作为键,newname作为值,并将其存储为散列 然后,我想打开一个不同的文件gff文件,用新名称替换其中的所有旧名称,并将其写入另一个文件 我已经尽了最大的努力,但是我犯了很多错误 如果你能让我知道我做错了什么,我将不胜感激 以下是这两个文件的外观: oldname newnamesfxxx文件: genemark-scaffold00013-abin

我是Perl新手,恐怕我被卡住了,想问问是否有人能帮助我

我有一个文件,有两列选项卡,分别是oldname和newname

我想使用oldname作为键,newname作为值,并将其存储为散列

然后,我想打开一个不同的文件gff文件,用新名称替换其中的所有旧名称,并将其写入另一个文件

我已经尽了最大的努力,但是我犯了很多错误

如果你能让我知道我做错了什么,我将不胜感激

以下是这两个文件的外观:

oldname newnamesfxxx文件:

genemark-scaffold00013-abinit-gene-0.18 SF130001
augustus-scaffold00013-abinit-gene-1.24 SF130002
genemark-scaffold00013-abinit-gene-1.65 SF130003
要在其中一行的示例中搜索和替换的文件:

scaffold00013   maker   gene    258253  258759  .   -   .   ID=maker-scaffold00013-augustus-gene-2.187;Name=maker-scaffold00013-augustus-gene-2.187;
以下是我的尝试:

#!/usr/local/bin/perl
use warnings;
use strict;

my $hashfile = $ARGV[0];
my $gfffile  = $ARGV[1];
my %names;
my $oldname;
my $newname;

if (!defined $hashfile) {
    die "Usage: $0 hash_file gff_file\n";
}

if (!defined $gfffile) {
    die "Usage: $0 hash_file gff_file\n";
}

###save hashfile with two columns, oldname and newname, into a hash with oldname as key and newname as value.

open(HFILE, $hashfile) or die "Cannot open $hashfile\n";

while (my $line = <HFILE>) {
    chomp($line);
    my ($oldname, $newname) = split /\t/;
    $names{$oldname} = $newname;
}

close HFILE;

###open gff file and replace all oldnames with newnames from %names.

open(GFILE, $gfffile) or die "Cannot open $gfffile\n";

while (my $line2 = <GFILE>) {
    chomp($line2);

    eval "$line2 =~ s/$oldname/$names{oldname}/g";

    open(OUT, ">SFrenamed.gff") or die "Cannot open SFrenamed.gff: $!";

    print OUT "$line2\n";

    close OUT;
}

close GFILE;

谢谢大家!

您为什么要使用eval?$oldname将在第二个while循环中未定义,因为第一个while循环在该范围内重新声明它们,即使使用了外部范围,它也会存储您处理的最后一个值,这不会有帮助

去掉脚本顶部的my$oldname和my$newname,它是无用的

取出整个评估线。您需要为每个要替换的对象重复正则表达式。尝试以下方法:

$line2 =~ s/$_/$names{$_}/g for keys %names;

另见Borodin的答案。他做了一个大的正则表达式而不是一个循环,并抓住了你们缺少第二个参数来拆分

您为什么要使用eval?$oldname将在第二个while循环中未定义,因为第一个while循环在该范围内重新声明它们,即使使用了外部范围,它也会存储您处理的最后一个值,这不会有帮助

去掉脚本顶部的my$oldname和my$newname,它是无用的

取出整个评估线。您需要为每个要替换的对象重复正则表达式。尝试以下方法:

$line2 =~ s/$_/$names{$_}/g for keys %names;

另见Borodin的答案。他做了一个大的正则表达式而不是一个循环,并抓住了你们缺少第二个参数来拆分

您的主要问题是没有拆分$line变量。默认情况下,split/\t/splits$\u,并且您没有在其中放入任何内容

该程序构建散列,然后通过按长度降序排序并使用| regex alternation运算符连接所有键来构造正则表达式。排序是必要的,以便在有任何备选方案时选择所有可能选项中最长的一个

在输入文件的每一行中,正则表达式的每一次出现都会被相应的新名称替换,并且输出会写入新文件

use strict;
use warnings;

die "Usage: $0 hash_file gff_file\n" if @ARGV < 2;

my ($hashfile, $gfffile) = @ARGV;

open(my $hfile, '<', $hashfile) or die "Cannot open $hashfile: $!";
my %names;
while (my $line = <$hfile>) {
    chomp($line);
    my ($oldname, $newname) = split /\t/, $line;
    $names{$oldname} = $newname;
}
close $hfile;

my $regex = join '|', sort { length $b <=> length $a } keys %names;
$regex = qr/$regex/;

open(my $gfile, '<', $gfffile) or die "Cannot open $gfffile: $!";
open(my $out, '>', 'SFrenamed.gff') or die "Cannot open SFrenamed.gff: $!";

while (my $line = <$gfile>) {
    chomp($line);
    $line =~ s/($regex)/$names{$1}/g;
    print $out $line, "\n";
}

close $out;
close $gfile;

您的主要问题是没有拆分$line变量。默认情况下,split/\t/splits$\u,并且您没有在其中放入任何内容

该程序构建散列,然后通过按长度降序排序并使用| regex alternation运算符连接所有键来构造正则表达式。排序是必要的,以便在有任何备选方案时选择所有可能选项中最长的一个

在输入文件的每一行中,正则表达式的每一次出现都会被相应的新名称替换,并且输出会写入新文件

use strict;
use warnings;

die "Usage: $0 hash_file gff_file\n" if @ARGV < 2;

my ($hashfile, $gfffile) = @ARGV;

open(my $hfile, '<', $hashfile) or die "Cannot open $hashfile: $!";
my %names;
while (my $line = <$hfile>) {
    chomp($line);
    my ($oldname, $newname) = split /\t/, $line;
    $names{$oldname} = $newname;
}
close $hfile;

my $regex = join '|', sort { length $b <=> length $a } keys %names;
$regex = qr/$regex/;

open(my $gfile, '<', $gfffile) or die "Cannot open $gfffile: $!";
open(my $out, '>', 'SFrenamed.gff') or die "Cannot open SFrenamed.gff: $!";

while (my $line = <$gfile>) {
    chomp($line);
    $line =~ s/($regex)/$names{$1}/g;
    print $out $line, "\n";
}

close $out;
close $gfile;

非常感谢您指出这一点并加以纠正!我想我甚至没有想到排序部分,现在拆分部分有意义了…看起来我还有很长的路要学,但我会坚持下去。再次感谢。很抱歉再次询问,因为上面的脚本不起作用,正在给我原始gff文件,删除了某些行,我不确定发生了什么…可能是因为我没有足够的澄清。我想用相应的SFXXXXX替换ID=和Name=后面的零件,而不是“maker-scaffold00013-augustus-gene-2.187”值。因此,在每一行中可以有多个替换项,也可以有多个具有相同键的行,并且只要键的整个长度可以匹配,所有键和值都是唯一的。@user2278592:那么我怀疑您的hash_文件不是以制表符分隔的。您在问题中发布的数据中没有选项卡。尝试将/\t/替换为“”,看看是否适合您。如果您使用空格作为分隔符,那么名称中不能有空格,可以吗?是的,很抱歉我没有检查,这是我的问题!。。真尴尬…谢谢你!非常感谢您指出这一点并加以纠正!我想我甚至没有想到排序部分,现在拆分部分有意义了…看起来我还有很长的路要学,但我会坚持下去。再次感谢。很抱歉再次询问,因为上面的脚本不起作用,正在给我原始gff文件,删除了某些行,我不确定发生了什么…可能是因为我没有足够的澄清。我想用相应的SFXXXXX替换ID=和Name=后面的零件,而不是“maker-scaffold00013-augustus-gene-2.187”值。因此,在每一行中可以有多个替换,也可以有多个替换
具有相同键的行以及所有键和值都是唯一的,只要键的整个长度可以匹配。@user2278592:那么我怀疑您的哈希_文件没有用制表符分隔。您在问题中发布的数据中没有选项卡。尝试将/\t/替换为“”,看看是否适合您。如果您使用空格作为分隔符,那么名称中不能有空格,可以吗?是的,很抱歉我没有检查,这是我的问题!。。真尴尬…谢谢你!老实说,我不知道eval,但我在某处看到了一些帖子,并误以为这就是我应该做的…但我现在明白了,我不需要它,谢谢你指出它!如何在第二个while循环中重新定义$oldname和$newname,或者对于键%names来说$line2=~s/$\uu/$names{$\u}/g不需要这样做?对于键%names,键%names将遍历%names的散列键,对每个键执行$line2=~s/$\u/$names{$\u}/g一次,其中$\uu循环变量包含键,当然还有$names{$\u}是对应的值。由于关键字my,$oldname和$newname超出了块末尾最近右大括号的范围。首先,您使用$oldname和$newname并将它们存储在%names散列中$oldname作为键,$newname作为值,这样就不再需要它们了。您的所有数据都在%names散列中。感谢您的澄清。由于某些原因,它仍然不起作用,现在它给我的行中的关键是没有更换,问题是所有的行都被重新洗牌。我需要在“;”处拆分$line2吗因为我的钥匙两侧都嵌入了字符,并且没有空格分隔?如果我的描述不清楚,请告诉我。在您的示例中,密钥将包含诸如genemark-SCAFFOLD0013-abinit-gene-0.18之类的字符串。第二个while循环将查找genemark-scaffold00013-abinit-gene-0.18,并将其替换为SF130001。这就是你想要的吗?老实说,我不知道eval,但我在某处看到了一些帖子,并误以为那是我应该做的…但我现在明白了,我不需要它,谢谢你指出!如何在第二个while循环中重新定义$oldname和$newname,或者对于键%names来说$line2=~s/$\uu/$names{$\u}/g不需要这样做?对于键%names,键%names将遍历%names的散列键,对每个键执行$line2=~s/$\u/$names{$\u}/g一次,其中$\uu循环变量包含键,当然还有$names{$\u}是对应的值。由于关键字my,$oldname和$newname超出了块末尾最近右大括号的范围。首先,您使用$oldname和$newname并将它们存储在%names散列中$oldname作为键,$newname作为值,这样就不再需要它们了。您的所有数据都在%names散列中。感谢您的澄清。由于某些原因,它仍然不起作用,现在它给我的行中的关键是没有更换,问题是所有的行都被重新洗牌。我需要在“;”处拆分$line2吗因为我的钥匙两侧都嵌入了字符,并且没有空格分隔?如果我的描述不清楚,请告诉我。在您的示例中,密钥将包含诸如genemark-SCAFFOLD0013-abinit-gene-0.18之类的字符串。第二个while循环将查找genemark-scaffold00013-abinit-gene-0.18,并将其替换为SF130001。这就是你想要的吗?