比较文件名并确定其增量数字

时间:2018-08-27 07:53:13

标签: regex perl

假设我有一系列文件,例如:

...
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. 检测丢失的文件(序列中的空白)

规则:

  • 正如他在回答中所提到的melpomene一样,文件名仅在一个仅包含数字的子字符串中有所不同
  • 计数数字可以出现在文件名中的任何地方
  • 数字可以用0填充(请参见上面的示例)

我可以做#2和#3,而我一直在努力以#1为起点。

4 个答案:

答案 0 :(得分:1)

看看这是否对您有用:

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' );

您基本上是通过以数字开头/结尾来分割字符串,循环遍历项目,然后比较每个“片断”。如果它们相等,则您会积累。如果不是,则突出显示差异并累积。

输出(我在突出显示中使用[[number]])

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


Test2:
segment999_ ((8)) _400_av.ts
segment999_ ((9)) _400_av.ts

答案 1 :(得分:1)

我假设只有计数器在字符串之间有所不同

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;
        }
    }
}

我们使用在每个字符串中找到的数字的arrayrefs遍历数组,并遍历每个arrayref的元素(例如[8, 400])。将字符串中的每个数字(第0或1st或...)与第0个字符串(数组元素)中的对应数字进行比较;其他所有数字都相同。

感兴趣的数字是一个不同的数字,我们在字符串中记录该数字是第($n个数字。

然后通过再次匹配它并使用@- regex variable与(刚建立的)索引$n来找到它在字符串中的位置,因此第n个匹配开始的偏移量。这部分可能不需要。尽管问题编辑帮助了我,但我仍然不确定该职位是否有用。

打印,位置从0开始计数

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

请注意,一旦发现它是第$i个数字,我们就无法使用index来找到它的位置;字符串中较早的数字可能恰好与此字符串中第$i个数字相同。

要进行测试,请在感兴趣的字符串之前添加相同的数字,以修改输入字符串。


每次问题更新时,要检查序列(例如缺少文件的序列),根据上述发现,您可以为带有hashrefs(num =>文件名)的数组中的所有字符串收集计数器。

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" },
]

有了这一切,“ 编辑2 ”中的所有目标都应该简单明了。

答案 2 :(得分:1)

您标记了这个问题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,这是明确的),然后让regex引擎进行艰苦的工作来寻找最长的公共名称前缀(使用贪婪和回溯)。

因为我们使用的是正则表达式,所以比仅仅找到最长的公共前缀要花更多的钱:我们可以确保前缀不以数字结尾(请参见segment1 vs. segment情况),我们可以验证后缀是否相同。

答案 3 :(得分:0)

我建议您通过将所有数字序列更改为(\d+)来构建正则表达式模式,然后查看捕获的值已更改

例如,使用segment8_400_av.tssegment9_400_av.ts,您将生成模式/segment(\d+)_(\d+)_av\.ts/。请注意,s/\d+/(\d+)/g将返回数字字段的数量,您需要进行后续检查

第一个将捕获8400,第二个将捕获94008与9不同,因此它是字符串中数字变化的区域

我不能真正编写太多代码,因为您没有说希望从此过程中获得什么样的结果