Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Using (s)printf()

by reptile (Monk)
on Jun 30, 2000 at 03:36 UTC ( [id://20519]=perltutorial: print w/replies, xml ) Need Help??

printf() and sprintf() work just like in C, with a few slight differences. printf() gives you a lot of control over the formatting of your values, which is what it was intended to do.

If you've ever wanted to make a nicely behaved, field-aligned report, round to an integer or specific decimal place, get octal or hexadecimal representations of your values, or just display your values in any other form imaginable, keep reading. printf() and sprintf() give you the utility to do that, and more.

printf() versus sprintf()

printf() and sprintf() look argumentally the same. That is, whatever arguments you pass to one, you can pass to the other without change. The difference is that printf() prints to a filehandle, and sprintf() returns the string printf() would have outputted.

To use printf() on a filehandle other than STDOUT, specify the filehandle you want to use just as you would with print, like so:

printf(HANDLE $format, @values);

The result will be printed to HANDLE.

sprintf doesn't take a filehandle, but instead returns the output into a string.

my $string = sprintf($format, @values);

The Format String

The format string in printf() is a number of tokens which describe how to print the variables you supply, and whatever else you want. Each variable format specifier starts with a %, is followed by zero or more of the optional modifiers, and ends with a conversion specifier.

A typical format string could look like this:

	"foo is %d"

Printed, it may look something like: 'foo is 12'. The %d is replaced by a variable specified after the format string argument. In this case, you would say:

	printf("foo is %d", $decimal);

so that %d is replaced with the value of $decimal. You can put as many specifiers in the format string as you like, with the same number of following arguments as there are specifiers. An example:

	printf("%d %s %f", $decimal, $string, $float);

Conversion Specifiers

You put these in your Format String. Each one, except %%, is replaced by the corresponding value in the printf argument list.

  • %% prints a single % (percent sign).
  • %c prints a single character with the given ASCII number (66 is printed as B, for example).
  • %s prints a given string
  • %d a signed integer in decimal
  • %u an unsigned (can't be negative) integer in decimal
  • %o an unsigned integer in octal
  • %x an unsigned integer in hexadecimal
  • %e a floating point number in scientific notation
  • %f a floating point number in fixed decimal notation
  • %g same as %e or %f, whichever printf thinks is best.
  • %X is the same as %x, except it uses upper-case letters
  • %E like %e, but with an upper-case 'E'
  • %G same as %E when scientific notation is used for the value.
  • %p a pointer; it outputs Perl's value address in hexadecimal.
  • %n an odd little bugger that *stores* (in the variable given for the position) the number of characters output so far.

Others that simply exist for backward compatibility:

  • %i synonym for %d
  • %D same as %ld (long decimal)
  • %U same as %lu (long unsigned decimal)
  • %O same as %lo (long octal)
  • %F same as %f

Format Specifiers

Each item below is optional (unless otherwise stated), and should be used in the order they appear here. If this is confusing, skip to the next section. It's intended as a reference, and copied from man 3 printf with some extra explanation and examples.

  • % - a percent sign. This is required, of course.
  • Zero or more of the following:
    • '#' (pound sign): Specifies that the value should be converted to an 'alternate form.' This has no effect on 'c', 'd', 'i', 'n', 'p', 's', and 'u' conversions. For 'o' (octal) conversions, this prepends a '0' to the beginning. For 'x' and 'X' (hexadecimal), 0x or 0X is prepended to the value. For 'e', 'E', 'f', 'g' and 'G', the value is always printed with a trailing decimal point (.), even if no numbers follow it. For 'g' and 'G', trailing zeros are not removed from the result.
      printf("%x", 10);		# prints just 'a', versus:
      printf("%#x", 10);		# prints '0xa'
      
    • '0' (zero): To specify zero-padding on a digit. The converted value is padded on the left with the specified number of zeros minus the number of digits to be printed. This is described in more detail below.
      printf("%03d", 7);		# prints '007' ;)
      printf("%03d", 153);		# prints '153'
      
    • '-' (minus): Specifies a negative field width. This indicates the value should be left-adjusted on the boundary, versus the default right-adjusted. See below for more on how to specify field widths.
      printf("%5s", 'foo');		# prints '  foo'
      printf("%-5s", 'foo');		# prints 'foo  '
      
    • ' ' (a space): To specify that a blank should be left before a positive number.
      printf("% d", 7);		# prints ' 7'
      printf("% d", -7);		# prints '-7'
      
    • '+' (plus sign): This specifies that a sign always be placed before the value. '+' overrides ' ' (space) if both are used.
      printf(*quot;%+d", 7);		# prints '+7'
      
  • A decimal digit specifying the minimum field width. Using the '-' modifier (see above) will left-align the value, otherwise it is right-aligned. With the '0' modifier for numeric conversions, the value is right-padded with zeros to fill the field width.
    printf("%5s", 'foo');		# prints '  foo'
    printf("%-5s", 'foo');		# prints 'foo  '
    
  • A precision value in the form of a period ('.'), followed by an optional digit string. If the digit string is omitted, a precision of zero is used. This specifies the minimum number of digits to print for 'd', 'i', 'o', 'u', 'x', and 'X' conversions. For 'e', 'E', and 'F' conversions, it is the number of digits to appear after the decimal point. For 'g' and 'G' conversions, it specifies the maximum number of signifigant digits. For the 's' (string) conversion, it is the maximum number of characters of the string to print. Use the latter to make sure long strings don't exceed their field width.
    printf("%.3d", 7);		# prints '007'
    printf("%.2f", 3.66666);	# prints '3.66'
    printf("%.3s", 'foobar');	# prints 'foo'
    
  • The character 'h' here will specify to treat the decimal value as a C type 'short' or 'unsigned short'.
    printf("%hd\n", 400000);	# prints '6784'
    
  • The character 'l' (ell) can be used to treat the value as a C 'long' type.
  • The character 'V' will interpret an integer as Perl's standard integer type.
    printf("%Vd", 7);		# prints '7'
    
  • Finally, the required conversion specifier. Valid conversions are listed in the previous section.

Leading Zeros

Say you have a number, something like 642, and you want to output it as 00642 instead. The %0nC specifier syntax lets you do just that, where 'n' is the field width, and C is the conversion specifier you want to use. A field width is the minimum (in this case) number of characters the value should fill. Any less than that, and the remainder is filled by prepending zeros on your value until it fits perfectly.

	printf("%05d", 642);		# outputs '00642'

You should note that certain conversions, like %f, are a little trickier. Floating point numbers (with %f) are always outputted with 6 places after the decimal point, unless you specify a precision with the '.' modifier (see below for a discussion of the '.' precision modifier). In other words, printing a value of '2' as %f will actually output as 2.000000. This means you have to take into account, when specifying the field width, that there are already 7 characters tacked on. To get the value of 2 to print with one leading zero, you have to use a field width of 9 (7 for the '.' and 6 zeros, 1 for the '2', and 1 for the leading zero).

All other specifiers act in this way, too. To find out how many characters are output by default for a specifier, output a value of 0 (zero) for it and count how many characters there are:

	# this outputs: '0, 0.000000, 0.000000e+00'
	printf("%d, %f, %e", 0, 0, 0);

You can also ask perl to count them for you:

	printf("There are %d characters\n", length(sprintf("%e", 0)));

Which should tell you there are 12 characters for 0 in scientific notation.

Padding with spaces

This is more or less the same as leading zeros, except it uses leading (or, if told, trailing) spaces to complete the field width. This is useful for lining up multiple lines of data into a report, for instance (though in that case, you may also want to specify a maximum field width to truncate long values - more on that below). The syntax is just like leading zeros, but drop the leading zero:

	printf("%6s", 'foo');		# prints '   foo'

By default, leading spaces are used, so values appear to be right-aligned in their field. To reverse this, put a '-' sign before the field width:

	printf("%-6s", 'foo');		# prints 'foo   '

For numeric values with default precision, like %f and %e, act here just like they do with leading zeros. %f, for example, won't have any padding unless you put a field width of more than 8.

Precision Modifier

The precision modifier tells printf() how many digits you want after the decimal point, if its a floating point specifier. If there are more digits than you specified, the value is rounded. If there are less, zeros are used to fill the space.

	printf("%.2f", 9.333333);		# prints '9.34'
	printf("%.2f", 9);			# prints '9.00'

For decimal values, the precision modifier has the same effect as the '0' modifier described above:

	printf("%.3f", 7);			# prints 007

For string values, it has the nice effect of specifying a maximum field width, where it will only print out the first n characters of the string. In combonation with the field width modifier described above, you can have a well-behaved-under-all-circumstances string field.

	printf("%.3s", 'foobar');		# prints 'foo'
	printf("%.10s", 'foobar');		# prints 'foobar'
	printf("%5.5s %5.5s", 'foobar', 'baz');	# prints 'fooba   baz'

Rounding Numbers with sprintf()

Ever wanted to round a floating point number to a decimal in perl? Or round it to an arbitrary decimal place? sprintf() gives you the ability to do that.

	# this sets $foo to '3'
	$foo = sprintf("%d", 3.14);

	# this sets $bar to '7.3531'
	$bar = sprintf("%.4f", 7.35309);

%d specifies to convert the given value to a decimal integer. The conversion rounds as necessary. %.4f specifies to convert the value given to a floating point number with a precision to 4 decimal places, rounding the value as needed.

Octal and Hexadecimal

You can convert your decimal based values to Hexadecimal and Octal values using printf() and sprintf(). To do so, specify the conversion as %o for octal, and %x for hexadecimal. %X is equivilant to %x, except the result is printed using capital letters.

	printf("%x", 15);	# prints 'f'
	printf("%X", 15);	# prints 'F'
	printf("%o", 15);	# prints '17'

As explained in the Format Modifiers section, using a '#' (pound sign) right after the % will convert the value to "alternate form." For %x and %X, it will prepend to the value '0x' and '0X' respectively. For %o, a single leading '0' (zero) is added. The extra characters using the # modifier are considered part of the field width.

	printf("%#x", 15);	# prints '0xf'
	printf("%#o", 15);	# prints '017'
	printf("%#4x", 15);	# prints ' 0xf'

In the last example, the field width of 4 is specified. Since the # modifier adds two extra characters to the value, it ends up taking 3 characters in total. Thus the single leading space.

When to Use, and When Not to

While printf() is more powerful than print(), it is also less efficient and more error-prone. The printf manpage tells us not to use printf() where a simple print() would suffice, and that's good advice. You should use printf where you need the control over a value's format that printf() gives you, when you want to use field widths (and don't care much for perl's reports), and when you want to round floating point numbers.

Errors and Corrections

Please post any errors and corrections below. I'll humbly and embarassingly admit my mistake and fix it. Helpful comments would be greatly appreciated.

Replies are listed 'Best First'.
RE: Using (s)printf()
by csorensen (Beadle) on Jun 30, 2000 at 21:53 UTC
    I've been trying to figure out the best way to output a logfile as a formatted report. This has been really helpful!!
    thank you
Re: Using (s)printf()
by Anonymous Monk on Apr 23, 2001 at 23:27 UTC
    I keep
    http://www.perlmonks.org/index.pl?node=Using%20(s)printf()&lastnode_id=954
    bookmarked, since it is by far the most helpful s/printf resource I have found to date.

    So: having shmoozed, care to take a question ? -) I have been working with the folks who are rewriting Math::BigFloat, and we have come to a (partial) disagreement. I am of the opinion that zero should be represented, in scientific notation, with exponent == 0; i.e. by something like '0e0'. This belief is at least in part due to the fact that every printf I've encountered renders

    printf '%e\n' 0

    as something like

    0.000000e+00

    Does this representation seem reasonable to you? Is this behavior specified anywhere?
    TIA, Tom_Roche@ncsu.edu

      Quoting the article you like so well:

      %g same as %e or %f, whichever printf thinks is best.
      then note that
      printf "%g", 0
      produces simply "0" so 'printf thinks "0" is best'. (:

              - tye (but my friends call me "Tye")
Re: Using (s)printf()
by Anonymous Monk on Aug 31, 2001 at 16:05 UTC
    I tried to use the example given in the text. This is what I get (using perl v5.6.1 built for cygwin):      printf("%.2f", 9.333333);

    This prints "9.33". Actually, this is what I expect, but the article says it should be 9.34.

    If I try the following:      printf("%.1f", 9.55);

    I get 9.6, which is OK, but if I try      printf("%.1f", 43.55);

    I get 43.5, which is rounded the wrong way.

    Can someone explain this? (Please send me an email (oz1lo001@sneakemail.com)).

      • The first one (9.3333 -> 9.34) is a typo in the original.
      • The second and third one are easily explained by the following
        printf("%.15f", 9.55); # prints 9.550000000000001 printf("%.15f", 43.55); # prints 43.549999999999997
        This means that due to the limitations of the internal representation, the literal 9.55 is in fact 9.550000000000001 which rounds to 9.56. The same goes for 43.55 but there the representation is slightly smaller than 43.55 so a rounding down occurs.

      -- Hofmator

        Actually, this isn't quite the whole story. Most everyone follows the IEEE convention of "round towards nearest or even."
        Examples (rounding all of these to the one's place):

        2.51 becomes 3 (this is the 'nearest' rule, which always comes first)
        2.49 becomes 2 (again, 'nearest')

        However, what happens if you have 2.50 ? Which way do you round it... 'tis no nearer to 2 than to 3. The IEEE standard says if there is a tie, round to the even number.

        2.50 becomes 2
        3.50 becomes 4

        You have to pick up or down... this method is consistent and thus tends to make your errors (statistically) smaller.

        Thank you for your explanation. How do I circumvent this problem? I have a little perl-program that pulls these numbers from a file and it has to round them correctly. Isn't there some way to store a number exactly?
Question:
by Anonymous Monk on Nov 07, 2001 at 02:33 UTC
    say you have:
    $half = 1/2;
    and you want to print $half so it appears as:
    .500

    printf ("%.3f", $half);
    gives:
    0.500
    how do i get rid of that extra zero?

      Use split() to get rid of it.
        sub _round {
        my $digits = shift;
        my $num = shift;

        my $add = 5 * (10 ** -($digits + 1));

        $num += $add;

        my ($int,$frac) = split /\./,$num;

        $frac = substr($frac, 0, $digits);

        $num = join '.', $int, $frac;

        return $num;
        }
Re: Using (s)printf() .......what is "%vd"
by Anonymous Monk on Dec 27, 2002 at 06:39 UTC
    If found an interesting solution to a problem of ANDing 2 IPs
    $anded = sprintf "%vd\n", 192.16.16.1 & 255.255.248.0;
    How is %vd interpreted here
    Ramprasad A Padmanabhan
Re: Using (s)printf()
by trvth (Initiate) on May 29, 2012 at 21:17 UTC
    Under "Format Specifiers" I see "printf(*quot;%+d", 7);" -- it looks like that leading asterisk "*" should be an ampersand "&" so that the double quote character will be displayed instead of "*quot;"
      printf(*quot;%+d", 7); # prints '+7'

      should be:

      printf("%+d", 7); # prints '+7'
Re: Using (s)printf()
by Anonymous Monk on Aug 01, 2012 at 16:59 UTC

    Your statement and example under "Rounding Numbers with sprintf()" is incorrect and misleading. Using "%d" does NOT round a float as necessary, it merely truncates everything after the decimal. Thus, printf("%d", 3.99); # will print 3, not 4.

    # To properly 'round a float as necessary': printf("%.0f", 3.49); # This will print 3 printf("%.0f", 3.51); # This will print 4

      Perl exhibits strange behaviour after moving from x32 to x64 platform:

      Before:

      # perl -e 'printf("%.2f", 6.685);' 6.68

      # perl -v This is perl, v5.10.1 (*) built for i386-linux-thread-multi

      After:

      # perl -e 'printf("%.2f", 6.685);' 6.69

      # perl -v This is perl, v5.10.1 (*) built for x86_64-linux-thread-multi

      Any changes in rounding algorithm?
Re: Using (s)printf()
by Br'er Rabbit (Novice) on Aug 31, 2016 at 13:17 UTC
    Having studied the article, I, the newest monk, still can't cipher how to print two floating-point numbers so they'll align On The Decimal, so the numbers may be added properly, pencilularly. e.g., print 0.12345 and -123456.12 to align thusly (oh, please let my spaces be preserved!):
    0.12345 -123456.12
    I know I'm not the only monk that seeks this. There is that other fellow in Andalusia.

      Try this

      perl -E 'printf("%12.4f\n",12.34)'

      gives

      >     12.3400

      see the docs for sprintf for much more detail :) but in brief it's "%<length>.<dp>f"

      Ahhh, but Re-studying the article, the thought arises that the answer might lie with "sprintf()", using it to convert to a string which can be fed to string-processing functions to find the location of the '.' and to identify, and reserve, the parts before and after. Perhaps?

        Trailing whitespace can be a bit tricky. Maybe something like:

        c:\@Work\Perl\monks>perl -wMstrict -le "for my $n (qw( 0.12345 -0.12345 123456.12 -123456.12 123456.12345 -123456.12345 )) { my ($left, $right) = (7, 5); my $s = sprintf qq{%${left}s.%-${right}s}, split m{[.]}xms, $n, 2; print qq{'$s'}; } " ' 0.12345' ' -0.12345' ' 123456.12 ' '-123456.12 ' ' 123456.12345' '-123456.12345'


        Give a man a fish:  <%-{-{-{-<

Re: Using (s)printf()
by Evel (Acolyte) on Feb 25, 2015 at 05:50 UTC
    sometime seems if the second parameter including a '%', if I need use printf to output in a file like csv. I always need using $print_out =~ s/%/%%/g; to replace the % to %% and then print in the file. I am not sure whether we have the better solution on this kinds of issue.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perltutorial [id://20519]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (5)
As of 2024-03-19 08:20 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found