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

Hello.

Preamble. I had a problem to find hash keys, which were written in string immediate after a substring Value: and separated by dots. Keys could be zero-length. After I found a substring which contains keys I tried to substitute poins by '}{' and surround all that line with $hash{' and '}, and then made 'eval'. For example if string was Value:., the answer is eval $hash{''}{''}

Main. And I experienced a problem when the line which contain keys equals to ''. It doesn't splits by '\.', and split returns nothing. And adding third parameter for split '-1' doesn't change behaviour.
So it means that there isn't a rule that the number of pieces gained by 'split' equals to one plus number of split fields found in string (pieces != 1 + fields). Therefore 'join' has a same opposite behaviour ( (join '', @strings) eq split 'field', join 'field', @strings), because if @strings = (''), then 0 == join 'field', @strings.

How to overcome that problem?
I used not elegant way by adding field and some character to a string, then splitting that string by field, and popping out last element:
$keys = ''; $keys .= 'field' . 'A'; @keys = split /field/, $keys, -1; # grabs trailing zero-lengths pop @keys;
Any better solutions?
use warnings; use strict; my @str = ( 'Value:', 'Value:.', 'Value:...', 'Value:A', 'Value:A.B', 'Value:.A', 'Value:A.', ); for (@str){ m/^ Value: \K (.*) $/x; my $keys = $1; my @keys; my $i = 0; @{ $keys[ $i ++ ] } = split /\./, $keys; @{ $keys[ $i ++ ] } = split /\./, $keys, -1; @{ $keys[ $i ++ ] } = $keys =~ m/\w*/g; @{ $keys[ $i ++ ] } = $keys =~ m/\w*?(?=\.|$)/g; my $keys_plus = $keys . '.A'; @{ $keys[ $i ++ ] } = split /\./, $keys_plus, -1; pop @{ $keys[ $i - 1 ] }; print "{$keys}:\n"; for my $j (0 .. $i - 1){ print map "<$_>\n", join '', map "[$_]", @{ $keys[ $j ] }; } print "\n"; } __END__ {}: <> <> <[]> <[]> <[]> {.}: <> <[][]> <[][]> <[][]> <[][]> {...}: <> <[][][][]> <[][][][]> <[][][][]> <[][][][]> {A}: <[A]> <[A]> <[A][]> <[A][]> <[A]> {A.B}: <[A][B]> <[A][B]> <[A][][B][]> <[A][][B][]> <[A][B]> {.A}: <[][A]> <[][A]> <[][A][]> <[][A][]> <[][A]> {A.}: <[A]> <[A][]> <[A][][]> <[A][][]> <[A][]>
... (0, 1) === map { scalar split /a/ } '', 'd'

Replies are listed 'Best First'.
Re: 1 != split /a/, '' ?
by choroba (Cardinal) on Nov 03, 2015 at 10:44 UTC
    If I understand you correctly, the only problematic case for the split with -1 is the empty one. You can solve it by
    push @keys, [ split /\./, $key_string, -1 ]; $keys[-1] = [ q() ] unless @{ $keys[-1] };

    I'm not sure it's more elegant than yours (elegance is in the eye of the beholder). In your solution, you can use just '.', the A is not needed.

    Note that I used a different names for the array and scalar.

    I don't understand the preamble. Do you need Data::Diver?

    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
      [ length($key_string) ? split(/\./, $key_string, -1) : '' ]
        what is the -1 in split meaning?
Re: 1 != split /a/, '' ?
by AnomalousMonk (Archbishop) on Nov 03, 2015 at 11:02 UTC

    I'm also not sure I entirely understand the problem, but here's another possible (?) approach:

    c:\@Work\Perl\monks>perl -wMstrict -le "use 5.010; ;; use warnings; use strict; ;; use Test::More 'no_plan'; use Test::NoWarnings; ;; my @vectors = ( [ 'Value:', '[]' ], [ 'Value:.', '[][]' ], [ 'Value:...', '[][][][]' ], [ 'Value:A', '[A]' ], [ 'Value:A.B', '[A][B]' ], [ 'Value:.A', '[][A]' ], [ 'Value:A.', '[A][]' ], ); ;; VECTOR: for my $ar_vector (@vectors) { my ($str, $expected) = @$ar_vector; ;; my $converted = join '', map qq{[$_]}, $str =~ m{ (?: Value: | [.]) \K [^.]* (?= [.] | \z) }xmsg; ;; is $converted, $expected, qq{'$str' -> '$converted'} } " ok 1 - 'Value:' -> '[]' ok 2 - 'Value:.' -> '[][]' ok 3 - 'Value:...' -> '[][][][]' ok 4 - 'Value:A' -> '[A]' ok 5 - 'Value:A.B' -> '[A][B]' ok 6 - 'Value:.A' -> '[][A]' ok 7 - 'Value:A.' -> '[A][]' ok 8 - no warnings 1..8


    Give a man a fish:  <%-{-{-{-<

Re: 1 != split /a/, '' ?
by jientho (Initiate) on Apr 18, 2016 at 16:01 UTC

    I had a similar "problem" with Perl split at about the same time last year. My solution, which doesn't require two references to the target ($keys) as some other solutions do (but does need two references to the result):

    @result = split /\./, $keys, -1 or push @result, "";