1. Perl
  2. File input/output
  3. here

Safely read and write files

When reading a file and writing to the same file, it is very dangerous to specify a read/write open mode "+ <" and write it back. If the system crashes while you are writing, the files will be corrupted and you will not be able to restore them.

For safe writing, write to a new file and then rename the file to the original one to overwrite it.

The following is an example for safe reading and writing. You need to do a thorough error check.

use strict;
use warnings;

# Used to rename the file.
use File::Copy;

# Be careful about the specified file as it will be edited and written back.
my $infile = shift;

# temporary file. As much as possible to other processes
# Make the process ID ($$) a temporary file name so that it does not overlap
# use.
my $tmpfile = "tmp $$";

open(my $infh, "<", $infile)
  or die "Cannot open $infile:$!";

# Open a temporary file for export.
open(my $tmpfh, ">", $tmpfile)
  or die "Cannot open $tmpfile:$!";
    
while (1) {
    my $line;

    # Make sure to avoid read errors
    undef $!;
    # In case of error or EOF, undef is returned
    unless (defined($line = <$infh>)) {
      if ($!) {
        # In case of error
        die "Error read $infh:$!";
      }
      else {
        # For EOF
        last;
      }
    }
    
    if ($. == 1) {
      # Combine the letters update: at the beginning of the file only on the first line
      $line = "update:". $line;
    }
    
    # Make sure to avoid write errors
    print $tmpfh $line
      or die "Error Writing $tmpfile:$!";
}

close $infh;

# Surely avoid close errors
close $tmpfh
  or die "Error closing $tmpfile";

# Change the temporary file name to the original file name.
File::Copy::move ($tmpfile, $infile)
  or die "Cannot move $tmpfile to $infile";

I'm using while statement to safely read the rows in order.

How to read and write files safely

If you read open function documentation, you should specify the open mode "+ <" for the open function to read and write files. maybe. You can read and write this way, but it's very dangerous.

If the system crashes while writing, the files will be corrupted and cannot be restored.

Therefore, the following methods are used to read and write safely.

  1. Read the input file.
  2. Write the edited contents to the temporarily created file.
  3. Change the temporarily created file name to the original file name.

This will preserve the contents of the original file until the third file is renamed. Even if the system crashes while renaming a file, you can avoid losing the contents of the file.

Code explanation

(1) Creating a temporary file name for writing

my $tmpfile = "tmp $$";

Make sure that the temporary file name includes the process ID ($$). By making it a convention to include the process ID ($) in the temporary file name, you can avoid conflicting temporary files with other processes (unless the convention is broken. )

(2) Open both read file and temporary write file

open(my $infh, "<", $infile)
  or die "Cannot open $infile:$!";

open(my $tmpfh, ">", $tmpfile)
  or die "Cannot open $tmpfile:$!";

The input file is opened in read mode ("<") and the temporary file for writing is opened in write mode (">").

(3) File reading error check

    unless (defined($line = <$infh>)) {
      if ($!) {
        die "Error read $infh:$!";
      }
      else {
         last;
      }
    }

If an error occurs at the end of the loading, we will exit the while loop. Then, it happens that the original file is overwritten because it was processed halfway. To prevent this from happening, you need to code to ensure that read errors are avoided.

(4) Error checking when writing a file

    print $tmpfh $line
      or die "Error Writing $tmpfile:$!";

If an error occurs during aile writing, the original file may be overwritten with incorrect content. To prevent this from happening, you need to code to make sure you avoid write errors.

(5) Error checking when closing a file

close $infh;

# Surely avoid close error
close $tmpfh
  or die "Error closing $tmpfile";

Although omitting the error check for closing the read file does not cause a serious problem, it is necessary to perform the error check for the written file.

Perl's print function buffers (stores in memory) what you write. Buffering means that when you call the print function, it doesn't actually write to the file. Outputting to a file is very slow, so it is implicit that the contents to be written are stored in a buffer and written out all at once.

If the written contents are accumulated to a certain extent, it will be output to a file, but it will not be output until then. In that case, when close function is called, it will be output to a file (forcibly writing the buffer contents to a file is called flash). increase).

So if close fails, the buffer will be left unflushed and you will not be able to write the correct content. To prevent this from happening, you need to code to ensure that you avoid close errors.

(6) Rename the temporary file name for writing to the original file name

# Change the temporary file name to the original file name.
File::Copy::move ($tmpfile, $infile)
  or die "Cannot move $tmpfile to $infile";

(Reference) die function

Use File::Copy module to rename the temporary file for writing to the original file name. That's it.

Related Informatrion