Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw

file open with variables

by Anonymous Monk
on Sep 27, 2022 at 16:16 UTC ( #11147134=perlquestion: print w/replies, xml ) Need Help??

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

Hello, I am a newbie in Perl and seeking a help with file open using variables. In below code, I like to use variables X_info, Y_info, Z_info in the file open line so I can only change the variable contents to open a file. Can someone help this? I can't figure this out. Thanks, Steve
use strict; my X_info = 3; my Y_info = -4; my Z_info = 5; ###### want to replace 3 with X_info, -4 with Y_info, 5 with Z_info ## +################################ open(DLOG, '<' 'D:\PROJ\N123_X3\dataInfo_X-4_Y5_decode.csv') or die "w +e have a problem: $!"; print "It Works.\n" close (DLOG);

Replies are listed 'Best First'.
Re: file open with variables
by choroba (Cardinal) on Sep 27, 2022 at 16:56 UTC
    Single quotes don't interpolate variables. You need double quotes.
    open(DLOG, '<', "D:\\PROJ\\N12${X_info}_X$X_info\\dataInfo_X${Y_info}_ +Y${Z_info}_decode.csv") or die "we have a problem: $!"; # ^

    Note that I needed to add the comma after the mode, it's not optional.

    Also, a backslash is special in double quotes, so it needs to be backslashed to keep its literal meaning.

    Moreover, a variable name can't be followed by an underscore (or any other character valid in a variable name) in double quotes, e.g. "$x_" means the variable named $x_, not $x followed by an underscore; you might need to use the curly braces syntax "${x}_".

    Even better, use sprintf:

    open(DLOG, '<', sprintf 'D:\PROJ\N12%s_X%s\dataInfo_X%s_Y%s_decode.csv +', $X_info, $X_info, $Y_info, $Z_info) or die "we have a problem: $!" +; # ~~ ~~ ~~ ~~

    The usual rant about using lexical filehandles will come shortly.

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
      a backslash is special in double quotes, so it needs to be backslashed to keep its literal meaning.

      In this special case, a DOS-style path on Windows not passed to legacy applications, using forward slashes instead of backslashes also works and is slightly more readable:

      open(DLOG, '<', "D:/PROJ/N12${X_info}_X$X_info/dataInfo_X${Y_info}_Y${ +Z_info}_decode.csv") or die "we have a problem: $!";

      (Explaining the forward slashes: The entire Windows API accepts both forward slashes and backslashes, and so did DOS. Only a few legacy commands use forward slashes to introduce switch parameters. Perl does not care at all about forward slashes or backslashes in path names, it just passses the path name to the underlying operating system. See Re^2: What is the meaning of this line in Perl on linux? and the links in that subthread for more details.)


      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

      The usual rant about using lexical filehandles will come shortly

      So as not to disappoint the choroba: always use lexical filehandles!!! (and the 3-argument form of open).

      Further to kcott's exemplary sample code, which uses the recommended 3-argument form of open with a lexical filehandle, note that lexical filehandles are automatically closed at end of scope (used here to illustrate how Perl garbage collection differs from Java).

Re: file open with variables
by kcott (Archbishop) on Sep 28, 2022 at 01:45 UTC

    G'day Steve,

    Welcome to the Monastery.

    Using the strict pragma is good; use it in all of your Perl code. You should also use the warnings pragma in all code.

    In this instance, I would also use the autodie pragma. Hand-crafting your own I/O exception messages is tedious and notoriously error-prone. In your code, the message won't tell you which file had a problem; compare with the output from my example code below, which I wrote to intentionally fail, but I didn't need to add 'or die "some message: $!"'.

    You need to start your variables with a sigil; e.g. $X_info instead of X_info. If you'd run your posted code, you would have been alerted to this. For future reference, please do basic checks on code before posting.

    From your question, I get the impression that you want to open different files based on the supplied variables. Use a subroutine to do this. See my code below for an example of this; the open function explains the preferred 3-argument form and use of a lexical filehandle.

    I wasn't certain exactly what replacements in the filename you wanted. I've just shown an example; modify to suit your needs. Also, I've used a single hash instead of three separate variables; that's just to show an alternative method which may, or may not, be useful for you.

    $ perl -e ' use strict; use warnings; use autodie; my %info = qw{X 3 Y -4 Z 5}; handle_csv_file(@info{qw{X Y Z}}); sub handle_csv_file { my ($X, $Y, $Z) = @_; my $csv_file = sprintf q{D:\PROJ\%s\%s\%s.csv}, $X, $Y, $Z; open my $fh, "<", $csv_file; # do something with $fh here return; } ' Can't open 'D:\PROJ\3\-4\5.csv' for reading: 'No such file or director +y' at -e line 14

    For future use, consider creating an account. This is very easy to do and doesn't require you to provide lots of personal information; see "Create A New User". You gain a number of benefits and it differentiates your posts from all of the other anonymous posts (currently >100,000).

    — Ken

Re: file open with variables
by BillKSmith (Monsignor) on Sep 28, 2022 at 15:49 UTC
    An alternate way to build a string (your file name) is to use the concatenation ('.') operator (refer: Additive Operators) rather than interpolation. In this case it has the advantage that you keep your single quotes and single backslashes. I have used Test::Simple to verify that I have constructed the string you expect.
    use strict; use warnings; use Test::Simple tests=>1; # Expected # my $expected_file_name = 'D:\PROJ\N123_X3\dataInfo_X-4_Y5_decode.csv'; # Given my $X_info = 3; my $Y_info = -4; my $Z_info = 5; my $concatenated_name = 'D:\PROJ\N123_X' . $X_info # 3 . '\dataInfo_X' . $Y_info # -4 . '_Y' . $Z_info # 5 . '_decode.csv' ; ok($concatenated_name eq $expected_file_name, 'concatenation');


    1..1 ok 1 - concatenation

      An alternative to chained string concatenation is the join built-in. This has the advantage IMHO that it operates on a "pure" list, i.e., one that can be built | built arbitrarily from strings, arrays, function calls, etc.

      Win8 Strawberry (32) Wed 09/28/2022 18:56:53 C:\@Work\Perl\monks >perl use strict; use warnings; use Test::Simple tests=>1; 1..1 # Expected my $expected_file_name = 'D:\PROJ\N123_X3\dataInfo_X-4_Y5_decode.csv'; # Given my $X_info = 3; my $Y_info = -4; my $Z_info = 5; my @intro = ('D:\PROJ\N123_X', $X_info, '\dataInfo_X', $Y_info); my $concatenated_name = join '', @intro, '_Y', $Z_info, '_decode.csv', ; ok($concatenated_name eq $expected_file_name, 'join concatenation'); ^Z ok 1 - join concatenation

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

Log In?

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

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (4)
As of 2022-12-01 19:57 GMT
Find Nodes?
    Voting Booth?