bh_perl has asked for the wisdom of the Perl Monks concerning the following question:

Dear monks..

I have list of files with sequence number start from 1 until 9999. The file sequence number will be loop between 1 until 9999 and the file sequence will be restart to 1 after it reach the maximum values (9999).

My problem is i want to check any missing sequence between 1 until 9999. But, i have a problem once the file sequence number reach the maximum values and reset to 1. As example, this is file sequence number:-
9996
9998
9999
0001
0003
0005
so, my program will return 9997,0002 and 0004 for missing file sequence..

Hopefully, somebody could shared some ideas to solved my problem. This is my coding:-

sub check_sequence { my $switch_name = shift; my $tmp = 0; my $archive_dir = $dirlst{$switch_name}; opendir(DIR, "$archive_dir") or die ("Can't open $archive_dir\ +n"); foreach my $infile (readdir(DIR)) { next unless $infile =~ /NOK/; $tmp_seq = (split(/_/, $infile))[2]; ($seqno = substr($tmp_seq,2,4)) =~ s/^0//g; push (@file_lst, $seqno) if $seqno !~ /$tmp/; $tmp = $seqno; print "$seqno\n"; } closedir(DIR); @sortlst = sort { $a <=> $b } @file_lst; $curseq = $sortlst[0]; for (my $i =1; $i < @sortlst; $i++) { $nextseq = $sortlst[$i]; $curseq++; if ($curseq ne $nextseq) { while ($curseq ne $nextseq) { push(@missing, $commseq); print ("Found missing file sequence $c +urseq - $nextseq\n"); $curseq++; } } $curseq = $nextseq; } }

Replies are listed 'Best First'.
Re: How to check missing sequence between sequence range ?
by ELISHEVA (Prior) on Mar 30, 2009 at 10:36 UTC

    The first and foremost reason your script is failing is that you are never putting anything into @missing except undefined values. $commseq is never defined nor declared. There are also at least four other undeclared variables in your script @sortlst, @missing, $nextseq, $curseq. I'm guessing therefore you are not using strictures.

    It is very important that you include the following two lines at the start of every script and module:

    use strict; use warnings;

    These two lines will save you a world of trouble by quickly drawing your attention to potential bugs and problems. Had you done that, it would have been immediately clear to you that $commseq was undefined.

    I also note that you are using ne to compare numbers. Even though the numbers originally began a strings, Perl can happily convert those strings to numbers. If you want to compare two numbers as numbers (and I assume you do since your sort routine uses <=> ), you should use the numeric operators:  < <= == => > - see perlop for more information.

    As for the algorithm itself, if you have large gaps you will be printing out lists of numbers that will be so long as to be unusable. If you want to print out all the missing numbers (not just the places where only one is missing), then you might want to consider modifying your code to print out ranges of missing numbers rather than each number individually, like this:

    # if ($curseq ne $nextseq) { # while ($curseq ne $nextseq) { # push(@missing, $commseq); # print ("Found missing file sequence $curseq - $nextseq\n"); # $curseq++; # } # } # $curseq = $nextseq; # } if ($curseq < $nextseq) { my $endseq = $nextseq-1; if ($curseq == $endseq) { push @missing, $curseq; } else { push @missing, "$curseq-$endseq"; } } $curseq = $nextseq; } print "missing=<@missing>\n";

    Best, beth

    Update added observations other than strictures

Re: How to check missing sequence between sequence range ?
by drench (Beadle) on Mar 30, 2009 at 10:46 UTC

    If I understand what you're asking, you want to detect gaps in the sequence if you've only skipped one number; you don't care about gaps of 2 or more, like the one between 0005 and 9996.

    So for each number you know exists (call it 'n'), if n+2 (with "wraparound" after 9999 back to 0001) exists but n+1 does not, that's a "skip":

    my @skips = get_skips(qw(9996 9998 9999 0001 0003 0005)); print join(',', sort @skips) . "\n"; # 0002,0004,9997 sub next_seq { my $n = shift() + 1; if ($n > 9999) { $n = 1; } return sprintf '%04d', $n; } sub get_skips { my %s = map { $_, 1 } @_; my @r; for (keys %s) { my $next = next_seq($_); my $nextnext = next_seq($next); if (exists($s{$nextnext}) && ! exists($s{$next})) { push @r, $next; } } return @r; }

    I'll leave extracting the list of numbers from the filenames to you.

How to find missing values in a range of integers
by ig (Vicar) on Mar 31, 2009 at 02:15 UTC

    If you can use modules you might like the following solution:

    #!/usr/local/bin/perl # use strict; use warnings; use Set::IntSpan::Fast; my @sequence_numbers = map {int($_)} (<DATA>); my $set = Set::IntSpan::Fast->new(); $set->add_range(1,9999); $set->invert(); $set->add(@sequence_numbers); $set->invert(); print $set->as_string() . "\n"; __DATA__ 9996 9998 9999 0001 0003 0005

    Which produces:

    2,4,6-9995,9997