Jú ’t

Contacting Juerd

Jabber
#####@juerd.nl
ICQ
7516019
MSN Messenger
_____-removethis@removethis-juerd.nl
E-mail
#####@juerd.nl

EFNet #perlhelp Q&A

Newer items go at the top. Note: all code is untested. Please don't contact me personally about anything here. But feel free to join #perlhelp on EFNet.

I've classified the questions into several categories:

The questions and answers are archived here so others can learn from them. Please note that not every question can be summarized, or even understood. This archive is incomplete. Especially it-doesn't-work questions are missing here.

Finding an RSS module

preglow wanted to know which RSS module was plain and simple.

RSS is a complex format, so a simple module would only make really using it hard. XML::RSS is not simple, but that does not mean it is not easy to use. It can both create well formed RSS files and parse them. Just parsing an RSS document and printing out a bunch of HTML hyperlinks is easy, as this example shows:

use HTML::Entities qw(encode_entities);
use XML::RSS;

my $rss = XML::RSS->new;
$rss->parse($filename);

for (@{ $rss->{items} }) {
    printf(
        qq[<a href="%s">%s</a><br>\n],
        encode_entities($_->{link}),
        encode_entities($_->{title})
    );
}

Testing definedness

macana wanted to know if Perl has a "statement" like PHP's isset().

isset() is a function (a language construct, even). A statement is something else.

In PHP, a variable can be set or unset. When it's set, it can still be NULL. However, isset() returns false if a variable is set, but NULL. In Perl, a variable is defined or undef.

Perl's defined operator returns true if its argument is defined, false if it is undef. It is the closest equivalent to PHP's isset(). The only important difference is that isset() can take multiple arguments and returns true only if none of them are unset or NULL; defined takes only one argument. Also, with isset(), you need to use a variable; with defined you can use any expression.

PHP has a defined() function, that tests whether a constant has been defined. This has nothing to do with Perl's defined operator.

Evil eval

gripe wanted to know why eval STRING is evil.

Evaluating a string of code can be very useful, but should normally be avoided because it bypasses compile time setup and makes it very easy to introduce security holes and hard-to-trace bugs.

URI-encoding a string

Mille- wanted to know how to uri-encode a string.

The answer is URI::Escape's uri_escape function.

This really is a FAQ, so perhaps this question should have been classified as RTFM, but perlfaq9's "those %-encodings on the web" isn't really something people look for. And the answer there doesn't mention URI::Escape.

Reconfiguring CPAN.pm

bDerrly wanted to know how to reconfigure CPAN.pm after they made a mistake.

The answer can be found in CPAN's documentation. Use o conf init.

Vector operations in Perl 5

Mille- wanted to know how to use the s/// operator on every element of an array.

The answer is simple:

for (@array) {
    s/foo/bar/;
}

or just:

s/foo/bar/ for @array;

Bidirectional piping

[m1] wanted to know how to open a pipe for both reading and writing. They already knew how to open for just writing.

This is a FAQ, answered in perlfaq8, under "How can I open a pipe both to and from a command?". Besides that, this issue is directly addressed in "open" in perlfunc.

Switch

acidjazz wanted to know what to do, since there is no switch statement in Perl.

This is a FAQ, answered in perlfaq7, under "How do I create a switch or case statement?".

After this question, acidjazz continued to ask FAQs and claimed more than once that they had read Beginning Perl, even though they asked questions literally answered in that book. Having wasted enough of our time, acidjazz got banned from #perlhelp after a few days.

Collecting output from an external process

__inferno wanted to know how to get the output from an external process, which they called a "system call".

This is a FAQ, answered in perlfaq8, under "Why can't I get the output of a command with system()?"

Sorting by multiple fields

W33 wanted to know how to use sort to sort by multiple fields.

This is explained in the documentation of sort in perlfunc. The documentation even features several examples.

Besides that, this is also a FAQ, answered in perlfaq4, under "How do I sort an array by (anything)?".

Converting a decimal number to binary

mina_ wanted to know how to convert a decimal number, say 65536, to its binary representation (10000000000000000).

This is a FAQ, answered in perlfaq4, under "How do I convert between numeric representations/bases/radixes?", or more specifically, under: "How do I convert from decimal to binary?".

Changing the base class at runtime

XyzZyx wanted to know if you really can modify @ISA at runtime, which would mean you can change a class' ancestors at any point in the program.

Yes, you really can. Please do note that this doesn't mean you should.

Things like this make the language extremely powerful. Always wear protection when using power tools :)

Ending a loop

acjcjc wanted to know the command to break from a loop.

last is that command. It is documented in perlfunc and every Perl learning book.

Changing the first 8 lines

Trana wanted to know how to change the first 8 lines of a text file without changing the other lines.

Text files are like other files just a stream of bytes. When the first 8 lines change in length, that means you also have to re-write the rest of the file.

In general, there are two approaches that beginners can use. The first involves slurping an entire file, changing some lines and writing the entire file back. This really is not basic stuff if multiple processes are accessing the same file and you don't want things to go wrong.

But fortunately, there is a somewhat slower, but much easier solution to this problem: Tie::File. With this module, you tie an array to a file, so that you can just manipulate the array as you would with any array, and the module takes care of manipulating the file accordingly.

This question is also answered by perlfaq5, but that only discusses Tie::File.

Current element index with foreach

OsRiS_ wanted to know if there is an easy way to determine the index of the element that foreach is currently processing.

No, this counter is internal and cannot be accessed easily. However, it is very simple to have your own counter:

my $index = -1;
foreach (@array) {
    $index++;
    ...;
}

Renaming files

buht wanted to know if with Perl it would be easy to rename files, replacing spaces with underscores.

Yes, it is. A one liner (doesn't work with strict):

($n = $_) =~ y/ /_/, rename $_, $n for glob "*";

And a cleaner version:

for my $old (glob "*") {
    (my $new = $old) =~ tr/ /_/;
    rename $old, $new
        or warn "$0: rename: $old: $!\n";
}

Removing blank lines

MoFlow wanted to know how te remove blank lines from a string, using s///.

The code for this is s/^\s*(?:\n|\z)//m.

The /m modifies the ^ to mean match at the beginning of a line instead of match at the beginning of the string. \s* slurps as many whitespace characters as it can, including newlines. The final \n forces it to end at the end of a line to keep indentation intact. This causes the regex engine to backtrack and the \s* to give up at least one character. Alternatively, instead of a \n character, the end of the string (\z) is also okay.

We can't just use $ here, because that matches just before \n, which results in the whitespace on the line being removed, but not the newline character, so the blank line isn't completely gone.

Of course, it is better to avoid having the blank lines in the first place. s/// with an empty string as the replacement is usually a sign of cleaning up after a mistake you made earlier. (It can also be a sign that you need to learn to capture what you want, instead of throw away what you don't want.)

For example, when the lines come from a file, this code avoids having blank lines:

my $file; /\S/ and $file .= $_ while readline $fh;

Now, lines are added to $file only if they contain a non-whitespace (\S) character.

The number of keys in a hash

powuh wanted to know how to tell how many items there are in a hash.

In scalar context, keys returns the number of keys of the given hash. This is documented in perlfunc. In short: scalar keys %hash.

Decoding a URI-encoded string

optical wanted to know how to decode a URI-encoded string.

The answer is URI::Escape's uri_unescape function.

This really is a FAQ, so perhaps this question should have been classified as RTFM, but perlfaq9's "those %-encodings on the web" isn't really something people look for. And the answer there doesn't mention URI::Escape.

Matching foo, but not bar

_Erlend wanted to know how to test if a string contains foo, but not bar, using a regular expression.

The simple and fast solution is to use two regexes:

$foo =~ /foo/ and $foo !~ /bar/

If you really want to do this with one regex, use a negative lookahead assertion. But that is slow and hard to read.

Removing duplicate array elements

Narcotix wanted to know how to remove duplicate elements from an array.

This is a FAQ, answered in perlfaq4, under "How can I remove duplicate elements from a list or array?".

Random array elements

telman200 wanted to know how to get two random elements from an array.

This is a FAQ, answered in perlfaq4, under "How do I select a random element from an array?".

Reading a file and calculating a sum of numbers

Napta wanted to know how to read a file that has a number on each line, and then calculate the sum.

Sounds like homework, but here goes:

my $sum = 0;
open my $fh, 'file' or die $!;
$sum += $_ while readline $fh;
close $fh;
print "Sum is: $sum\n";

chomp is not needed here, since Perl doesn't mind an extra newline character in a string when converting it to a number.

Eventually, Napta said that they were really trying to sum up used bandwidth from an Apache common logfile. This can be done using this simple oneliner:

perl -nle'$total += (split)[-1]; END { print $total }'

SMTP authentication with Net::SMTP

BoyTheo wanted to know whether Net::SMTP supports SMTP authentication.

The answer can be found in Net::SMTP's documentation. Use the auth method, which has two obvious arguments (username and password).

Finding CGI.pm documentation

systemloc wanted to know where CGI.pm's table method was documented and how to get there with perldoc.

It's quite simple:

perldoc CGI

perldoc -f can only be used for built-in functions listed in perlfunc.

Creating accessor methods for object properties

XyzZyx wanted to know a good way to create what they called a "getter/setter method". He came up with this code:

sub method {
    my $self = shift;
    @_ ? $self->{method} = shift : $self->{method};
}

This is good, but with Attributes::Property, both coding the class and using the objects is much easier.

use Attribute::Property;

sub method : Property;

This is much simpler in use (and also a bit slower), because method is now an lvalue method, which means you can use it with normal operators, like =~ and ++.

# Archaic method
my $temp = $foo->bar;
$temp =~ s/foo/bar/;
$foo->bar($temp);

# Lvalue method
$foo->bar =~ s/foo/bar/;

Additional reading: http://perldesignpatterns.com/?AccessorPattern.

Using complex data structures

UeberLame wanted to know how to create complex data structures, like a tree where every node has several scalars and an array.

The answer can be found in perldsc. Additional reading: perlreftut and perlref.

Finding something in an array or a file

Narcotix wanted to know how to find strings matching $keyword in an array @lines.

grep is the obvious choice here. It's documented in perlfunc.

my @matching_lines = grep /\Q$keyword/, @lines;

The \Q in the pattern is a convenient way to use quotemeta, which escapes non-word (\W) characters. This is done because $keyword is a string, not a regex.

If @lines was read from a file, it's better to read selectively instead of reading the entire file into memory and then copying the lines that match.

open my $fh, ... or die $!;
/\Q$keyword/ and push @lines, $_ while readline $fh;
close $fh;

Of course, this is not a good idea if you have several keywords. But in that case, it's better to make a regexp out of them.

my $regex = join '|', map quotemeta, @keywords;
$regex = qr/$regex/;  # precompile

Documentation for perl command line arguments

matja wanted to know where command line switches like -l are documented.

They're documented in perlrun. Perhaps they didn't know that the perldocs are indexed in perl and perltoc.

Passive mode with Net::FTP

CobyJones wanted to know how to use PASV transfers using Net::FTP.

The answer can be found in Net::FTP's manual. You either pass the Passive argument with a true value to the constructor, or use the pasv method if you feel masochistic and want to control everything yourself.