Perl: использование grep для извлечения сопоставления с образцом подстроки в строке файла - PullRequest
2 голосов
/ 06 февраля 2012

У меня есть следующее содержимое в файле:

Г | 170570902 | гб | ABLA01000008.1 | 0,457 24 0,581 24 0,876 11 0,744 0,669 Y 0,450 SignalP-noTM

Для каждой строки в файле я хотел бы извлечь выделенное выше число и поместить его в массив. Я пытаюсь найти это число и извлечь его из согласованной строки, но, похоже, я не могу найти правильный способ сделать это.

Вот что я имею в виду:

while ($sec_gi = <IN_SIDS>){
    $sec_gi =~ s/[0-9]{5,}/$&/;
    print $sec_gi."\n";
}

$ & должно быть строкой с точным соответствием. При этом я получаю совпавшую линию, ЗА ИСКЛЮЧЕНИЕМ шаблона совпадения, который в точности противоположен тому, что я хочу.

Может ли кто-нибудь помочь?

Спасибо!

Ответы [ 7 ]

5 голосов
/ 06 февраля 2012

Похоже, split - самое простое решение (оптимизированное ETA):

while (<IN_SIDS>) {
    my $nums  = (split /\|/, $field, 3)[1];
    print "$nums\n";
    push @array, $nums;
}

Я сделал тест для сравнения эффективности с решением регулярных выражений:

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

my $data = "gi|170570902|gb|ABLA01000008.1| 0.457 24 0.581 24 0.876 11 0.744 0.669 Y 0.450 SignalP-noTM";

use Benchmark qw(cmpthese);

cmpthese(shift, {
        'Regex' => \&regex,
        'Split' => \&splitting
    });

sub regex {
    if ($data =~ /^[^|]+\|(\d{5,})\|/) {
        return $1;
    }
}

sub splitting {
    return (split /\|/, $data, 3)[1];
}

Результат - ничья:

tlp@ubuntu:~/perl$ perl tx.pl 1000000
           Rate Split Regex
Split 2083333/s    --   -2%
Regex 2127660/s    2%    --

Спасибо М42 за совет в комментариях. Я выбрал решение split для простоты и удобства обслуживания, а не производительности, но на данный момент оно равно решению регулярных выражений.

1 голос
/ 06 февраля 2012

Вы также можете просто

$sec_gi =~ /([0-9]{5,})/;</p> <p>print "$1\n";

0 голосов
/ 10 июля 2013

Если вам не нужно использовать grep, сработает следующая короткая программа.

Надеюсь, это поможет.

Кейтлин

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

my @array;

for ( <DATA> )
{
    push @array, $1 if /gi\|(\d+)\|/;
}

for (@array) {
    print "$_\n";
}

__DATA__
gi|170570902|gb|ABLA01000008.1| 0.457 24 0.581 24 0.876 11 0.744 0.669 Y 0.450 SignalP-noTM
gi|178370902|gb|ABLA01000008.1| 0.457 24 0.581 24 0.876 11 0.744 0.669 Y 0.450 SignalP-noTM
gi|170593502|gb|ABLA01000008.1| 0.457 24 0.581 24 0.876 11 0.744 0.669 Y 0.450 SignalP-noTM
gi|170578993|gb|ABLA01000008.1| 0.457 24 0.581 24 0.876 11 0.744 0.669 Y 0.450 SignalP-noTM
gi|170898368|gb|ABLA01000008.1| 0.457 24 0.581 24 0.876 11 0.744 0.669 Y 0.450 SignalP-noTM
0 голосов
/ 06 февраля 2012

Если значение всегда является вашим вторым полем, вы можете использовать это:

while ($sec_gi = <IN_SIDS>) {
  if ($sec_gi =~ m/^[^|]*\|([^|]+)/) {
    print "$1\n";
  }
}

Если некоторые из вторых полей не всегда те, которые вы хотите (т.е. вам нужно только 5 или более чисел, как подразумевается)) тогда вы могли бы быть более конкретным:

while ($sec_gi = <IN_SIDS>) {
  if ($sec_gi =~ m/^[^|]*\|(\d{5,})/) {
    print "$1\n";
  }
}

Если ваш Perl-скрипт выполняет ТОЛЬКО это, вы можете использовать gnu coreutil cut (man cut).

0 голосов
/ 06 февраля 2012

Могу также дать вам ответ № 3:

# Declare Array outside the loop
my @my_array;
while ( $sec_gi = <IN_SIDS> ){
    chomp $sec_gi;

    # Test if this field actually exists

    if ( $sec_gi =~ /([0-9]{5,})/ ) {

        # Field exists, push it into your array (or print it)\

        push @my_array, $1;
    }
    else {

        # Field doesn't exist: Take appropriate action (which might mean none)

        print "Field not found\n";
    }
}

# Array @my_array has all of your values

yadda, yadda, yadda

Кстати, это будет определять местонахождение поля независимо от того, где оно находится на линии. Если этот номер будет только в поле № 1, вы хотите использовать split:

my @my_array;
while ( $sec_gi = <IN_SIDS> ) {
    chomp $sec_gi;
    @sec_gi_array = split /\|/, $sec_gi;
    if ( $sec_gi_array[1] =! /[0-9]{5,}/ ) {
         push @my_array, $sec_gi_array[1];
    }
    else {
         print "Field not found\n";
    }
}
0 голосов
/ 06 февраля 2012

Вам необходимо указать группу захвата:

  while ($sec_gi = <IN_SIDS>){
     $sec_gi =~ s/^.*([0-9]{5,}).*$/$1/;
     print $sec_gi."\n";
 }
0 голосов
/ 06 февраля 2012

Вы можете использовать:

$sec_gi =~ s/.*?\|(\d{5,}).*/\1/;

Однако, если он всегда находится во 2-м столбце, вы можете использовать split:

@lst = split('\|', $sec_gi );
$sec_gi = $lst[1];
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...