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

I made:
#!/usr/local/bin/perl use strict; use warnings; open(FILE, ">height.txt") || die "file can't be opened"; while(<FILE>) { chomp; s/'/\t/g; } close FILE;

=======

I want to replace apostrophy with a tab. so s/'/\t/g;

the file I load is something like this:

Name SSN height Person1 478937439 5'1 Person2 436655676 6'3

My actual file is much larger than this actually.

Each field is separated by tab. Now I want to separate the height field into feet and inches. So I want to replace ' by tab.

My script doesn't work and it says an error: Filehandle main::FILE opened only for output at height line 8. Can somebody please help me ?

2001-03-16 Edit by Corion : Added formatting and changed title

Replies are listed 'Best First'.
Re: Opening a file for reading or writing (was: Newbie)
by OeufMayo (Curate) on Mar 18, 2001 at 01:56 UTC

    If the only thing you want to edit in your height.txt file, you aim for Laziness and go for the following one-liner:

    perl -pi.bak -e "s/'/\t/" height.txt # Win32 perl -pi.bak -e 's/\x27/\t/' height.txt # *nix

    But, as I'm typing this one, I realize there's a big flaw in you scheme: What if the Name of Person1 has a single quote in it? Maybe one of the regexen below could do the trick (and fortunately, the previous one made automatically a backup of the file!)

    Win32 and *nix versions perl -pi.bak -e "s/\s(\d+)'(\d+)$/\t$1\t$2/" height.txt perl -pi.bak -e 's/\s(\d+)\x27(\d+)$/\t$1\t$2/' height.txt perl -pi.bak -e "@r=split(/\t/);$r[2] =~ s/'/\t/;$_=join(qq'\t',@r)" +height.txt perl -pi.bak -e '@r=split(/\t/);$r[2] =~ s/\x27/\t/;$_=join("\t",@r)' + height.txt

    The first one finds two groups of digit separated by a ' at the end of the string, and replace the ' by a tabulation character.

    TMTOWTDI!

    <kbd>--
    my $OeufMayo = new PerlMonger::Paris({http => 'paris.mongueurs.net'});</kbd>
(jeffa) Re: Newbie
by jeffa (Bishop) on Mar 18, 2001 at 01:07 UTC
    while(<FILE>) {
    EEEK! UPDATE! change your open to
    open(FILE, "height.txt") # > means write
    Hope you didn't just wipe out your file . . . :O

    Yup he did - I should have caught that, I did view the question BEFORE Corion added code tags - so <FILE> was not displayed in the while loop.

    Sorry.

    Jeff

    R-R-R--R-R-R--R-R-R--R-R-R--R-R-R--
    L-L--L-L--L-L--L-L--L-L--L-L--L-L--
    
      It runs fine now. But it doesn't do anything to the text file I load.
      All of my datas stay the same
Re: Opening a file for reading or writing (was: Newbie)
by Corion (Patriarch) on Mar 18, 2001 at 01:13 UTC

    The error describes relatively good what went wrong. FILE was only opened for writing, whereas you want to read from it.

    So, go back and recreate the content in that file (it's gone by now) and then open the file for reading :

    open FILE, "< $file" or die "Couldn't open $file for reading : $!\n" +; while (<FILE>) { print $_; };
      I'm a little bit confused.
      I want to read the file but also modify the file if it matches with '
      so can I just open(FILE, "height.txt");
      I tried everything, but it still doesn't do anything.
      my data in the text file I load stay the same.

        Inplace modification of text files is considered harmful. I suggest that you write your output first to a second file and then rename the first file to a backup filename and the second file then to the original filename.

        If you are bent on modifying a file in place (which is a bad idea while developing a program), just take a look at the documentation for the open() call :

        open(DBASE, '+<$file') # open for update or die "Can't open '$file' for update: $!";

        But I really really really have to recommend that you let your script output the stuff to the console first, and then simply redirect the console output into a new file. This is much much safer, and Perl has the -i command line switch which lets you switch on the in place modification easily.

        Try this: perl -i.bak -pe "s/\'/\t/;" somefile.dat

        The entire problem can be solved with a one-liner. Using tr or y instead of s might be good in this case because s loses performance due to regex computation. You don't really need a regex here -- you're only replacing one character -- unless, of course, you only want to replace the first one, in which case use s.

        Modifying a file, then putting the modified file in place of the original file, is known as modifying the file "in place", which is done using the i switch. The .bak is to make a backup as somefile.dat.bak, so that in case of error, you can recover the original data. Since you are modifying it in place, your while(<FILE>) { ... } loop becomes a while(<>) { ... } loop. To get the modified data to be output back to the file, you need to use a print statement, thus: while(<>) { ... print; } This can be accomplished by using the p switch, which places that around whatever code is given. Finally, the resulting code is so short that it can be one-lined using e, which is "run code from next argument".

        See perlman:perlrun and perlman:perlop for more information on perl options and operators.

        Drake Wilson

        Update: D'oh! Corion had already pointed out -i. Oh well.

Re: Opening a file for reading or writing (was: Newbie)
by how do i know if the string is regular expression (Initiate) on Mar 18, 2001 at 01:49 UTC
    The '>' in the open call truncates and open's the file for writing. You want to open it in in update mode ('+<'). This will open it for reading and writing.

    Try this:

    #!/usr/local/bin/perl -w use strict; use warnings; open(FILE, "+<height.txt") || die "file can't be opened"; my @file = <FILE>; # Read entire file into array map{ s/'/\t/g } @file; # substitution on each element seek(FILE,0,0); # go back to the beginning of the file truncate(FILE, tell(FILE)) # truncate from here to end of file print FILE @file; # write back to file close FILE;
    I don't really think you need the truncate() here... but it's safer. If the size of the data you wind up writing to the file is smaller then the size of the original file, the truncate will get rid of that excess data. Which is not the case here. But it's good habit.

    - FrankG

Re: Opening a file for reading or writing (was: Newbie)
by enoch (Chaplain) on Mar 18, 2001 at 01:12 UTC
    Did you check your permissions for opening the file?

    Jeremy
      yes I did
Re: Opening a file for reading or writing (was: Newbie)
by LiTinOveWeedle (Scribe) on Mar 18, 2001 at 18:35 UTC
    Here is something wich should work. It litle big but you can cut it.

    # -------------------------------------------------------------------- +----- # read array from file sub read_file { my ( $line ); if ( not open(FILE, $file_path) ) { &logging( "Can't open database file." ); die; } if ( $file_lock ) { flock(FILE, 1); } @file = (); while ( $line = <FILE> ) { chomp($line); push(@file, $line); } } <p>
    Now you can do anything you want with array @file.

    # -------------------------------------------------------------------- +----- # write array back to file sub write_file { my ( $line ); if ( not open(FILE, ">".$file_path) ) { &logging( "Can't open database file." ); die; } if ( $file_lock ) { flock(FILE, 2); } foreach $line ( @file ) { print FILE "$line\n"; } close(FILE); }

    if $file_lock is <> 0 then script will use file locking.

    Li Tin O've Weedle
    mad Tsort's philosopher

      First of all useful error messages should always preserve whatever information you can get on what went wrong. In this case that means you shouldn't lose the filename and contents of $!.

      Secondly your file locking makes all of the usual mistakes and will be subject to all of the regular race conditions. You need to put your locks around the entire sequence of actions that should be an atomic unit. There have been several threads on this before, see Re (tilly) 1: Flock Feedback for an example.

Re: Opening a file for reading or writing (was: Newbie)
by Anonymous Monk on Mar 19, 2001 at 06:23 UTC
    Your opening the file as if you were writing to it.

    try:open(FILE, "height.txt") or die "file can't be opened: $!"; or: open(FILE, "<height.txt") or die "file can't be opened: $!";

    notice the difference in the file operator
    '>' is for writing to
    '<' is for reading from
    ''(nothing) is default read from

    2001-03-19 Edit by Corion : Added CODE tags