Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

New Line at End File

by Anonymous Monk
on Dec 09, 2002 at 22:35 UTC ( [id://218675]=perlquestion: print w/replies, xml ) Need Help??

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

Hello monks, Is there an easy way to determine if there are new line characters at the bottom of a file, and if so delete them?

Thanks

Replies are listed 'Best First'.
Re: New Line at End File
by tachyon (Chancellor) on Dec 09, 2002 at 23:29 UTC

    If you don't want to read the whole file into memory you can just truncate it in place....

    [tachyon@www cgi-bin]# cat ./test.pl #!/usr/bin/perl my $file = '/tmp/test.txt'; open FILE, ">$file" or die $!; print FILE "foo\nbar\nfoo\nbar\nfoo\nbar\nfoo\nbar\nfoo\nbar\nfoo\nbar +\n\n\r\n\n\r"; close FILE; # grab the last 20 bytes for analysis open FILE, $file or die $!; { local $/ = undef; seek FILE, -20, 2; $EOF = <FILE>; } close FILE; my $file_length = -s $file; (my $newlines) = $EOF =~ m/([\015\012]+)\z/; my $num_newlines = length $newlines; print "File length $file_length bytes, number of newlines $num_newline +s\n"; truncate $file, ($file_length - $num_newlines) or die $!; print "Now file is ", -s $file, "bytes\n\n"; [tachyon@www cgi-bin]# ./test.pl File length 53 bytes, number of newlines 6 Now file is 47 bytes [tachyon@www cgi-bin]#

    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print


      You should have pointed out that this solution will only work if there are 20 or less newlines at the end of the file.

      --
      John.

        but that would have left nothing left to say.... I would generally read in 512 bytes anyway as this is a generally a sector and realistically the smallest chunk of a disk that the OS ever reads. This was an example and the limitation is self evident I would have thought. As it happens it will only take of the last 10 'newline' CRLF line endings on Windows if we want to get really picky ;-)

        If you want to move on to the more arcane and esoteric neither seek() nor read() for that matter can be relied upon to do exactly what you ask. From the docs....

        If you want to position file for sysread or syswrite, don't use seek-- +buffering makes its effect on the file's system position unpredictabl +e and non-portable. Use sysseek instead.

        cheers

        tachyon

        s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: New Line at End File
by jdporter (Paladin) on Dec 09, 2002 at 22:41 UTC
      You could also use File::ReadBackwards to read in just the last line and test for the newline characters.

      use File::ReadBackwards; $bw = File::ReadBackwards->new('file') or die "can't read 'file' $!"; $lastline = $bw->readline;

      bW

      The special variable $^I not only turns in-place editing on, it also specifies the extension for a backup of the original file. Something like $^I = '.bak' might be better than setting it to '1'. If you really don't want the backup you can set it to an empty string; in-place editing is on unless the variable is undefined.

      -sauoq
      "My two cents aren't worth a dime.";
      
      Thanks,
      I think I see whats going on, but if you wouldn't mind, can you explain how this works.

      Thanks

        Sure!

        local @ARGV = $file_name; This sets up the file to be read in via perl's command-line magic. It has the same effect as if you had specified the file on the command line to perl. Any subsequent read from <> will come from this opened file.
        local $^I = ''; This has the same effect as the -i command-line switch to perl. It means that when a file is opened for reading via the @ARGV magic, then a subsequent print will go to that same file. Hence "edit in place".
        If you'd like to make a backup of the original file, use some value other than the empty string shown here. The string you give will be used as the extension for the backup. E.g. '.bak'
        Tip o' the hat to sauoq.
        local $/; # sluurp This sets $/ to an undefined value, thus turning off automatic per-line input reading. Any subsequent read from <> (in scalar context) will read all the remaining available input, instead of just the next line.
        local $_ = <>; Here, we finally get around to reading the input. Because of the first and third lines above, $_ will get the entire contents of the named file.
        while ( chomp ) { } Remember that chomp() returns the number of characters chomped off. Since you want to remove any/all newlines at the end of the file, we keep chomping until chomp says it wasn't able to chomp any more.
        print; When all that is done, print the contents of $_ back out to the file, by virtue of $^I being defined.
        Finally, we want to make sure that all those global variables we used aren't munged from the perspective of any other code. (For example, you might have good reason for @ARGV to keep its original values that you passed on the command line.) Therefore, we use local to localize all our changes to the block specified by the curly braces.

        I hope that's clear. If you'd like any further clarification, just ask!

      seek() and truncate() also work well as shown below....

      cheers

      tachyon

      s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: New Line at End File
by sauoq (Abbot) on Dec 09, 2002 at 23:43 UTC

    Here's a way to do it without reading the whole file in first...

    #!/usr/bin/perl use strict; use Fcntl qw(SEEK_END SEEK_CUR); my $f = shift; open F, "+<", $f or die "Couldn't open $f: $!\n"; seek F, -1, SEEK_END; seek F, -2, SEEK_CUR while (getc(F) eq "\n"); truncate F, tell F;
    -sauoq
    "My two cents aren't worth a dime.";
    

      It's nit time:^) The only question is the nit the code or me but...

      Doesn't the use of -2, make this non-portable? Wouldn't this only work on DOSish systems? How about -length "\n" instead of -2?


      Okay you lot, get your wings on the left, halos on the right. It's one size fits all, and "No!", you can't have a different color.
      Pick up your cloud down the end and "Yes" if you get allocated a grey one they are a bit damp under foot, but someone has to get them.
      Get used to the wings fast cos its an 8 hour day...unless the Govenor calls for a cyclone or hurricane, in which case 16 hour shifts are mandatory.
      Just be grateful that you arrived just as the tornado season finished. Them buggers are real work.

        The only question is the nit the code or me but...

        Personally, I think that nit should be with the "OS"... but, yeah. You got me. ;-)

        -sauoq
        "My two cents aren't worth a dime.";
        

      This also strips the last newline from the file.

      Changing the last line to the following should fix it:

      my $pos = tell F; truncate F, 1 +$pos if $pos > 1;

      --
      John.

        This also strips the last newline from the file.

        Of course it does. That's exactly what was asked for. The OP read, "Is there an easy way to determine if there are new line characters at the bottom of a file, and if so delete them?" He didn't ask how to delete all but one newline or how to remove blank lines.

        -sauoq
        "My two cents aren't worth a dime.";
        
Re: New Line at End File
by jmcnamara (Monsignor) on Dec 10, 2002 at 08:59 UTC

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others avoiding work at the Monastery: (4)
As of 2024-04-24 12:42 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found