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

I'm trying to come up with some substitutions that will replace escape sequences with their single-character equivalents, e.g., turning "\\n" into "\n" (a single newline character), along with all the other Perl escape sequences. Here's my current guess.

warn 'tab' unless unescape("\\t") eq "\t"; #works warn 'nl' unless unescape("\\n") eq "\n"; #fails warn 'bel' unless unescape("\\07") eq "\07"; #fails sub unescape { my($s)=@_; $s =~ s/(\\[^\d])/$1/ge; #"\t", "\n", etc. $s =~ s/(\\0\d+)/$1/ge; #"\07", etc. return $s; }

I'm trying to let users type these sequences in input files and interpret them as Perl would within my code. The Perl and PM FAQs have similar questions, but none that solved this for me.

Thanks,

Jim

Replies are listed 'Best First'.
Re: Unable To Replace Escape Sequences With 1-Char Equivalents
by ikegami (Patriarch) on Oct 20, 2004 at 01:50 UTC

    \n is only converted to newline when it's seen in a string literal by the perl compiler. The compiler doesn't see \n in your example, just $1.

    The following makes the compiler see it (by compiling the match everytime one is found):

    warn 'tab' if unescape("\\t") eq "\t"; warn 'nl' if unescape("\\n") eq "\n"; warn 'bel' if unescape("\\07") eq "\07"; sub unescape { my($s)=@_; $s =~ s/(\\[^\d])/ (eval "\"$1\"") || $1 /ge; # "\t", "\n", etc $s =~ s/\\(0\d+)/ eval "chr($1)" /ge; # "\07", etc. return $s; }

    It's more efficient to do the logic yourself:

    my %SLASHED = ( t => "\t", n => "\n", r => "\r", ); warn 'tab' if unescape("\\t") eq "\t"; warn 'nl' if unescape("\\n") eq "\n"; warn 'bel' if unescape("\\07") eq "\07"; sub unescape { my($s)=@_; $s =~ s/\\([^\d])/ $SLASHED{$1} || $1 /ge; # "\t", "\n", etc. $s =~ s/\\0(\d+)/ chr(oct($1)) /ge; # "\07", etc. return $s; }

    Both solutions are untested.

    Update: Tested.

Re: Unable To Replace Escape Sequences With 1-Char Equivalents
by etcshadow (Priest) on Oct 20, 2004 at 02:32 UTC
    What you mean to be doing with your unescape routine is actualy:
    sub unescape { my($s)=@_; $s =~ s/(\\[^\d])/"\"$1\""/gee; #"\t", "\n", etc. $s =~ s/(\\0\d+)/"\"$1\""/gee; #"\07", etc. return $s; }

    That is: implicitly evoking an eval with a second /e modifier. Also, you could really do both regexes in one regex (and, fyi: [^\d] is also called, more simply, \D):

    sub unescape { my($s)=@_; $s =~ s/\\(0\d+|\D)/"\"\\$1\""/gee; return $s; }
    ------------ :Wq Not an editor command: Wq
Re: Unable To Replace Escape Sequences With 1-Char Equivalents
by simonm (Vicar) on Oct 20, 2004 at 05:07 UTC
    I've provided this function in my String::Escape module.
    use String::Escape qw( unprintable ); print unprintable( "\\n" );