2017-01-27 3 views
1

Я делаю небольшой скрипт perl, но у меня есть некоторые проблемы при чтении файла, а не в повторении по регулярному выражению.Perl File Reading и RegEx Matching

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

Это пример строки файла

  1A OCC OCC 4B 5B 6B 7B 8B 9A 
     OCC OCC 12B 13B 14B OCC 16B 17B 18B OCC OCC 

я должен соответствовать для первого, второго, п .., линия separetly: 1A 4B 5B 6B 7В ...

excecpt OCC.

я писал этот код:

my $path="file.txt"; 

open (my $fh, "<", $path); 

while(my $line = <$fh>) 
{ 
    for ($line =~/(\d{1,2}[A|B|C])/){ 
     print " $1"; 
} 
} 

Результат, который я получил только матч за первое вхождение согласованной на линии. 1A 12B

Как я могу расширять для чтения всю строку и правильно соответствовать контенту?

Результат печати предназначен только для моего теста отладки.

ответ

2

Матч, который вы написали, фиксирует раз и останавливается. Таким образом, цикл for превышает тот номер, который находится внутри (line =~ ...).

Вы можете вместо этого использовать /gмодификатор, который заставит регулярное выражение продолжить поиск и найти все совпадения. Если вы назначите, что массив, то оператор в контексте списка и возвращает все матчи

my @matches = $line =~ /\d{1,2}[A-C]/g; 

Здесь вам не нужно захваченной круглые скобки, так как вы берете на себя весь матч. Если есть сомнения, добавьте их. Если вам просто нужны номера, за которыми следуют любые буквы, вы можете вместо этого использовать /\d+\w+/g.

Я хотел бы сделать еще несколько комментариев.

  • Пожалуйста всегда начать свои программы с use warnings; и use strict;

  • Всегда, всегда обратные вызовы как open

В целом

use warnings 'all'; 
use strict; 
use feature qw(say); 

my $path="file.txt"; 

open my $fh, "<", $path or die "Can't open $path: $!"; 

while (my $line = <$fh>) 
{ 
    my @matches = $line =~ /(\d{1,2}[A-C])/g; 

    say "@matches"; 
} 

close $fh; 
2

Чтобы соответствовать всем вхождениям регулярного выражения, вам необходимо использовать модификатор /g.

Кроме того, поскольку аргумент for оценивается в контексте списка, он возвращает все совпадения сразу, поэтому использование $1 будет возвращать одно и то же значение (последнее) для каждого совпадения; но вы можете использовать переменную цикла вместо:

for ($line =~ /(\d{1,2}[ABC])/g) { 
    print " $_"; 
} 

Но, это общепринятая перебрать матчи с while вместо этого, так как она возвращает совпадающие части один за другим, без необходимости иметь длинный список соответствий. Здесь вам нужно $1, как условие цикла вычисляется в скалярном контексте:

while ($line =~ /(\d{1,2}[ABC])/g) { 
    print " $1"; 
} 

Примечание: Ваш вход не содержит |, так что я удалил его из класса символов.