Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chriss Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

string formating help 1

Status
Not open for further replies.

quebasic2

Programmer
Dec 17, 2002
174
IN
I have a varible called $Red_Subtotal. The number can vary anywhere from 1 to in the thousands. How can I check to see if the number has two decimal places, if it does not, it will add .00 to the end of the string. It will not, however, change a number like $24.32. I also want my program to add a comma if the number is over 999. example:

Original number: 24
Output: 24.00

Original number: 2400
Output: 2,400.00

Original number: 24.43
Output: 24.43

Original number: 2400.25
Output: 2,400.25

Thanks in advance. I am converting a php form processor to perl, and I used to preform this task with the number_format command, but perl apparently does not a have a similar command. If there is one, please notify me.
 
$Red_Subtotal !~ /\./ && $Red_Subtotal =~ s/^(.*)$/$1.00/;
Searches for a decimal in a the scalar (as a string) and if one is not found, it adds a .00 to the end. Depending on how the value is set (if computed or if input), you could have "14." with a tailing decimal. It would not add the 00 to the end. As this is not likely, I wouldn't be too concerned. Could throw another line in to test for that.

Actually, now that I think about it,
$Red_Subtotal = sprintf("%.2f",$Red_Subtotal);
is a much better, catch-all way of doing it. Even handles a trailing decimal.

I'll tinker with that comma separation one a little more, but it didn't jump out at me like the first one. Heck, there might be a perl function to do these things already, dunno. ----------------------------------------------------------------------------------
...but I'm just a C man trying to see the light
 
Well, this isn't quite what I envisioned, but it gets the job done. The extra reverse junk is because perl runs regex left-to-right, and for adding commas, it's best to add from right-to-left every three.
Code:
$_ = '1345678.99';
$_ = reverse $_;
s/(\d{3})(?!\Z)/$1,/g;
$_ = reverse $_;
print;
The (?!\Z) looks ahead to make sure it hasn't reached the end of the string (beginning, in reverse). Without it, multiples of three digits would have leading commas (,123,456 instead of 123,456).

I do hope there's an easier, cleaner way to do this. ----------------------------------------------------------------------------------
...but I'm just a C man trying to see the light
 
One way to commify that I've come across is
Code:
1 while $Red_Subtotal =~ s/^(\d+)(\d{3})/$1,$2/;
This must be done AFTER you format the decimal places or sprintf will complain of a non-numeric value since commas aren't valid in numeric variables (int, float, etc.)

jaa
 
Thanks for all the help, but can you explain how some of the formatting works, not just that it works. I am a newbie in perl.
 
One more thing, does the above formating take care of numbers like 12.5, or would it just leave it like that because of it already had a decimal?
 
Second question is easier. The sprintf() will handle 12.5 as a number or string and make it '12.50' as a string. If you do some kind of math to it, it'll revert back to its simpliest form (12.5). Example:
Code:
$a = 12.5;
$a = sprintf("%.2f",$a); #is now 12.50
print "$a\n";
$a += 1; #add one to 12.50
print "$a\n"; #is now 13.5, not 13.50
printf/sprintf are taken straight from C. They take a format string and then a list of parameters. In the format string, you can specify what types of values to output by a % followed by a letter (f = float, d = integer, s = string, u = unsigned integer, x = hexadecimal value...I think there's others). In between the % and the letter, you can specify how you want it to be output. I'll just list some examples:
%10s a string that fills at least 10 characters
%3d an integer that fills at least 3 char
%03d an int that fills at least 3 char, but filling with leading zeros instead of spaces
%5f a floating point number filling 3
%5.2f a float taking up five characters, including one as the decimal itself and two being on the right of the decimal

Any of these can be prefixed with a negative sign to make it left-justified instead of right (padding on right instead of left if you perfer to think of it that way).
Code:
printf("%4d",34);  #'  34'
printf("%04d",34); #'0034'
printf("%-4d",34); #'34  '
It will round decimals down to, or add zeros up to whatever %.#f you choose. If the rest of the number or string is larger than what's specified, it generally just flows over.
Code:
printf("%3d",1234); #'1234' despite the 3
Hope this has been of some help. I'll let justice talk about his soln, it's nicer than mine. ----------------------------------------------------------------------------------
...but I'm just a C man trying to see the light
 
As for the while loop formatting...

Take the number '1234567890.123'.
The while loop will keep looping as long as the substitution (s///) matches. The body of the loop, the '1', does nothing--everything happens in the test expression.
The regex grabs all digits from the beginning of the string except for the last three and stuffs them into $1, the last three go into $2, so at the first test:
Code:
(  \d+  )(\d{3})
 1234567   890   .123
(  $1   )( $2  ) ^non-digit
now the string is '1234567,890.123'.
Next time through:
Code:
(\d+ )(\d{3})
 1234   567  ,890.123
( $1 )( $2 ) ^non-digit
String becomes '1234,567,890.123'.
Last time through (you get the idea):
Code:
(\d+)(\d{3})
  1    234  ,567,890.123
($1 )( $2 ) ^non-digit
The final string is '1,234,567,890.123'.
The next time through the loop the test fails and the loop exits.

jaa
 
So this is every thing?
$Red_Subtotal = 1002.9;
$Red_Subtotal = reverse $Red_Subtotal;
s/(\d{3})(?!\Z)/$1,/g;
$Red_Subtotal = reverse $Red_Subtotal;
$Red_Subtotal = sprintf("%.2f",$a);
1 while $Red_Subtotal =~ s/^(\d+)(\d{3})/$1,$2/;
print $Red_Subtotal;
#$Red_Subtotal now is 1,002.90?


 
completely leave out this junk:
Code:
$Red_Subtotal = reverse $Red_Subtotal;
s/(\d{3})(?!\Z)/$1,/g;
$Red_Subtotal = reverse $Red_Subtotal;
It does the same thing as that while loop, but not quite as pretty. Just be sure to do the sprintf() before the while loop, and you should be okay to display.

And make the sprintf like this:
[tt]
$Red_Subtotal = sprintf("%.2f",$[red]Red_Subtotal[/red]);
[/tt]
sprintf() takes a format string (the % junk), then the values it has to format, and returns a formatted string that you can assign to something. So, all at once:
Code:
$Red_Subtotal = 1002.9;
$Red_Subtotal = sprintf("%.2f",$Red_Subtotal);
1 while $Red_Subtotal =~ s/^(\d+)(\d{3})/$1,$2/;
You can print it at the end to check if you'd like. ----------------------------------------------------------------------------------
...but I'm just a C man trying to see the light
 
There was one other thing that I wanted to do with the string. First, you should know that I added a $ to the end of each number: Red_Subtotal = "\$" . Red_Subtotal;. The highest amount that anyone can chalk up on my form is a little over a thousand. So the longest possible length of the number after all the chages have been made is $1,000.00 or 9 letters. However, when I list all the the person has bought, I want the numbers to line up properly:

$1,000.00
$25.00
NOT
$1,000.00
$25.00

So if the length of my string is not 9, I need to add some trailing spaces to the beginning of the string to make it 9.
If possible can you this function into some of the previous ones.

One more question:

As stated above, I am trying to convert my form from php to perl. In php, strings are aloud to do this:
$string = "
Blasd
ashfl
agafhs
";
Is that aloud in perl, or do I need to do something like:
$string = "Blasd\nashfl\nagafhs\n";

Thanks so much icrf and justice41. I would be lost without you.
 
You can use printf to format the output.
Code:
printf "%9s\n", $Red_Subtotal;
which will right justify $Red_Subtotal;

As to the multi-line string assigments, what you wrote will work. Another way to do it is using a heredoc:
Code:
my $string =<<EOT;
line one
second line
third
EOT
jaa
 
A much easier way to do this:
Code:
my @vars = (
1.1111,
234.55,
456.2045,
0.034859,
);

foreach (@vars) {
  write;
}

format STDOUT =
@######.##
$_
.
Cheers, Neil
 
Though of course this doesn't give you the grouping separators :-(
 
In the case of what I am doing, it would actually be harder to use arrays than just a varible. Thanks for the idea though.
 
Another quick question :)
When you submit a form to a perl script, is there any thing you need to do to use the form varibles, or can you use them immediately?
example:

<input type=&quot;text&quot; length=&quot;50&quot; maxlength=&quot;50&quot; name=&quot;whatever&quot;>

When I process my form, I should be able to use the varible $whatever immediately? Or, do I need to use some function to read the data into the varibles?
 
Look at search.cpan.org for the CGI module. You can just say [tt]$variable = param('whatever');[/tt] if you don't want anything complicated of object-oriented. ----------------------------------------------------------------------------------
...but I'm just a C man trying to see the light
 
So by useing the param command, I can assign form values to varibles?
 
Pretty much. If you send the data the the script using METHOD=POST you can read it and parse it from STDIN, but unless you're dealing with a lot of information (like a textarea the user fills out comments) it's probably not worth the time.

cpan.org should have respectable documentation on its use. ----------------------------------------------------------------------------------
...but I'm just a C man trying to see the light
 
On my form, I have the user input their email address. When I use the param command to assign that email address to avarible, won't there be a problem with and unescaped @ in the email?
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top