假设我有一系列文件,例如:
...
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:也许我的问题可以描述得更简单:对于给定的文件集,我想要:
规则:
我可以做#2和#3,而我一直在努力以#1为起点。
答案 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.ts
和
segment9_400_av.ts
,您将生成模式/segment(\d+)_(\d+)_av\.ts/
。请注意,s/\d+/(\d+)/g
将返回数字字段的数量,您需要进行后续检查
第一个将捕获8
和400
,第二个将捕获9
和400
。 8
与9不同,因此它是字符串中数字变化的区域
我不能真正编写太多代码,因为您没有说希望从此过程中获得什么样的结果