Сравнение имен файлов и определение их инкрементных цифр - PullRequest
0 голосов
/ 27 августа 2018

Представьте, что у меня есть последовательность файлов, например ::100100

...
segment8_400_av.ts
segment9_400_av.ts
segment10_400_av.ts
segment11_400_av.ts
segment12_400_av.ts
...

Когда имена файлов известны, я могу сопоставить их с регулярным выражением, например:

/segment(\d+)_400_av\.ts/

Потому что я знаю пошаговый шаблон.

Но каков общий подход к этому? Я имею в виду, как я могу взять два имени файла из списка, сравнить их и выяснить, где в имени файла находится подсчитывающая часть, учитывая любые другие цифры, которые могут встречаться в имени файла (в данном случае 400)

Цель: То, что я хочу сделать, - это запустить сценарий для различных последовательностей файлов, чтобы проверить, например, отсутствующие файлы, поэтому это должен быть первый шаг, чтобы выяснить схему нумерации. Последовательности файлов могут встречаться в разных модах, например ::

test_1.jpg (simple counting suffix)
test_2.jpg
...

или

segment9_400_av.ts  (counting part inbetween, with other static digits)
segment10_400_av.ts
...

или

01_trees_00008.dpx  (padded with zeros)
01_trees_00009.dpx
01_trees_00010.dpx

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

  1. Узнайте, , если - это пронумерованная последовательность файлов, с правилами ниже
  2. Получить первый номер файла, получить последний номер файла и количество файлов
  3. Обнаружение отсутствующих файлов (пропуски в последовательности)

Правила:

  • Как суммировал Мельпомена в своем ответе, имена файлов отличаются только одной подстрокой, которая состоит только из цифр
  • Счетные цифры могут встречаться в любом месте имени файла
  • Цифры могут быть дополнены 0 (см. Пример выше)

Я могу сделать № 2 и № 3, с чем я борюсь, это # ​​1 в качестве отправной точки.

Ответы [ 4 ]

0 голосов
/ 27 августа 2018

Вы отметили этот вопрос regex, поэтому вот решение на основе регулярных выражений:

use strict;
use warnings;

my $name1 = 'segment12_400_av.ts';
my $name2 = 'segment10_400_av.ts';

if (
    "$name1\0$name2" =~ m{
        \A
        ( \D*+ (?: \d++ \D++ )* )  # prefix
        ( \d++ )                   # numeric segment 1
        ( [^\0]* )                 # suffix
        \0                         # separator
        \1                         # prefix
        ( \d++ )                   # numeric segment 2
        \3                         # suffix
        \z
    }xa
) {
    print <<_EOT_;
Result of comparing "$name1" and "$name2"
Common prefix: $1
Common suffix: $3
Varying numeric parts: $2 / $4
Position of varying numeric part: $-[2]
_EOT_
}

Выход:

Result of comparing "segment12_400_av.ts" and "segment10_400_av.ts"
Common prefix: segment
Common suffix: _400_av.ts
Varying numeric parts: 12 / 10
Position of varying numeric part: 7

Предполагается, что

  • строки отличаются (защитите условие с помощью $name1 ne $name2 && ..., если это не гарантировано)
  • есть только одна подстрока, которая отличается между входными строками (в противном случае она не найдет совпадений)
  • отличающаяся подстрока состоит только из цифр
  • все цифры, окружающие первую точку различия, являются частью изменяющегося приращения (например, в приведенном выше примере распознается segment в качестве общего префикса, а не segment1)

Идея состоит в том, чтобы объединить два имени в одну строку (разделенную NUL, что недвусмысленно, поскольку имена файлов не могут содержать \0), а затем позволить механизму регулярных выражений выполнить тяжелую работу по поиску самого длинного общего префикса ( используя жадность и прослеживание).

Поскольку мы в регулярном выражении, мы можем получить немного больше фантазии, чем просто найти самый длинный общий префикс: мы можем убедиться, что префикс не заканчивается цифрой (см. segment1 против segment случай выше), и мы можем проверить, что суффикс также тот же.

0 голосов
/ 27 августа 2018

Посмотрите, работает ли это для вас:

use strict;
use warnings;

sub compare {
    my ( $f1, $f2 ) = @_;

    my @f1 = split /(\d+)/sxm, $f1;
    my @f2 = split /(\d+)/sxm, $f2;

    my $i    = 0;
    my $out1 = q{};
    my $out2 = q{};
    foreach my $p (@f1) {
        if ( $p eq $f2[$i] ) {
            $out1 .= $p;
            $out2 .= $p;
        }
        else {
            $out1 .= sprintf ' ((%s)) ', $p;
            $out2 .= sprintf ' ((%s)) ', $f2[$i];
        }
        $i++;
    }

    print $out1 . "\n";
    print $out2 . "\n";
    return;
}

print "Test1:\n";
compare( 'segment8_400_av.ts', 'segment9_400_av.ts' );

print "\n\nTest2:\n";
compare( 'segment999_8_400_av.ts', 'segment999_9_400_av.ts' );

Вы в основном разбиваете строки, начиная / заканчивая цифрами, просматривая элементы и сравнивая каждую из «частей».Если они равны, вы накапливаете.Если нет, то вы выделяете различия и накапливаете.

Вывод (я использую ((число)) для выделения)

Test1:
segment ((8)) _400_av.ts
segment ((9)) _400_av.ts


Test2:
segment999_ ((8)) _400_av.ts
segment999_ ((9)) _400_av.ts
0 голосов
/ 27 августа 2018

Я предполагаю, что только счетчик отличается между строками

use warnings;
use strict;
use feature 'say';

my ($fn1, $fn2) = ('segment8_400_av.ts', 'segment12_400_av.ts');

# Collect all numbers from all strings    
my @nums = map { [ /([0-9]+)/g ] } ($fn1, $fn2);

my ($n, $pos);  # which number in the string, at what position

# Find which differ
NUMS: 
for my $j (1..$#nums) {                           # strings
    for my $i (0..$#{$nums[0]}) {                 # numbers in a string
        if ($nums[$j]->[$i] != $nums[0]->[$i]) {  # it is i-th number
            $n = $i;
            $fn1 =~ /($nums[0]->[$i])/g;          # to find position
            $pos = $-[$i];
            say "It is $i-th number in a string. Position: $pos";
            last NUMS;
        }
    }
}

Мы зациклились на массиве с номерами массивов, найденными в каждой строке, и на элементах каждого массива (например, [8, 400]). Каждое число в строке (0-е или 1-е или ...) сравнивается с его аналогом в 0-й строке (элемент массива); все остальные числа одинаковы.

Интересующий номер - тот, который отличается, и мы записываем, какое число в строке это ($n -й).

Затем ее положение в строке определяется путем ее повторного сопоставления и использования @- регулярного выражения с (только что установленным) индексом $n, поэтому смещение начала n-го матч. Эта часть может быть ненужной; Хотя редактирование вопросов помогло, я все еще не уверен, может ли эта позиция быть полезной или нет.

Печать с подсчетом позиции от 0

It is 0-th number in a string. Position: 7

Обратите внимание, что, как только будет обнаружено, что это $i -ое число, мы не можем использовать index, чтобы найти его позицию; число ранее в строках может совпадать с $i -ым в этой строке.

Для проверки измените входные строки, добавив к каждой из них один и тот же номер перед интересующим.


Для обновления вопроса, чтобы изучить последовательность (например, для отсутствующих файлов), с помощью приведенных выше выводов вы можете собрать счетчики для всех строк в массиве с помощью hashrefs (num => filename)

use Data::Dump qw(dd);

my @seq =  map { { $num[$_]->[$n] => $fnames[$_] } } 0..$#fnames;

dd \@seq;

, где @fnames содержит имена файлов (например, два, выбранные для примера выше, $fn1 и $fn2). Это предполагает, что список файлов был отсортирован для начала или добавлен сортировка, если это не было

my @seq =  
    sort { (keys %$a)[0] <=> (keys %$b)[0] }
    map { { $num[$_]->[$n] => $fnames[$_] } } 
    0..$#fnames;

Порядок поддерживается массивом.

Добавление этого к приведенному выше примеру (с двумя строками) добавляет к печати

[
  { 8 => "segment8_400_av.ts" },
  { 12 => "segment12_400_av.ts" },
]

При этом все цели в " Edit 2 " должны быть простыми.

0 голосов
/ 27 августа 2018

Я предлагаю создать шаблон регулярных выражений, изменив все последовательности цифр на (\d+), а затем посмотреть, какие захваченные значения изменились

Например, с segment8_400_av.ts и segment9_400_av.ts вы сгенерируете шаблон/segment(\d+)_(\d+)_av\.ts/.Обратите внимание, что s/\d+/(\d+)/g вернет количество числовых полей, которое вам понадобится для последующей проверки

Первое будет содержать 8 и 400, а второе - 9 и 400,8 отличается от 9, так что именно в той области строки, где число меняется

Я не могу написать много кода, так как вы не говорите, какого результата вы хотите от этого процесса

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...