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

Hi Monks,

I am using  $string =~ s/^\s*$//mg; to match & remove white-spaces from a string. The current code is matching spaces, tabs and newlines, but I don't want to match newlines, I just want to match all the other white-spaces except newlines. I want to exclude the newlines from this reg-ex. How can I do this? Can any monk show me a way to do it?

Thanks in advance.

Replies are listed 'Best First'.
Re: regex doubt on excluding
by hdb (Monsignor) on Apr 21, 2014 at 08:40 UTC

    A question on whitespace is difficult to address as one cannot see the objects of interest. Your regex suggests that in a multiline string you want to replace lines consisting only of whitespaces with truly empty lines. Whitespaces in non-empty lines will be preserved. I am replacing the whitespace with 'x' to see where we got matches:

    use strict; use warnings; my $string = "next line is spaces next line is tabs and now some newlines end"; $string =~ s/^\s*$/x/mg; print "$string\n";
    The result of this would be:
    next line is spaces x next line is tabs x and now some newlines xx end
    When you say you want to exclude newlines from the match, what is the desired effect you want to see? Preserve multiple empty lines?
      Yes, that's it, Preserve multiple empty lines. (but without white-spaces)

        How about this?

        $string =~ s/^\s*?\n$/\n/mg;

        UPDATE: the regex is not working properly but this should, the \n and the $ are somewhat duplicate:

        $string =~ s/^\s*?\n/\n/mg;
Re: regex doubt on excluding
by Laurent_R (Canon) on Apr 21, 2014 at 08:31 UTC
    You could build your own character class. Something like:
    [ \t]
    Add whatever else you need to it.

      Thank You Laurent.

      So it should be something like this,

      $string =~ s/^[ \t]$//mg;

      but that code is not working for me!

        I am not sure what exactly you are trying to do, but, with the start and end of line anchors, your regex will match only lines which have only exactly one character of the character class and nothing else. You may want to have at least have something like:
        $string =~ s/^[ \t]+$//mg;
        Or maybe you don't want the ^ and $ anchors. Or maybe you don't want the multi-line m modifier. Please explain more precisely what you want to achieve, and give an example input where it does not work the way you wish, together with the output that you are looking for.
Re: regex doubt on excluding
by Athanasius (Archbishop) on Apr 21, 2014 at 08:41 UTC

    Sorry, but the code shown is not removing newlines, as is easily demonstrated:

    18:23 >perl -wE "my $s = qq[abc\n\t \ndef\n \ngh]; $s =~ s/^\s*$/X/m +g; say $s;" abc X def X gh 18:24 >

    That’s because the /m modifier lets ^ and $ match at the beginning and end of each line within the string (see perlre#Modifiers). What the code does is to remove any whitespace from an otherwise empty line, i.e. whitespace is removed if and only if the whitespace is the only thing between two newlines (or between the beginning of the string and the first newline, or between the last newline and the end of the string). Is this what was intended? Or were you wanting to remove all whitespace except newlines themselves? If the latter, Laurent_R’s approach is what you want:

    18:24 >perl -wE "my $s = qq[abc\n\t \nd\tef\n \ngh]; $s =~ s/[ \t]+/ +X/mg; say $s;" abc X dXef X gh 18:39 >

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Preserve multiple empty lines. (but without white-spaces)

        Ok, I understand now, and it seems I spoke too soon: the original code is removing some newlines, since it reduces a sequence of successive newlines to a single one.

        I don’t understand how this is working. From perlre#Regular-Expressions:

        By default, the "^" character is guaranteed to match only the beginning of the string, the "$" character only the end (or before the newline at the end), and Perl does certain optimizations with the assumption that the string contains only one line. Embedded newlines will not be matched by "^" or "$". You may, however, wish to treat a string as a multi-line buffer, such that the "^" will match after any newline within the string (except if the newline is the last character in the string), and "$" will match before any newline. At the cost of a little more overhead, you can do this by using the /m modifier on the pattern match operator.

        — but I don’t see how this explains the behaviour we are seeing?

        Can someone please explain what the regex is doing here?

        Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Re: regex doubt on excluding
by jellisii2 (Hermit) on Apr 21, 2014 at 11:27 UTC

    The "big hammer" approach says replace all of those characters with a newline.

    s/^\s*$/\n/mg;

    I'm a fan of the big hammer approach, but as you'd expect subtlety isn't exactly my strong point.

Re: regex doubt on excluding
by kcott (Archbishop) on Apr 22, 2014 at 02:31 UTC

    If you look in "perlre: Metacharacters", you'll see that '$' matches "the end of the line (or before newline at the end)" (emphasis added). So, what you really want is to match any whitespace character except if it follows '$'. That makes your substitution "s/(?<!$)\s//gm", shown here:

    #!/usr/bin/env perl -l use strict; use warnings; my $string = "\t is a TAB\n is a SPACE\nTAB\t and SPACE"; print '*** BEFORE ***'; print $string; $string =~ s/(?<!$)\s//gm; print '*** AFTER ***'; print $string;

    Output:

    *** BEFORE *** is a TAB is a SPACE TAB and SPACE *** AFTER *** isaTAB isaSPACE TABandSPACE

    However, if you know that you only want to match the whitespace characters space (" ") and tab ("\t"), then the transliteration "y/\t //d" will be faster. (See "Search and replace or tr" in "Perl Performance and Optimization Techniques" for a Benchmark example.) As you can see, the code is virtually identical (which makes replacing the s/// with y/// easy):

    #!/usr/bin/env perl -l use strict; use warnings; my $string = "\t is a TAB\n is a SPACE\nTAB\t and SPACE"; print '*** BEFORE ***'; print $string; $string =~ y/\t //d; print '*** AFTER ***'; print $string;

    Output:

    *** BEFORE *** is a TAB is a SPACE TAB and SPACE *** AFTER *** isaTAB isaSPACE TABandSPACE

    [In case you didn't know, y/// and tr/// are synonymous. You'll find both forms used in different sections of the perlop documentation.]

    -- Ken

Re: regex doubt on excluding
by AnomalousMonk (Archbishop) on Apr 21, 2014 at 19:47 UTC

    The 'standard' way to construct a character class that excludes characters is  [^...].

    One way to define the class of all whitespace except a newline would be  [^\S\n]. (Note this the big-S  \S which is the inverse of  \s i.e.,  \S matches any character that is not whitespace.) So this is a kind of double-negative. You might read it as "the class of all characters that are not non-whitespace and also not a newline."