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

I am a newbie to Perl, and am trying to use a simple script to read from one file and write to another. The file I am reading from has two text strings that very in length (many iterations) and I think the problem lies in my regex as all I can get it to do is write "1" on two lines (minus the "") Can you help?

TIA

here is an example of the two text strings that vary in length

points: "_eglErdrm@}}vA|}vAihtB~}vAik{C|}vA}~mHnojBgzlBjy[aktAoeAooo@_l`@wl^y{oA",

levels: "PKLMOKNLP",

Here is my code so far..

open(FILEREAD, "< read.js") or die "Cannot open FILEREAD:$!"; open(FILEWRITE, "> write.js") or die "Cannot open FILEWRITE:$!"; while (<FILEREAD>){ $points = s//(points)+.,/; $levels = s//(levels)+.,/; print FILEWRITE "$points\n"; print FILEWRITE "$levels\n"; } close FILEWRITE; close FILEREAD;

Replies are listed 'Best First'.
Re: Regex question
by jwkrahn (Abbot) on Jul 04, 2008 at 15:21 UTC
    $points = s//(points)+.,/;

    Your regular expression is empty which means it will match at the first character boundary which is the beginning of the string and will substitute that with the text string "(points)+.," so if the current line in $_ contains qq[levels: "PKLMOKNLP",\n] after the substitution it will contain qq[(points)+.,levels: "PKLMOKNLP",\n].

    The substitution operator always returns either true (1) if the substitution succeeded or false ('') if the substitution failed and in your case the substitution will always succeed so $points will always be assigned the value 1.

      The substitution operator always returns either true (1) if the substitution succeeded
      Just to elaborate, from perlop:
      returns the number of substitutions made
      So, as in the case of the OP, s/// will return 1 if a substitution is made, but s///g can return more than 1 if many substitutions are made.
Re: Regex question
by graff (Chancellor) on Jul 04, 2008 at 15:23 UTC
    There are a few points you need to learn about the s/// operator:
    • The first part (right after "s/") is called the pattern -- that is what you expect to match in the input string.
    • The second part (after "s/pattern/") is called the replacement -- that is what gets put in place of the pattern.
    • The value returned by "s/pattern/replace/", is always "1" for true if the pattern matched and was replaced, and "0" for false if it didn't match.

    You gave a couple examples of input, but it's not at all clear what you wanted for output. Could you explain a little more about that?

      Thanks for all your help so far, really appreciated.

      I need to clarify a little.....

      There are many iterations (hundreds) of points and levels that I need to write to another file, and they are all different, for example;

      points: "{lbzAb{pnCedn@_t{S",
      levels: "PP",

      points: "abetBivqgFwtcOv `Ayspb@lhiVopoFxzmEysmBl|s@gv`Bd`M",
      levels: "POMMLP",

      I need a way of copying the lines containing the values for points and levels (however long the strings are) to the new file. I am now thinking that the substitution operator is not the right one.

      My output is therefore the same as the input, in that the script searches the input file for all iterations of:

      points: "variable_length_string_containing_any_characters",
      levels: "variable_length_string_containing_any_characters",
      And simply writes this to another file..Do I need a loop to achieve this?

        Solved it! Thanks pc88mxer for your help with the code.
        The bit of code you posted solved my problem,
        Thanks again
Re: Regex question
by pc88mxer (Vicar) on Jul 04, 2008 at 15:11 UTC
    Inside your loop you want something like:
    if (m/points:(.*)/) { $points = $1; # do something with $points? } elsif (m/levels:(.*)/) { $levels = $1; # do something with $levels? }
Re: Regex question
by olus (Curate) on Jul 04, 2008 at 15:22 UTC

    There are a few issues with the code you showed us. You are reading the file line by line, so you do not want to check for points and levels the way you are doing, and write two lines for each line you read. Also, the match pattern must be between the first two slashes. I'd advise you to read the documentation again.

    Nevertheless, I wrote a small script that I believe will get you on the right path. The foreach loop will act like your while (<FILEREAD>){ by iterating on the array @strings "line by line".

    use strict; use warnings; my @strings = ( 'points: "_eglErdrm@}}vA|}vAihtB~}vAik{C|}vA}~mHnojBgzlBjy +[aktAoeAooo@_l`@wl^y{oA",', 'levels: "PKLMOKNLP",' ); foreach my $string (@strings) { $string =~ s/(points|levels):(.*),$/$2/; print $string."\n";; } # That outputs: "_eglErdrm@}}vA|}vAihtB~}vAik{C|}vA}~mHnojBgzlBjy[aktAoeAooo@_l`@wl^y +{oA" "PKLMOKNLP"