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

Dear perlmonks,
Sorry for my English.
I would like to hear your opinions about solution of following problem. I have some numbers and templates (like No-###-#####-#######) and I need to format this numbers according to the templates: change symbol '#' on digits of number. I created following function:
sub format_number { die 'Usage:format_number(<number>, <format>)!' unless scalar(@_) = += 2; my ($num, $format) = @_; my @form = split('', $format); @form = reverse @form; my @num = split('', $num); my ($symbol, @res); foreach(@form) { if(/^([Xx]|#)$/) { unless($symbol = pop @num) { $symbol = 0; } } else { $symbol = $_; } unshift @res, $symbol } return join('', @res); }
But I think that it's brute-force approach. Maybe somebody knows more elegant solution. I will be very grateful for your help.
Thanks.

Replies are listed 'Best First'.
Re: Format of number by template
by jmcnamara (Monsignor) on Feb 11, 2003 at 14:11 UTC

    The Number::Format module would work for this:
    #!/usr/bin/perl -w use strict; use Number::Format qw(:subs :vars); $NEG_FORMAT = "x"; print format_picture(123456789012345, 'No-###-#####-#######'); __END__ Prints: No-123-45678-9012345
    Note: the above code generates a warning in the module but that is probably easily fixed.

    --
    John.

Re: Format of number by template
by theorbtwo (Prior) on Feb 11, 2003 at 12:40 UTC

    I like the basic idea, but your right, the implementation could use some work. First off, a couple minor changes to how your doing it now. First off, your API is poorly defined -- the only API you seem to have is "change symbol '#' on digits of number", but your code will change #, X, or x. You should either document that, or remove it. Consider that a template of '#XJ42-##', plus a number of 100, will format to 01J42-00, not 1XJ42-00. Also, there are some innefficencies that you could get around easily. You can accumulate results with $res = $symbol . $res, which avoids having to keep around that list. Since $_ is gaurneteed to be one character long in your test, you don't need the ^...$ anchors. Also, you should use eq, rather then regex matches, when you can get away with it, in general, as they are much faster. Replace /^([Xx]|#)$/ with (($_ eq '#') || ($_ eq 'X') || ($_ eq 'x')) (or just ($_ eq '#'), if you decided that X isn't neccessary). Even ignoring that point, is there a reason you wrote [Xx]|#, rather then [Xx#]?

    Most importantly, you could avoid most of the work by using a s///e to do your matching and substution (the /e modifier means the right hand side is an expression, not a double-quoted string (e for execute). See perlop, under "regex quote-like operators".

    $_=reverse $form; s =~ s/#/pop @num || 0/e; return reverse $_;

    Hm. On an alternate route, you should be able to convert them to s?printf-style format strings.


    Warning: Unless otherwise stated, code is untested. Do not use without understanding. Code is posted in the hopes it is useful, but without warranty. All copyrights are relinquished into the public domain unless otherwise stated. I am not an angel. I am capable of error, and err on a fairly regular basis. If I made a mistake, please let me know (such as by replying to this node).

      Thank you for your corrections.
      Yes, my function changes #, X and x because a template can be like thit 'DSF-###-####' and like that - 'XXX-XXXXXX-XXXX'<code>. <br>If number is <code>2345 and template is 'Ro-XXX-XXXXX-XXX'<code> result has to be <code>'Ro-000-00002-345'.
      I used regular expression for compactness of code.Thanks for interenting idea about /e modifier. I haven't known it. (I don't know a lot of things yet I think ;)) ).
      Michael
Re: Format of number by template
by jmcnamara (Monsignor) on Feb 11, 2003 at 14:26 UTC

    You could also use unpack for this, preferably in conjuction with sprintf:
    #!/usr/bin/perl -wl use strict; my $str = 123456789012345; print join "-", "No", unpack("a3 a5 a7", $str); # Or slightly more flexible: # Zero pad the string to the required length $str = sprintf "%015s", $str; my $template = "###-#####-#######"; $template =~ s/-/ /g; $template =~ s/(#+)/'a' . length $1/eg; # Template is now "a3 a5 a7" print join "-", "No", unpack($template, $str); # You could even automate it a little more as follows: my $digits; $str = 12345; $template = "###-#####-#######"; $digits = $template =~ tr/#//; $template =~ s/-/ /g; $template =~ s/(#+)/'a' . length $1/eg; $str = sprintf "%0*s", $digits, $str; print join "-", "No", unpack($template, $str); __END__ Prints: No-123-45678-9012345 No-123-45678-9012345 No-000-00000-0012345

    Update: Added last example.

    --
    John.

Re: Format of number by template
by davis (Vicar) on Feb 11, 2003 at 12:59 UTC
    From what I understand of your intentions, you want to:
    1. Pad a number (with zeroes) out to a given number of digits (printf and its sister sprintf are useful for this)
    2. Break the padded number up into sections (substr is made for slicing up strings)
    3. Reformat these sections according to your "template" (printf calls it a "format")

    Here's one way to do it, step-by-step

    #!/usr/bin/perl use warnings; use strict; my $num = 34; #example number my $padded_num = sprintf("%015d",$num); #Step 1: Pad the number my $first = substr($padded_num, 0, 3); #Step 2: Break the padded numb +er up my $second = substr($padded_num, 3, 5); my $third = substr($padded_num, 8, 7); my $string = sprintf("NO-%s-%s-%s", $first, $second, $third); #Step 3: + Reformat the sections print $string . "\n";

    davis
    Is this going out live?
    No, Homer, very few cartoons are broadcast live - it's a terrible strain on the animator's wrist
      Davis, thanks for your reply. Your way isn't suit for me because template isn't fixed. I can have any numbers of templates and they can be different.
      So, it's hard to define format for sprintf.
      Michael
Re: Format of number by template
by Abigail-II (Bishop) on Feb 11, 2003 at 13:28 UTC
    Your may want to look into the perlform manual page. It might not be hard to translate your formats to Perl formats. But since you gave only a single example, it may be hard.

    Abigail