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

Dearest Monks,

I am trying to replace the text in file #1 with the text in file #2 to make file #3. I would like to do so based on the identity between the text in file #1 and the text before the underscore (_) in file #2 to make file #3. The basic idea is to be able to add the content after the underscore (which is indicated in file #2) into each entry of file #1, but I want to maintain the commas and line breaks found in file #1 so that it can be read into another program.

Here are smaller versions of my files:

File 1 (1.txt):

IIY86TY04J1FRV,IIY86TY04IIJKA,IIY86TY04JGLTI IIY86TY04JH3LU IIY86TY04I9BQV,IIY86TY04JEKGS,IIY86TY04JWBSL,IIY86TY04JUIVK IIY86TY04JUW5G,IIY86TY04JAWV0

File 2 (2.txt):

IIY86TY04J1FRV_HG1 IIY86TY04IIJKA_HG3 IIY86TY04JGLTI_HG4 IIY86TY04JH3LU_HG1 IIY86TY04I9BQV_crop4 IIY86TY04JEKGS_HG1 IIY86TY04JWBSL_HG4 IIY86TY04JUIVK_HG4 IIY86TY04JUW5G_HG2 IIY86TY04JAWV0_HG3

I want to make File 3 (3.txt):

IIY86TY04J1FRV_HG1,IIY86TY04IIJKA_HG3,IIY86TY04JGLTI_HG4 IIY86TY04JH3LU_HG1 IIY86TY04I9BQV_crop4,IIY86TY04JEKGS_HG1,IIY86TY04JWBSL_HG4,IIY86TY04JU +IVK_HG4 IIY86TY04JUW5G_HG2,IIY86TY04JAWV0_HG3

Based on a similar post, I thought I could do the following (please excuse my child-like annotations):

#!/usr/local/bin/perl use warnings; use strict; my $f1 = '1.txt'; # set up files 1 through 3 my $f2 = '2.txt'; my $f3 = '3.txt'; my (@strings, $text); open (FH, $f1) || die; # open file 1 in a file handle chomp(@strings = <FH>); # take off the line breaks open (FH, $f2) || die; # open file 2 in a file handle $text = join '', <FH>; # joins files 1 and 2? - I don't think I wa +nt to do this... $text =~ s/\Q$_\E/$f3/g for @strings; # this is the problem spot, +I think... open (FH, ">$f3") || die; print FH $text; close FH;

but obviously not because when I do I get this for 3.txt:

IIY86TY04J1FRV_HG1 IIY86TY04IIJKA_HG3 IIY86TY04JGLTI_HG4 3.txt_HG1 IIY86TY04I9BQV_crop4 IIY86TY04JEKGS_HG1 IIY86TY04JWBSL_HG4 IIY86TY04JUIVK_HG4 IIY86TY04JUW5G_HG2 IIY86TY04JAWV0_HG3

Any ideas?

And many thanks in advance.

Vanessa

THANK YOU PERL MONKS!! YOU ARE AWESOME!!

Here is another version that works too. I also switched around the order of where the information after the underscore was placed in the output file...

use warnings; use strict; my $f1 = '1.txt'; # file that needs the treatments added on my $f2 = '2.txt'; # file that has all of the treatments and an under +score my $f3 = '3.txt'; # output file my %ids; my $fh; open $fh, '<', $f2 or die; while (<$fh>) { chomp; my ($name, $id) = split /_/; $ids{$name} = $id; } close $fh; open my $fho, '>', $f3 or die; open $fh, '<', $f1 or die; while (<$fh>) { chomp; my @names = split /,/; foreach my $name (@names) { $name = $name . '_' . $ids{$name}; } my $line = join(',', @names); print $fho $line, "\n"; } close $fh; close $fho;
  • Comment on SOLVED!!!: search contents of one file and replace with contents of another to make a new file
  • Select or Download Code

Replies are listed 'Best First'.
Re: search contents of one file and replace with contents of another to make a new file
by toolic (Bishop) on May 06, 2015 at 17:13 UTC
    You can store your file2 names and ids in a hash, then loop through file1 and make substitutions:
    use warnings; use strict; my $f1 = '1.txt'; # set up files 1 through 3 my $f2 = '2.txt'; my $f3 = '3.txt'; my %ids; my $fh; open $fh, '<', $f2 or die; while (<$fh>) { chomp; my ($name, $id) = split /_/; $ids{$name} = $id; } close $fh; open my $fho, '>', $f3 or die; open $fh, '<', $f1 or die; while (<$fh>) { for my $name (keys %ids) { s/$name/${name}_$ids{$name}/; } print $fho $_; } close $fh; close $fho;
Re: search contents of one file and replace with contents of another to make a new file
by flexvault (Monsignor) on May 06, 2015 at 17:12 UTC

    Welcome vcorby,

    Your script isn't doing what you think! Please look at 'split'. You need to 'slurp' the file into an array or use a loop to get your first file. (untested)

    my @strings = (); open (my $F1, "<", $f1) || die "! open-100 $!";; # open file 1 while ( my $input = <$F1> ) { chomp( $input ); # take off the line breaks $strings[$#strings] = $input; } close $F1;

    And life will be easier for you if you start using the 3 parameter 'open' from the beginning:

    open (my $F3, ">", $f3 ) || die "! open-300 $!";
    This gives you a start and ask more as you get closer.

    Note: I like to number my 'die's so it's quicker to find the problem. If you have a 50K line script, you'll be glad you did :-).

    Regards...Ed

    "Well done is better than well said." - Benjamin Franklin

Re: search contents of one file and replace with contents of another to make a new file
by aaron_baugher (Curate) on May 06, 2015 at 17:21 UTC

    I can think of one quick-and-dirty solution that would work for small files but wouldn't scale up well to large ones, so I'll assume the files might be large. First, turn file2 into a hash, with the first part of each line as the key and the second part as the value. Then, loop through the lines of file1, check to see if each "word" is in the hash, and append its value to it if it is. Something like this:

    #!/usr/bin/env perl use 5.010; use warnings; use strict; my %h; open my $fd2, '<', 'file2' or die $!; while (<$fd2>){ chomp; my($k, $v) = split /_/; $h{$k} = $v; } open my $fd1, '<', 'file1' or die $!; while (<$fd1>){ s/(\w+)/$h{$1}?"${1}_$h{$1}":$1/ge; print; }

    Oh, and here's the quick and dirty solution, which reads all of file1 into memory:

    perl -e '$f1=`cat file1`;open $f2,"<","file2";while(<$f2>){chomp;($k,$ +v)=split "_";$f1=~s/$k/${k}_$v/g;}print $f1;'

    Aaron B.
    Available for small or large Perl jobs and *nix system administration; see my home node.

Re: search contents of one file and replace with contents of another to make a new file
by marinersk (Priest) on May 06, 2015 at 17:38 UTC

    Seeing open FHwithout matching close FHbothers me. The behavior might be correct -- heck, for all I know, Perl might even by design do the right thing -- but it jangles my OCD nerve.

    open (FH, $f1) || die; # open file 1 in a file handle chomp(@strings = <FH>); # take off the line breaks --> close FH; open (FH, $f2) || die; # open file 2 in a file handle $text = join '', <FH>; # joins files 1 and 2? - I don't think I wa +nt to do this... --> close FH;

      Yes, a close is the best way.
      However in this situation, a new OPEN for that same filehandle will cause a "close" before the next OPEN happens.
      So basically this "works".
        I love Perl.
Re: search contents of one file and replace with contents of another to make a new file
by lonewolf28 (Beadle) on May 06, 2015 at 23:38 UTC

    Similar to the above posts but with very slight difference.

    use strict; use warnings; use Data::Dumper; open ( my $fh2, '<', 'file2.txt') or die ("cannot open file1: $!"); my %test; while(my $line = <$fh2> ){ chomp $line; my( $part1, $part2) = split(/_/, $line); $test{$part1} = $part2; } close $fh2; open ( my $fh1, '<', 'file1.txt') or die ( "Cannot open the file2: $!" +); my @file1; while (my $line = <$fh1>) { chomp $line; push @file1, split /,/, $line; } close $fh1; open(my $wh, '>', 'file3.txt') or die ( "cannot open file2 to write: $ +!"); for my $file1 (@file1) { if (exists $test{$file1}) { print $wh join("_", ($file1, $test{$file1}."\n" )); } } close $wh;