Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Print text on the same line after match

by periodicalcoder (Novice)
on Dec 14, 2018 at 20:16 UTC ( [id://1227265]=perlquestion: print w/replies, xml ) Need Help??

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

Hello all, it has been a while for me. You were all a huge help with my last project.

This is actually a follow-up of that same project, and while I feel that this should be simple I cannot find (or don't know enough to adapt) a solution online. I have a text file with a line (there will only ever be a single line that matches) like this:

N1*PE*COMPANY NAME INC*XX*123456~

**edit: on Windows using Strawberry Perl with a .pl script** I need to extract the number at the end of this line, excluding the tilde, and add it to a variable so that $number = 123456. So far I have only been able to come up with the match regex, but I'm not sure how to get the text at the end. So far I have this which gives the entire line:

perl -ne "print if /INC\*XX\*/" cr835.txt

Inside my perl script I assume that I will use something like this, but of course including any recommended code changes:

my $number = "perl -ne "print if /INC\*XX\*/" cr835.txt";

My end goal is to detect this number and use it to rename the file. I already have the code tested for renaming the file with a timestamp and I just need help populating this variable.

Thanks a bunch!

peridicalcoder

Replies are listed 'Best First'.
Re: Print text on the same line after match
by haukex (Archbishop) on Dec 14, 2018 at 21:00 UTC
    I have only been able to come up with the match regex, but I'm not sure how to get the text at the end.

    Unless you're really concerned about performance, it doesn't hurt to split things up into two regexes to make things easier to think about, for example you could say:

    ($number) = /(\d+)~$/ if /INC\*XX\*/;

    But in this case it's not too difficult to do it in a single regex, see below.

    (Update: To answer your question more basically, have a look at perlretut, where capturing groups are explained.)

    Inside my perl script I assume that I will use something like this

    It's almost never necessary to call perl from inside a Perl script. The while loop in the following code is the equivalent of a one-liner. I've added some checks to make sure that the number is found exactly once in the input file - I use defined instead of a boolean check to differentiate between 0, which Perl would consider false, and undef, which in this case means that no number was found. In the regex, I'm using a couple of techniques: /x for readability, and a negative lookbehind (?<!\d) to make sure the thing before the number is not also a number - not really required in this case (it's a bit of paranoia), but it could become an important technique if your match happens to get more complex. You didn't say whether there could be any characters in the string between "INC*XX*" and the number; if not, then the regex could be simplified to just /INC\*XX\*(\d+)~$/.

    use warnings; use strict; use Data::Dumper; $Data::Dumper::Useqq=1; my $filename = 'cr835.txt'; open my $fh, '<', $filename or die "$filename: $!"; my $number; while (<$fh>) { if ( m{ INC\*XX\* .* (?<!\d) (\d+) ~ $ }x ) { die "number matched twice" if defined $number; $number = $1; } } die "no number found" unless defined $number; close $fh; print Dumper($number);
Re: Print text on the same line after match
by toolic (Bishop) on Dec 14, 2018 at 20:35 UTC
    You can use capturing parentheses and print the captured number ($1) instead of the entire line:
    perl -ne 'print $1 if /INC\*XX\*(\d+)/' cr835.txt 123456
      Toolic, that did work, and thank you!

      One detail that I forgot to mention, and I edited the original post, is that I am working on Windows with Strawberry Perl. I had some trouble integrating the command into my script and could not store the output into a variable as it just went to stdout due to the "print $1 if" portion. I tried to redirect stdout, and not using "print" at all, but I ended up creating a temp file using this code to store the resulting number in a variable:

      use IO::Handle; # Extract the Payee ID from the file and store it into a temporary fil +e my $payeeid; system( q(perl -ne "print $1 if /INC\*XX\*(\d+)/" cr835.txt > payee.tx +t) ); # Define a file variable and read the contents to a variable my $file; $file = path("./payee.txt"); my $payeeid = $file->slurp; # Define the date and time for use in the new file name use constant DATETIME => strftime("%Y-%m-%d_%H-%M", localtime); # Define the new file name my $file = "CR835.txt"; my $newfilename = "CR835-" . $payeeid . "-processed_" . DATETIME . ".t +xt"; # Rename the file move $file, $newfilename;
      Is there a better way to store the result into the variable?
        Is there a better way to store the result into the variable?

        Yes - I showed how to do this without calling an extra perl process, it'll give you much more power and control.

Re: Print text on the same line after match
by johngg (Canon) on Dec 14, 2018 at 23:57 UTC

    I usually create a new file with its new name rather than renaming the original file and I compare new and old to make sure nothing has gone wrong before unlinking the original. Perhaps an over-complication but a background in systems administration, where trashing a file could bring down a service, has instilled caution.

    use strict; use warnings; use POSIX qw{ strftime }; use Digest::MD5; my $origFile = q{cr835.txt}; open my $origFH, q{<}, $origFile or die qq{open: < $origFile: $!\n}; my $line = <$origFH>; close $origFH or die qq{close: < $origFile: $!\n}; my $payeeID; if ( $line =~ m{INC\*XX\*(\d+)} ) { $payeeID = $1; } else { die qq{Could not match payee ID\n}; } my $newFile = qq{cr835-} . $payeeID . strftime q{-processed_%Y-%m-%d_%H-%M.txt}, localtime; open my $newFH, q{>}, $newFile or die qq{open: > $newFile: $!\n}; print $newFH $line; close $newFH or die qq{close: > $newFile: $!\n}; my $origMD5 = Digest::MD5 ->new() ->addfile( do { open my $fh, q{<}, $origFile or die $!; $fh; } ) ->hexdigest(); my $newMD5 = Digest::MD5 ->new() ->addfile( do { open my $fh, q{<}, $newFile or die $!; $fh; } ) ->hexdigest(); if ( $origMD5 eq $newMD5 ) { unlink $origFile or die qq{unlink: $origFile: $!\n}; } else { die qq{Original and new files differ\n}; }

    I hope this is helpful.

    Cheers,

    JohnGG

      The command switch /i accomplishes almost the same thing. Ref: Command Switches. Note that the OP is already using /n.
      Bill
        The command switch /i accomplishes almost the same thing

        I would say it might accomplish something vaguely similar but it doesn't satisfy the main requirement of the OP's script which is to rename the file. Since the content of the original file is not being changed in any way but is only being queried to provide the new filename I don't think that in-place editing using -p is appropriate. All it can do here is write exactly the same content to the same filename, with the possibility of leaving an identical backup file behind as well. Also, as far as I can see the OP is only using -n in the unnecessary system call inside the script to the Perl one-liner; we only see part of the main script from the look of it.

        Cheers,

        JohnGG

Re: Print text on the same line after match
by thanos1983 (Parson) on Dec 14, 2018 at 23:55 UTC

    Hello periodicalcoder,

    Fellow Monks have already answered your question. Just for fun this also a possible solution using rindex, substr and rename.

    The script is written based on the assumptions of your statement. That there is a unique line ending with (~) and the numbers that you want to extract are 6. Other than that it should be really fast and efficient. Or at least I believe so :)

    Hope this helps, BR.

    Seeking for Perl wisdom...on the process of learning...not there...yet!

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1227265]
Approved by toolic
Front-paged by toolic
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others studying the Monastery: (4)
As of 2024-04-19 03:56 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found