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

Esteemed Monks,

I am writing a quick script to analyze a bunch of files using substr and sometimes I see "substr outside of string." If this happens once, I'd like to skip the entire file. This is where I am having trouble.

Ideally, I would like to see this:

Checking file_a...
Checking file_b...
Skipping file_c... (due to the substr message)

Currently I see this:

Checking file_a...
Checking file_b...
substr outside of string at ./script_name.pl line 35, <$fh> line 1.
substr outside of string at ./script_name.pl line 35, <$fh> line 2.
substr outside of string at ./script_name.pl line 35, <$fh> line 3.
(and so on until the end of the file...)

My first approach was using an eval block with $@ until I discovered (through perldiag) that the substr message is a warning, not an error. The only way I recall being able to handle warnings is via %SIG--is this correct?

I then tried using $SIG{__WARN__} and I was able to suppress the warning; however, I cannot get it to skip the rest of the file.

The relevant code looks like this:
use warnings; use strict; use File::Find; # $data_dir is determined. $SIG{__WARN__} = sub { print "Skipping...\n"; # skip the rest of the loop for the file. } sub find_process { # open each file that meets the naming criteria. # begin looping through the file. # attempt to access the substr. # skip this file if there was a substr warning and report it. } find(\&find_process, $data_dir);
Thank You.

Replies are listed 'Best First'.
Re: Advanced warning handling?
by ikegami (Patriarch) on Mar 23, 2006 at 18:18 UTC

    You can disable warnings at the file/function/block level:

    { no warnings 'substr'; ... use substr here ... }

    In the above snippet, substr will then return the empty string ("") without giving a warning when the start position is beyond the end of the string.

    Another way is to check the length of the string on which substr is called:

    my $substr; if (length($_) >= 4) { $substr = substr($_, 4, 3); } else { $substr = ''; }
Re: Advanced warning handling?
by diotalevi (Canon) on Mar 23, 2006 at 18:38 UTC

    Set a flag when the condition occurs and check for it.

    our $SKIP_REST_OF_FILE; sub trap_substr_warning { my $e = $@; if ( $e =~ /whatever/ ) { $SKIP_REST_OF_WARNING = 1; } else { print STDERR $e; } } sub find_process { local $SKIP_REST_OF_FILE = 0; local $SIG{__WARN__} = \ &trap_substr_warning; ... if ( $SKIP_REST_OF_FILE ) { ... } }

    ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

Re: Advanced warning handling?
by acid06 (Friar) on Mar 23, 2006 at 18:52 UTC
    Well, you could:
    use warnings FATAL => 'substr'; ... eval { substr($str, $really_big_number) }; if ($@) { # an exception is now thrown }
    Note that I'd consider this as being sloppy programming. I think you shouldn't pass wrong arguments to substr(). Instead of worrying about how to trap the condition, you should prevent it from happening.


    acid06
    perl -e "print pack('h*', 16369646), scalar reverse $="
Re: Advanced warning handling?
by eff_i_g (Curate) on Mar 23, 2006 at 20:06 UTC
    Thanks everyone--it makes sense. The way I was approaching the problem can certainly be improved, however, I'm glad I learned some new ideas.