use strict;
use warnings;
open(FILE4,"cool.txt");
open(FILE6,">./mool.txt");
$line = <FILE4>;
while ($line ne "")
{
#array = split(/,/,$line);
$line = <FILE4> ;
print FILE6 ($array[0]);
print FILE6 ("\t");
print FILE6 ($array[1]);
print FILE6 ("\t");
print FILE6 ($array[2]);
}
These is the code I have written in perl. But the code is not working fine. Its giving tab space for every nextline. But i dont need that TAB space for every new line.Let me show you how the output is.
name contact email
samy 32344245 hyte#some.com
alex 231414124 coool#some.com
This is how i see my mool.txt file.The first line its working fine.But from the nextline I'm facing tab space.I am trying to find out where the bug is.Can anyone please let me know where the code is going wrong?I have gone through it so many times but unable to figure it out.Thank you
As someone asked im showing you the input file
"name","contact","email"
"samy","32344245","hyte#some.com"
You probably have whitespace at the beginning/end of lines in the input file.
Try stripping it with s///:
use strict;
use warnings;
open my $in, "<", "cool.txt" or die $!;
open my $out, ">", "mool.txt" or die $!;
while (my $line = <$in>) {
$line =~ s/^\s+|\s+$//g;
my #array = split(/,/, $line);
print $out join("\t", #array), "\n";
}
Put this statement inside the while loop.
chomp
This should be the first line after reading a line from a file. This will remove unwanted spaces.
use strict;
use warnings;
open(FILE4,"cool.txt");
open(FILE6,">./mool.txt");
while (<FILE4>)
{
chomp; #This will remove unwanted spaces
#array = split(/,/,$_); #$_ will represent the line which is read
print FILE6 ($array[0]);
print FILE6 ("\t");
print FILE6 ($array[1]);
print FILE6 ("\t");
print FILE6 ($array[2]);
print FILE6 ("\n");
}
What you seem to be doing is changing this file from comma-delimited to a tab-delimited. If so, this might be an easier way to do it:
while (<>) {
s/,/\t/g;
print;
}
And then use it like this:
$ script.pl cool.txt > mool.txt
You might even get away with a one-liner:
perl -pwe 's/,/\t/g' cool.txt > mool.txt
Or if you have weird whitespace:
perl -pwe 's/^\s+|\s+$//g; s/,/\t/g' cool.txt > mool.txt
Or, a safer version, using Text::CSV. This will handle complex data and such for you. If you get blank output, it may be your extra whitespace messing things up. If so, you can run the one-liner above without the s/,/\t/g line to get a "cleaned-up" version of the input file:
perl -pwe 's/^\s+|\s+$//g;' cool.txt > cool_clean.txt
Script:
use warnings;
use strict;
use Text::CSV;
use autodie;
my $csv_in = Text::CSV->new();
my $csv_out = Text::CSV->new( { sep_char => "\t", eol => "\n" } );
open my $fh, '<', 'data.csv';
open my $out, '>', 'mool.txt';
while (my $row = $csv_in->getline($fh)) {
$csv_out->print($out, $row);
}
Related
I want to open a file, and store its content in an array and make changes to each sentence one at a time and then print the output of the file.
I have something like this:
open (FILE , $file);
my #lines = split('.' , <FILE>)
close FILE;
for (#lines) {
s/word/replace/g;
}
open (FILE, ">$file");
print FILE #lines;
close FILE;
For some reason, perl doesn't like this and won't output any content into the new file. It seems to not like me splitting up the array. Can someone give me an explanation why perl does this and a possible fix? Thanks!
split needs a regexp. Change split('.' , <FILE>) to split(/\./ , <FILE>)
Change my #lines = split('.' , <FILE>) to my #lines = split('\.' , <FILE>)
Only . is used in regex to match a single character. So you need to escape . to split on full stop.
#!/usr/local/bin/perl
use strict;
use warnings;
my $filename = "somefile.txt";
my $contents = do { local(#ARGV, $/) = $filename; <> };
my #lines = split '\.', $contents;
foreach(#lines){
#lines is an array which contains one sentence at each index.
}
what i found was in second line of your script missing semicolon(;) that is the error and also your script is not capable of handling content of entire file.It will process only one line. So please find the modification of your script below.If any clarification please let me know.
my $file='test.txt';#input file name
open (FILE , $file);
#my #lines = split('\.' ,<FILE>); this will not process the entire content of the file.
my #lines;
while(<FILE>) {
s/word/replace/g;
push(#lines,$_);
}
close FILE;
open (FILE, ">$file");
print FILE #lines;
close FILE;
You have lots of problems in your code.
my #lines = split('.' , <FILE>) will just read the first line and split it.
split('.' should be split(/\./
my #lines = split('.' , <FILE>) no semicolon terminator.
print FILE #lines; - you have lost all your full stops!
Finally I have to wonder why you are bothered about 'sentences' at all when you are just replacing one word. If you really want to read one sentence at a time (presumably to do some kind of sentence based processing) then you need to change the record separator variable $\. For example:
#!/usr/bin/perl
use strict;
use warnings;
my $file = "data.txt";
open (FILE , $file);
my #buffer;
$/ = '.'; # Change the Input Separator to read one sentence at a time.
# Be careful though, it won't work for questions ending in ?
while ( my $sentence = <FILE> ) {
$sentence =~ s/word/replace/g;
push #buffer, $sentence;
}
close FILE;
.. saving to file is left for you to solve.
However if you just want to change the strings you can read the whole file in one gulp by setting $/ to undef. Eg:
#!/usr/bin/perl
use strict;
use warnings;
my $file = "data.txt";
open (FILE , $file);
$/ = undef; # Slurp mode!
my $buffer = <FILE>;
close FILE;
$buffer =~ s/word/replace/g;
open (FILE, ">$file");
print FILE $buffer;
close FILE;
If you are really looking to process sentences and you want to get questions then you probably want to slurp the whole file and then split it, but use a capture in your regex so that you don't lose the punctuation. Eg:
!/usr/bin/perl
use strict;
use warnings;
my $file = "data.txt";
open (FILE , $file);
$/ = undef; # slurp!
my $buffer = <FILE>;
close FILE;
open (FILE, ">$file" . '.new'); # Don't want to overwrite my input.
foreach my $sentence (split(/([\.|\?]+)/, $buffer)) # split uses () to capture punctuation.
{
$sentence =~ s/word/replace/g;
print FILE $sentence;
}
close FILE;
Why does setting the Perl input record separator to $/ = "__Data__\n" not work?
The data record is set as follows:
__Data__\n
1aaaaaaaaaa\n
aaaaaaaaaaa\n
aaaaaaaaaaaaa\n
__Data__\n
1bbbbbbbbbb\n
bbbbbbbbbbb\n
bbbbbbbbbbbbb\n
__Data__\n
1cccccccccc\n
ccccccccccc\n
ccccccccccccc\n
__Data__\n
Here is the Perl code to access the first row of each data record...
$/ = "__Data__\n";
open READFILE, "<", "logA.txt" or die "Unable to open file";
while (<READFILE>)
{
if (/([^\n]*)\n(.*)/sm)
{
print "$1\n";
}
}
close(<READFILE>);
I get the undesirable output of:
__Data__
and not the desirable output of:
1aaaaaaaaaaa
1bbbbbbbbbbb
1ccccccccccc
Why is the input record separator $/="__Data__"; not working? How should it work?
If I understand the question correctly, you want to strip out the __Data__ part. You want this...
1aaaaaaaaaa
1bbbbbbbbbb
1cccccccccc
...but you're getting this...
__Data__
1aaaaaaaaaa
1bbbbbbbbbb
1cccccccccc
You can use the chomp command to remove the end of line. Normally this is just a newline, but chomp responds to whatever you set $/ to.
use strict;
use warnings;
{
local $/="__Data__\n";
open my $fh, "<", "logA.txt" or die "Unable to open file";
while(my $record = <$fh>) {
chomp $record;
print $record;
}
}
BTW because you changed the concept of "end of line", everything between the __Data__ fields will be considered a single line. If you need to split the lines up, you can use my #lines = split "\n", $record.
use strict;
use warnings;
{
# Isolate the change to the global $/
local $/="__Data__\n";
open my $fh, "<", "logA.txt" or die "Unable to open file";
while(my $record = <$fh>) {
# Remove the __Data__ separator
chomp $record;
# Split the record by line
my #lines = split /\n/, $record;
# Empty record, skip it
next if !#lines;
# Print the first line of the record
print $lines[0], "\n";
}
}
I also made some general improvements to your code. $/ is global and will affect everything that reads files. local ensures your change only happens inside the block.
I've used lexical filehandles, they automatically close themselves when they go out of scope (when the block they're declared in is done).
And I've turned on strict and warnings which will catch typos and little mistakes like close(<READLINE>).
input.txt
__Data__
1aaaaaaaaaa
aaaaaaaaaaa
aaaaaaaaaaaaa
__Data__
1bbbbbbbbbb
bbbbbbbbbbb
bbbbbbbbbbbbb
__Data__
1cccccccccc
ccccccccccc
ccccccccccccc
__Data__
using $/=qq{__Data__\n}
perl -e 'use Data::Dumper;$Data::Dumper::Useqq=1; $/=qq{__Data__\n}; open $fh,"input.txt"; print Dumper [ <$fh> ]'
$VAR1 = [
"__Data__\n",
"1aaaaaaaaaa\naaaaaaaaaaa\naaaaaaaaaaaaa\n__Data__\n",
"1bbbbbbbbbb\nbbbbbbbbbbb\nbbbbbbbbbbbbb\n__Data__\n",
"1cccccccccc\nccccccccccc\nccccccccccccc\n__Data__"
];
using $/=qq{Data}
$VAR1 = [
"__Data",
"__\n1aaaaaaaaaa\naaaaaaaaaaa\naaaaaaaaaaaaa\n__Data",
"__\n1bbbbbbbbbb\nbbbbbbbbbbb\nbbbbbbbbbbbbb\n__Data",
"__\n1cccccccccc\nccccccccccc\nccccccccccccc\n__Data",
"__"
];
I guess it's self explanatory.
I have been learning Perl for a few days and I am completely new.
The code is supposed to read from a big file and if a line contains "warning" it should store it and print it on a new line and also count the number of appearances of each type of warning. There are different types of warnings in the file e.g "warning GR145" or "warning GT10" etc.
So I want to print something like
Warning GR145 14 warnings
Warning GT10 12 warnings
and so on
The problem is that when I run it, it doesnt print the whole list of warnings.
I will appreciate your help. Here is the code:
use strict;
use warnings;
my #warnings;
open (my $file, '<', 'Warnings.txt') or die $!;
while (my $line = <$file>) {
if($line =~ /warning ([a-zA-Z0-9]*):/) {
push (#warnings, $line);
print $1 ,"\n";
}
}
close $file;
You are using case sensitive matching in your if statement. Try adding a /i:
if($line =~ /warning ([a-z0-9]*):/i)
EDIT: I misread the actual question, so this answer could be ignored...
You need to use a hash array, a mapping from warning string to occurrence count.
use strict;
use warnings;
my %warnings = {};
open (my $file, '<', 'Warnings.txt') or die $!;
while (my $line = <$file>) {
if ($line =~ /warning ([a-zA-Z0-9]*)\:.*/) {
++$warnings{$1};
}
}
close $file;
foreach $w (keys %warnings) {
print $w, ": ", $warnings{$w}, "\n";
}
I am facing issues with perl chomp function.
I have a test.csv as below:
col1,col2
vm1,fd1
vm2,fd2
vm3,fd3
vm4,fd4
I want to print the 2nd field of this csv. This is my code:
#!/usr/bin/perl -w
use strict;
my $file = "test.csv";
open (my $FH, '<', $file);
my #array = (<$FH>);
close $FH;
foreach (#array)
{
my #row = split (/,/,$_);
my $var = chomp ($row[1]); ### <<< this is the problem
print $var;
}
The output of aboe code is :
11111
I really don't know where the "1" is comming from. Actually, the last filed can be printed as below:
foreach (#array)
{
my #row = split (/,/,$_);
print $row[1]; ### << Note that I am not printing "\n"
}
the output is:
vm_cluster
fd1
fd2
fd3
fd4
Now, i am using these field values as an input to the DB and the DB INSERT statement is failing due this invisible newline. So I thought chomp would help me here. instead of chomping, it gives me "11111".
Could you help me understand what am i doing wrong here.
Thanks.
Adding more information after reading loldop's responce:
If I write as below, then it will not print anything (not even the "11111" output mentioned above)
foreach (#array)
{
my #row = split (/,/,$_);
chomp ($row[1]);
my $var = $row[1];
print $var;
}
Meaning, chomp is removing the last string and the trailing new line.
The reason you see only a string of 1s is that you are printing the value of $val which is the value returned from chomp. chomp doesn't return the trimmed string, it modifies its parameter in-place and returns the number of characters removed from the end. Since it always removes exactly one "\n" character you get a 1 output for each element of the array.
You really should use warnings instead of the -w command-line option, and there is no reason here to read the entire file into an array. But well done on using a lexical filehandle with the three-parameter form of open.
Here is a quick refactoring of your program that will do what you want.
#!/usr/bin/perl
use strict;
use warnings;
my $file = 'test.csv';
open my $FH, '<', $file or die qq(Unable to open "$file": $!);
while (<$FH>) {
chomp;
my #row = split /,/;
print $row[1], "\n";
}
although, it is my fault at the beginning.
chomp function return 1 <- result of usage this function.
also, you can find this bad example below. but it will works, if you use numbers.
sometimes i use this cheat (don't do that! it is my bad-hack code!)
map{/filter/ && $_;}#all_to_filter;
instead of this, use
grep{/filter/}#all_to_filter;
foreach (#array)
{
my #row = split (/,/,$_);
my $var = chomp ($row[1]) * $row[1]; ### this is bad code!
print $var;
}
foreach (#array)
{
my #row = split (/,/,$_);
chomp ($row[1]);
my $var = $row[1];
print $var;
}
If you simply want to get rid of new lines you can use a regex:
my $var = $row[1];
$var=~s/\n//g;
So, I was quite frustrated with this easy looking task bugging me for the whole day long. I really appreciate everyone who responded.
Finaly I ended up using Text::CSV perl module and then calling each of the CSV field as array reference. There was no need left to run the chomp after using Text::CSV.
Here is the code:
#!/usr/bin/perl
use warnings;
use strict;
use Text::CSV;
my $csv = Text::CSV->new ( { binary => 1 } ) # should set binary attribute.
or die "Cannot use CSV: ".Text::CSV->error_diag ();
open my $fh, "<:encoding(utf8)", "vm.csv" or die "vm.csv: $!";
<$fh>; ## this is to remove the column headers.
while ( my $row = $csv->getline ($fh) )
{
print $row->[1];
}
and here is hte output:
fd1fd2fd3fd4
Later i was pulled these individual values and inserted into the DB.
Thanks everyone.
Ive been trying to compare lines between two files and matching lines that are the same.
For some reason the code below only ever goes through the first line of 'text1.txt' and prints the 'if' statement regardless of if the two variables match or not.
Thanks
use strict;
open( <FILE1>, "<text1.txt" );
open( <FILE2>, "<text2.txt" );
foreach my $first_file (<FILE1>) {
foreach my $second_file (<FILE2>) {
if ( $second_file == $first_file ) {
print "Got a match - $second_file + $first_file";
}
}
}
close(FILE1);
close(FILE2);
If you compare strings, use the eq operator. "==" compares arguments numerically.
Here is a way to do the job if your files aren't too large.
#!/usr/bin/perl
use Modern::Perl;
use File::Slurp qw(slurp);
use Array::Utils qw(:all);
use Data::Dumper;
# read entire files into arrays
my #file1 = slurp('file1');
my #file2 = slurp('file2');
# get the common lines from the 2 files
my #intersect = intersect(#file1, #file2);
say Dumper \#intersect;
A better and faster (but less memory efficient) approach would be to read one file into a hash, and then search for lines in the hash table. This way you go over each file only once.
# This will find matching lines in two files,
# print the matching line and it's line number in each file.
use strict;
open (FILE1, "<text1.txt") or die "can't open file text1.txt\n";
my %file_1_hash;
my $line;
my $line_counter = 0;
#read the 1st file into a hash
while ($line=<FILE1>){
chomp ($line); #-only if you want to get rid of 'endl' sign
$line_counter++;
if (!($line =~ m/^\s*$/)){
$file_1_hash{$line}=$line_counter;
}
}
close (FILE1);
#read and compare the second file
open (FILE2,"<text2.txt") or die "can't open file text2.txt\n";
$line_counter = 0;
while ($line=<FILE2>){
$line_counter++;
chomp ($line);
if (defined $file_1_hash{$line}){
print "Got a match: \"$line\"
in line #$line_counter in text2.txt and line #$file_1_hash{$line} at text1.txt\n";
}
}
close (FILE2);
You must re-open or reset the pointer of file 2. Move the open and close commands to within the loop.
A more efficient way of doing this, depending on file and line sizes, would be to only loop through the files once and save each line that occurs in file 1 in a hash. Then check if the line was there for each line in file 2.
If you want the number of lines,
my $count=`grep -f [FILE1PATH] -c [FILE2PATH]`;
If you want the matching lines,
my #lines=`grep -f [FILE1PATH] [FILE2PATH]`;
If you want the lines which do not match,
my #lines = `grep -f [FILE1PATH] -v [FILE2PATH]`;
This is a script I wrote that tries to see if two file are identical, although it could easily by modified by playing with the code and switching it to eq. As Tim suggested, using a hash would probably be more effective, although you couldn't ensure the files were being compared in the order they were inserted without using a CPAN module (and as you can see, this method should really use two loops, but it was sufficient for my purposes). This isn't exactly the greatest script ever, but it may give you somewhere to start.
use warnings;
open (FILE, "orig.txt") or die "Unable to open first file.\n";
#data1 = ;
close(FILE);
open (FILE, "2.txt") or die "Unable to open second file.\n";
#data2 = ;
close(FILE);
for($i = 0; $i < #data1; $i++){
$data1[$i] =~ s/\s+$//;
$data2[$i] =~ s/\s+$//;
if ($data1[$i] ne $data2[$i]){
print "Failure to match at line ". ($i + 1) . "\n";
print $data1[$i];
print "Doesn't match:\n";
print $data2[$i];
print "\nProgram Aborted!\n";
exit;
}
}
print "\nThe files are identical. \n";
Taking the code you posted, and transforming it into actual Perl code, this is what I came up with.
use strict;
use warnings;
use autodie;
open my $fh1, '<', 'text1.txt';
open my $fh2, '<', 'text2.txt';
while(
defined( my $line1 = <$fh1> )
and
defined( my $line2 = <$fh2> )
){
chomp $line1;
chomp $line2;
if( $line1 eq $line2 ){
print "Got a match - $line1\n";
}else{
print "Lines don't match $line1 $line2"
}
}
close $fh1;
close $fh2;
Now what you may really want is a diff of the two files, which is best left to Text::Diff.
use strict;
use warnings;
use Text::Diff;
print diff 'text1.txt', 'text2.txt';