Perl: Skip line N while reading a file - perl

How would I skip say line 9 while I'm parsing a text file?
Here's what I got
use strict; use warnings;
open(my $fh, '<', 'file.txt') or die $!;
my $skip = 1;
while (<$fh>){
$_=~ s/\r//;
chomp;
next if ($skip eq 9)
$skip++;
}
Not sure if this works but I'm sure there's a more eloquent of doing it.

You can use $.:
use strict; use warnings;
open(my $fh, '<', 'file.txt') or die $!;
while (<$fh>){
next if $. == 9;
$_=~ s/\r//;
chomp;
# process line
}
Can also use $fh->input_line_number()

Related

Read a text file and store each line in a variable using perl

I have a text file (sample.txt) with some data. I want to read the text file and store each line in an array or a variable.
sample.txt
ab1234
str:abcd
pq4567
How can i store each of these lines in an array or a variable using perl script.
It is easy. We open the file, push each line in the file to an array after you chomped \n (newline characters) and to test it, we print the array.
Here $_ is each of the lines read from file where #lines will store each of $_ in an array.
use strict;
use warnings
my $file = "sample.txt";
open(my $fh, "<", "sample.txt") or die "Unable to open < sample.txt: $!";
my #lines;
while (<$fh>) {
chomp $_;
push (#lines, $_);
}
close $fh or die "Unable to open $file: $!";
print #lines;
an even easier method is to just store the content to array.
use strict;
use warnings
my $file = "sample.txt";
open(my $fh, "<", "sample.txt") or die "Unable to open < sample.txt: $!";
my #lines = <$fh>;
chomp(#lines);
print #lines;
# open the file
open my $fh, '<', 'sample.txt'
or die "Could not open sample.txt: $!";
# Read the file into an array
my #lines = <$fh>;
# Optionally, remove newlines from all lines in the array
chomp(#lines);
If you are able to use CPAN modules, then Tie::File is there for your help.
Using this module you can modify, add or delete the contents in the file.
below is the script.
#!/usr/bin/perl
use strict;
use warnings;
use Tie::File;
my #contents=();
tie #contents, 'Tie::File','sample.txt' or die "Not able to Tie sample.txt\n";
my $count=1;
foreach (#contents)
{
print "line $count:$_\n";
$count++;
}
untie #contents;
output:
line 1: ab1234
line 2: str:abcd
line 3: pq4567

unable to write the output into a file in perl

I want to write the result of my code into a file , but it's not written into my file. My code is working and it's about removing duplicate line, but when i wanted to write the output into a file, the file is empty. This is my code:
use strict;
use warnings;
open(DATA,"/root/Desktop/SIEMENS/printtokens/outputs/common_distinct/MR1/thiscommon.txt");
open FILE2, ">/root/Desktop/SIEMENS/printtokens/outputs/common_distinct/MR1/common_element.txt" or die $!;
my %lines;
#open DATA, $ARGV[0] or die "Couldn't open $ARGV[0]: $!\n";
while (<DATA>) {
print if not $lines{$_}++;
print FILE2 if not $lines{$_}++;
}
close DATA;
close FILE2;
This works as expected
use strict;
use warnings;
use autodie;
open(my $in, '<', './in.txt');
open(my $out, '>', './out.txt');
my %lines;
while (<$in>) {
print $out $_ unless $lines{$_}++;
}
close $in;
close $out;
Note the usage of the 3-argument open, lexical filehandles and the autodie pragma.
Me as a fan of the usage of CPAN modules, would write the same as:
use 5.014;
use warnings;
use Path::Tiny;
use List::Util qw(uniq);
my $out_file = './out.txt';
my $in_file = './in.txt';
path($out_file)->spew( uniq path($in_file)->lines );
Your main problem probably is that $lines{$_}++ changes the value of $lines{$_} so the second will never evaluate to false. You could probably fix your problem just by removing ++ on the first occurence. But for readability I would recommend wrapping both output lines with one if statement, avoid using $_ and remembering to close the files, so something like:
use strict;
use warnings;
my %lines;
open(my $in, '<', './in.txt');
open(my $out, '>', './out.txt');
while (my $line = <$in>) {
unless ($lines{$line}++) {
print STDOUT $line;
print {$out} $line
}
}
close($in);
close($out);
As you can see, I also prefer lexical filehandles and 3-argument open.
Not quite sure what you want in common_element.txt, but I think the logic in the read loop is wrong. No need to increase the $lines twice, and only output if there is a match from existing data?
Try:
use strict;
use warnings;
open(DATA,"thiscommon.txt");
open (FILE2, '>common_element.txt') or die $!;
my %lines;
while (<DATA>) {
print FILE2 if $lines{$_};
print if not $lines{$_}++;
}
close DATA;
close FILE2;
Only with your paths, obviously.

Extract private key and write to a new file in perl

Simply put, I want to extract the entire private key, listed below, from trust.pem and save that text to a new file (tmp6.pem), using Perl. My current implementation only gives me the first for letters of the key.
-----BEGIN CERTIFICATE-----
MIIDazCCAtSgAwIBAgIBCDANBgkqhkiG9w0BAQQFADA8MQ8wDQYDVQQDEwZoMjFk
Y2ExDTALBgNVBAsTBFdJTlQxDTALBgNVBAoTBFdJTlQxCzAJBgNVBAYTAlVTMB4X
DTE1MDQxNjA0MTYyNloXDTI1MDQxMzA0MTYyNlowQjELMAkGA1UEBhMCVVMxDTAL
BgNVBAoTBFdJTlQxDTALBgNVBAsTBFdJTlQxFTATBgNVBAMTDGgyMWRjYS1vY3Nw
NDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMCVRB8NpRhOZIpPk2qb
xqPS/X6odDsGVxHofnLJBRLBjOPQgUpa9FsXZ5//ZMMSSpudRN5N0eIcnAzPED78
FgLWbAm+D0j+OsE+1DLr+qwputxhz2Fm7f6snb/MH3JqGpioyREkY8yxNHDrN7nP
hp2fpZojlaow/Bpxg9iPZZm+J8C9NY9vun7/vLx3O5BOdH0A/qWlo4qmxH0d1vTi
KTGNE9hKE2Fl1KtZhsrJRKgOu6B0ATYL75NPKANOzNKLZmxlF2HKd3FIEOAEzSFS
/ZaJHwPD4SyLapFwqxMVfDF+MPb2Tvs9YEHBraNxWx8OiB27nAss1zfF7JeKnpck
ApsCAwEAAaOB8jCB7zAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NM
IEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUY+cf2lJ6EZhgAy/rQU8f
Z4FGNLcwZgYDVR0jBF8wXYAU2Q1ss1/VO6fejshKp0XizESFEB6hQqRAMD4xETAP
BgNVBAMTCHdpbnRyb290MQ0wCwYDVQQLEwRXSU5UMQ0wCwYDVQQKEwRXSU5UMQsw
CQYDVQQGEwJVU4IBDzALBgNVHQ8EBAMCBeAwIAYDVR0lAQH/BBYwFAYIKwYBBQUH
AwEGCCsGAQUFBwMJMA0GCSqGSIb3DQEBBAUAA4GBAEETJk/Jt0/JNpKjhU6QbYhy
UNErAJyclyrrbC4JyKbTABzLyubYZz2g0VM+YKHmhHliGosjx4yqKqUc44zvYqzW
o0EpuOWDTnj7yXUZjDozVDSJiZcr27I2R3aOxvF4PGw2bk/sWRcs7QMhgT2z5RRE
vXIYrGIjW2jFz1oKHGGT
-----END CERTIFICATE-----
Here is my code, thus far:
use warnings;
use strict;
use FileHandle;
use Fcntl qw(:DEFAULT :flock :seek); # import LOCK_* constants
my $F_IN = FileHandle->new("<trust.pem");
my $F_OUT = FileHandle->new(">tmp6.pem");
while (my $line = $F_IN->getline) {
if ($line =~ m|(MIID)|) {
$F_OUT->print("$1\n");
}
}
$F_IN->close();
$F_OUT->close();
This will skip the first and last lines of the input file by using the input line number to detect the first line and checking whether the next read would return end of file to detect the last line:
use strict;
use warnings;
open(my $in, '<', 'trust.pem') or die $!;
open(my $out, '>', 'tmp6.pem') or die $!;
while (<$in>) {
next if $. == 1;
last if eof($in);
print $out $_;
}
close($in);
close($out);
If you don't mind reading the entire file into memory---which is no problem at all in this case---you could use shift and pop to remove the first and last lines from the array:
use strict;
use warnings;
open(my $in, '<', 'trust.pem') or die $!;
my #lines = <$in>;
close($in);
shift(#lines);
pop(#lines);
open(my $out, '>', 'tmp6.pem') or die $!;
print $out #lines;
close($out);
Note that the previous examples won't work very well if you have any leading or trailing blank lines. In that case, I'd probably do something like this instead, which skips PEM file headers/footers and lines containing only whitespace:
use strict;
use warnings;
open(my $in, '<', 'trust.pem') or die $!;
open(my $out, '>', 'tmp6.pem') or die $!;
while (<$in>) {
next unless /\S/;
next if /^-----/;
print $out $_;
}
close($in);
close($out);

How to reset $.?

I know $. shows the line number when $/ is set to "\n".
I wanted to emulate the Unix tail command in Perl and print the last 10 lines from a file but $. didn't work. If the file contains 14 lines it starts from 15 in the next loop.
#!/usr/bin/perl
use strict;
use warnings;
my $i;
open my $fh, '<', $ARGV[0] or die "unable to open file $ARGV[0] :$! \n";
do { local $.; $i = $. } while (<$fh>);
seek $fh, 0, 0;
if ($i > 10) {
$i = $i - 10;
print "$i \n";
while (<$fh>) {
#local $.;# tried doesn't work
#undef $.; #tried doesn't work
print "$. $_" if ($. > $i);
}
}
else {
print "$_" while (<$fh>);
}
close($fh);
I want to reset $. so it can be used usefully in next loop.
Using local with $. does something else than you think:
Localizing $. will not
localize the filehandle's line count. Instead, it will localize
perl's notion of which filehandle $. is currently aliased to.
$. is not read-only, it can be assigned to normally.
1 while <$fh>;
my $i = $.;
seek $fh, $. = 0, 0;
You must reopen the file handle. Otherwise, as you have found, the line number just continues to increment
#!/usr/bin/perl
use strict;
use warnings;
my ($filename) = #ARGV;
my $num_lines;
open my $fh, '<', $filename or die qq{Unable to open file "$filename" for input: $!\n};
++$num_lines while <$fh>;
open $fh, '<', $filename or die qq{Unable to open file "$filename" for input: $!\n};
print "$num_lines lines\n";
while ( <$fh> ) {
print "$. $_" if $. > $num_lines - 10;
}
Here's a neater way
#!/usr/bin/perl
use strict;
use warnings;
my ($filename) = #ARGV;
my #lines;
open my $fh, '<', $filename or die qq{Unable to open file "$filename" for input: $!\n};
while ( <$fh> ) {
push #lines, $_;
shift #lines while #lines > 10;
}
print #lines;

Read Increment Then Write to a text file in perl

I have this little perl script which opens a txt file, reads the number in it, then overwrites the file with the number incremented by 1. I can open and read from the file, I can write to the file but I"m having issues overwriting. In addition, I'm wondering if there is a way to do this without opening the file twice. Here's my code:
#!/usr/bin/perl
open (FILE, "<", "data.txt") or die "$! error trying to a\
ppend";
undef $/;
$number = <FILE>;
$number = int($number);
$myNumber = $number++;
print $myNumber+'\n';
close(FILE);
open(FILE, ">data.txt") or die "$! error";
print FILE $myNumber;
close(FILE);
Change the line
$myNumber = $number++;
to
$myNumber = $number+1;
That should solve the problem.
Below is how you could do by opening the file just once:
open(FILE, "+<data.txt") or die "$! error";
undef $/;
$number = <FILE>;
$number = int($number);
$myNumber = $number+1;
seek(FILE, 0, 0);
truncate(FILE, tell FILE);
print $myNumber+"\n";
print FILE $myNumber;
close(FILE);
It's good that you used the three-argument form of open the first time. You also needed to do that in your second open. Also, you should use lexical variables, i.e., those which begin with my, in your script--even for your file handles.
You can just increment the variable that holds the number, instead of passing it to a new variable. Also, it's a good idea to use chomp. This things being said, consider the following option:
#!/usr/bin/env perl
use strict;
use warnings;
undef $/;
open my $fhIN, "<", "data.txt" or die "Error trying to open for reading: $!";
chomp( my $number = <$fhIN> );
close $fhIN;
$number++;
open my $fhOUT, ">", "data.txt" or die "Error trying to open for writing: $!";
print $fhOUT $number;
close $fhOUT;
Another option is to use the Module File::Slurp, letting it handle all the I/O operations:
#!/usr/bin/env perl
use strict;
use warnings;
use File::Slurp qw/edit_file/;
edit_file { chomp; $_++ } 'data.txt';
Try this:
#!/usr/bin/perl
use strict;
use warnings;
my $file = "data.txt";
my $number = 0;
my $fh;
if( -e $file ) {
open $fh, "+<", $file or die "Opening '$file' failed, because $!\n";
$number = <$fh>;
seek( $fh, 0, 0 );
} else { # if no data.txt exists - yet
open $fh, ">", $file or die "Creating '$file' failed, because $!\n";
}
$number++;
print "$number\n";
print $fh $number;
close( $fh );
If you're using a bash shell, and you save the code to test.pl, you can test it with:
for i in {1..10}; do ./test.pl; done
Then 'cat data.txt', should show a 10.