What should I use instead of printf in Perl?

2019-06-17 07:27发布

问题:

I need to use some string replacement in Perl to ease translations, i.e. replace many

print "Outputting " . $n . " numbers";

by something like

printf ("Outputting %d numbers", $n);

However, I'd like to replace printf with something easier to parse for humans, like this:

printX ("Outputting {num} numbers", { num => $n });

or generally something more Perly.

Can you recommend something (from CPAN or not) you like and use?

回答1:

Most Templating modules on CPAN will probably do what you want. Here's an example using Template Toolkit...

use Template;
my $tt = Template->new;

$tt->process( \"Outputting [% num %] numbers\n", { num => 100 } );


And you can mimic your required example with something like this...

sub printX {
    use Template;
    my $tt = Template->new( START_TAG => '{', END_TAG => '}' );
    $tt->process( \( $_[0] . "\n" ), $_[1] );
}

and you've got...

printX 'Outputting {num} numbers' => { num => 100 };


回答2:

What about simply:

 print "Outputting $n numbers";

That's very Perly. If you don't need any kind of fancy formatting, string interpolation is definitely the way to go.



回答3:

The print builtin is very convenient for most situations. Besides variable interpolation:

print "Outputting $n numbers";    # These two lines
print "Outputting ${n} numbers";  # are equivalent

Remember that print can take multiple arguments, so there is no need to concatenate them first into a single string if you need to print the result of a subroutine call:

print "Output data: ", Dumper($data);

However, for outputting numbers other than simple integers, you'll probably want the formatting convenience of printf. Outputting other data types is easy with print, though.

You can use join to conveniently output arrays:

print join ', ', @array;

And combine with map and keys to output hashes:

print join ', ', map {"$_ : $hash{$_}"} keys %hash;

Use the qq operator if you want to output quotes around the data:

print join ', ', map {qq("$_" : "$hash{$_}"}) keys %hash;


回答4:

If you're looking to ease translations you should consider using one of the L10n/i18n CPAN modules that are available. Specifically, a good overview of why your approach will end up falling short is written up as part of the Local::Maketext docs.

Another great module that pairs nicely with Locale::Maketext is Locale::Maketext::Lexicon. This allows you to use more standard localization formats such as gettext's .po/.mo files which have GUI tools to help translators work through all the text that needs translating. Locale::Maketext::Lexicon also comes with a helper script (xgettext.pl) that helps keep your localization files up-to-date with your templates or modules that have text that need translating. I've had very good results with this kind of setup in the past.



回答5:

It seems you want to have a different way of parsing strings. I would advise you not to do this. The only one who is seeing the syntax with the %d in it is the developer and he will exactly understand what is meant. The printf syntax is powerful because of the options like padding, decimals etc.

I think you want to use more a replace method. It is perlish to do s/{num}/$n/.



回答6:

In light of your comment about being for translators I suggest writing a perl script that strips all printf() and tabulates them in an easier more translator friendly manner.

Something like this:

while(<>)
{
    #regex for striping printf

    #print in tabulated form
}

If you print out the line number too you can easily write another program to replace the translated text.

This solution wouldn't take you any longer than re-factoring away from printf() and it's reusable.


I would definitely stick with printf(), it's standard across many languages.

It has almost become a standard for string output. Like i is for for loops.



回答7:

Generally answer from Draegtun is great, but if you'd need something smaller (i.e. less memory), and not as powerful you can easily do it using this function:

sub printX {
    my ( $format, $vars ) = @_;

    my @sorted_keys = sort { length($b) <=> length($a) } keys %{ $vars };
    my $re = join '|', map { "\Q$_\E" } @sorted_keys;

    $format =~ s/ \{ \s* ($re) \s* \} /$vars->{$1}/xg;

    print $format;
}


回答8:

well, perl has printf function... wait, do you want something like python's string formatting with dict?

 >>> print '%(key)s' % {'key': 'value'}
 value

mmm, I don't know something like that exist in perl... at least not this "easy"... maybe Text::Sprintf::Named can be your friend