motherconfessor
motherconfessor

Reputation: 11

Print email addresses to a file in Perl

I have been scouring this site and others to find the best way to do what I need to do but to no avail. Basically I have a text file with some names and email addresses. Each name and email address is on its own line. I need to get the email addresses and print them to another text file. So far all I have been able to print is the "no email addresses found" message. Any thoughts? Thanks!!

#!/usr/bin/perl

open(IN, "<contacts.txt") || die("file not found");
#chooses the file to read
open(OUT, ">emailaddresses.txt");
#prints file
$none = "No emails found!";
$line = <IN>;

for ($line)
{
    if ($line =~ /[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}/g)
    {
        print (OUT $line);
    }
    else
    {
        print (OUT $none);
    }
}

close(IN);
close(OUT);

Upvotes: 1

Views: 1453

Answers (1)

amon
amon

Reputation: 57640

First, always use strict; use warnings. This helps writing correct scripts, and is an invaluable aid when debugging.

Also, use a three-arg-open:

open my $fh, "<", $filename or die qq(Can't open "$filename": $!);

I included the reason for failure ($!), which is a good practice too.

The idiom to read files (on an open filehandle) is:

while (<$fh>) {
  chomp;
  # The line is in $_;
}

or

while (defined(my $line = <$fh>)) { chomp $line; ... }

What you did was to read one line into $line, and loop over that one item in the for loop.

(Perl has a notion of context. Operators like <$fh> behave differently depending on context. Generally, using a scalar variable ($ sigil) forces scalar context, and @, the sigil for arrays, causes list context. This is quite unlike PHP.)

I'd rewrite your code like:

use strict; use warnings;
use feature 'say';
my $regex = qr/[A-Z0-9._%+-]+\@[A-Z0-9.-]+\.[A-Z]{2,4}/i; # emails are case insensitive
my $found = 0;

while (<>) { # use special ARGV filehandle, which usually is STDIN
  while (/($regex)/g) {
    $found++;
    say $1;
  }
}
die "No emails found\n" unless $found;

Invoked like perl script.pl <contacts.txt >emailaddresses.txt. The shell is your friend, and creating programs that can be piped from and to is good design.

Update

If you want to hardcode the filenames, we would combine the above script with the three-arg open I have shown:

use strict; use warnings; use feature 'say';
use autodie; # does `... or die "Can't open $file: $!"` for me
my $regex = qr/[A-Z0-9._%+-]+\@[A-Z0-9.-]+\.[A-Z]{2,4}/i;
my $found = 0;

my $contact_file = "contacts.txt";
my $email_file   = "emailaddresses.txt";

open my $contact, "<", $contact_file;
open my $email, ">", $email_file;

while (<$contact>) {    # read from the $contact filehandle
  while (/($regex)/g) { # the /g is optional if there is max one address per line
    $found++;
    say {$email} $1;    # print to the $email file handle. {curlies} are optional.
  }
}
die "No emails found\n" unless $found; # error message goes to STDERR, not to the file

Upvotes: 8

Related Questions