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

Read and write using random access

This is a random access example where you can pinpoint only the part you want to change, change it, and write it back. Random access is generally used for fixed-length binary files, but for convenience, we will use fixed-length text files here.

use strict;
use warnings;

# Used to use the option to specify the position of seek
use Fcntl qw(:seek);

# Rewrite the file contents by random access.
my $file = "data_20080810.txt";

# Open in read/write mode (not very secure)
open(my $fh, "+ <", $file)
  or die "Cannot open $file";

# 16 bytes
my $len_rec = 16;

# Beginning of record
my $pos_name = 0;

# Fixed length 8 bytes
my $len_name = 8;

# 8th byte from the beginning
my $pos_age = 8;

# Fixed length 8 bytes
my $len_age = 8;

# This time, add 1 to the age of peko.
# peko is on the second record. Age is the 8th byte from the beginning of the record
my $pos_peko_age = 1 * $len_rec + $pos_age;

# Move the position of the file pointer to the position of Peco's age
seek ($fh, $pos_peko_age, SEEK_SET)
  or die "Cannnot seek: $file:$!";

# Load peko's age into $age
my $age;

# Number of bytes read
my $len_read;
$len_read = read($fh, $age, $len_age);

if (! defined $len_read) {
  die "Cannot read $file:$!";
}
elsif ($len_read != $len_age) {
  die "Read only $len_read bytes";
}


# Add 1 to age
$age ++;

# When read, the file pointer advances by the amount read, so return it to the original position.
seek ($fh, $pos_peko_age, SEEK_SET)
  or die "Cannnot seek: $file:$!";

# Write to file
printf $fh "%08s", $age
  or die "Cannot print: $file:$!";

# Close file
close $fh
  or die "Cannot close $file:$!";

The following is the data to read. It is the data that three records (name 8 bytes, age 8 bytes) are lined up. Save it as data_20080810.txt.

taro 00000023peko 00000018akira 00000023

Code explanation

(1) Preparing to use the symbols used in the seek function

use Fcntl qw(:seek);

Import the symbols associated with the seek function from the Fcntl module. Three constants, SEEK_SET, SEEK_CUR and SEEK_END, are imported.

(2) How to specify the position in a fixed - length file

# 16 bytes
my $len_rec = 16;

# Beginning of record
my $pos_name = 0;

# Fixed length 8 bytes
my $len_name = 8;

# 8th byte from the beginning
my $pos_age = 8;

# Fixed length 8 bytes
my $len_age = 8;

# This time, add 1 to the age of peko.
# peko is on the second record. Age is the 8th byte from the beginning of the record
my $pos_peko_age = 1 * $len_rec + $pos_age;

For fixed-length files, you can specify the byte position directly. As mentioned above, it is convenient to define the length of one record, the position from the beginning of each data and the number of bytes, when specifying the position.

(3) Use the seek function to move to the byte position you want to read.

# Move the position of the file pointer to the position of Peco's age

seek ($fh, $pos_peko_age, SEEK_SET)
  or die "Cannnot seek: $file:$!";

You can use the seek function to move the file pointer to the specified byte position. Think of a file pointer as a variable that remembers the byte position of the current file.

The first argument is the open filehandle, the second argument is the byte position you want to move, and the third argument is the reference position (start, current position, end).

The constants that can be specified for the third argument are as follows.

constant meaning
SEEK_SET Based on the beginning of the file
SEEK_CUR Based on the position pointed to by the current file pointer
SEEK_END Based on the end of the file

The value of the second argument is added based on the above position. SEEK_SET is used to specify the byte position from the beginning. In the case of SEEK_END, the second argument will specify a negative value.

If the seek function fails, it returns undef and sets $! To the error content.

(4) Read the data with the read function

# Load peko's age into $age
my $age;

# Number of bytes read
my $len_read;
$len_read = read($fh, $age, $len_age);

if (! defined $len_read) {
  die "Cannot read $file:$!";
}
elsif ($len_read != $len_age) {
  die "Read only $len_read bytes";
}

By using the read function, only the specified number of bytes can be read from the file. The first argument is the open filehandle, the second argument is the scalar variable that stores the read data, and the third argument is the number of bytes to read.

The return value of the read function is the number of bytes actually read. In case of read error, undef is returned and the error content is set in $!.

If the number of bytes read is insufficient, error handling is performed for each read error.

(5) Move to the write position with the seek function

# When read, the file pointer advances by the amount read, so return it to the original position.
seek ($fh, $pos_peko_age, SEEK_SET)
  or die "Cannnot seek: $file:$!";

The read function advances the position of the file pointer by the amount read. To write to the same position again, call the seek function again.

(6) Write to a file

printf $fh "%08s", $age
  or die "Cannot print: $file:$!";

Writing is done by specifying the format with printf function. By specifying%08s, it means that "in an 8-digit string, the part less than 8 digits is filled with 0".

Related Informatrion