Reputation: 147
I want to normalize zipcodes to be 5 digits long with zeros replacing any missing characters like so:
"95616" >> "95616"
"854" >> "00854"
"062" >> "00062"
"0016" >> "00016"
I have tried using sprintf like so sprintf("%05s", zipcode)
and like so
sprintf("%0.5d", zipcode)
. Both give incorrect answers. Using the s
:
"95616" >> "95616"
"854" >> " 854"
"062" >> " 062"
"0016" >> " 0016"
This is the correct number of characters, but using spaces, not zeros.
Using the d
:
"95616" >> "95616"
"854" >> "00854"
"062" >> "00050"
"0016" >> "00014"
What is the proper use of sprintf() in this case?
Upvotes: 11
Views: 9698
Reputation: 3734
whirlwin's solution seems to me a valid (general padding) solution. Just improve it with max :
zipcode.insert 0, '0' * ([5 - zipcode.length, 0].max)
To check it :
az = ["95616", "854", "062", "0016", '1', '123456']
az.collect {|zipcode| zipcode.insert 0, '0' * (5 - zipcode.length) }
$ ruby -w t.rb
t.rb:2:in `*': negative argument (ArgumentError)
Add max to cope with negative values :
az = ["95616", "854", "062", "0016", '1', '123456']
az.collect {|zipcode| zipcode.insert 0, '0' * ([5 - zipcode.length, 0].max) }
p az
$ ruby -w t.rb
["95616", "00854", "00062", "00016", "00001", "123456"]
Upvotes: 2
Reputation: 147
Thank you for your input everyone. I was looking to learn more about sprintf and specifically to use sprintf in the solution, and all the ideas were really helpful. I eventually figured out a bit of a hacky solution to my problem that seems to be flexible and that hasn't been mentioned yet, so I thought I would post it.
As I mentioned, sprintf("%05s", zipcode)
would return spaces instead of zeros if a number string was not long enough, but it did return the correct number of spaces -- so I just chained a gsub
to the end to replace the spaces with zeros.
sprintf("%05s", zipcode).gsub!(" ", "0")
Please let me know if there are any significant problems with this solution. I have tested it and so far it seems to work.
Upvotes: 0
Reputation: 3379
Your usage of sprintf is fine. Your problem is that 062 (and 0016) is octal (as is any integer that starts with a 0), and it becomes 50 when converted to base10.
The solution is to get rid of that 0 before it hits your Ruby app. Presuming that it's a string (because your examples show strings), you can do something like this:
"062".gsub /^0/, ''
And then carry on with the padding and print formatting.
The other way is to knowingly print it as an octal if you know it starts with a 0:
"%05o" % 062 # => "00062"
Of course, if you have control over the input, your best bet is to ensure people can't break your code by inputting numbers you don't expect. eg.
"%05s" % 0xff0055 # => 16711765
"%05x" % 0xff0055 # => "ff0055"
Checking inputs changes the problem from being a formatting one to a validation one, and it's better to prevent than treat.
(the quirky % syntax is sugar for printf
/sprintf
)
Upvotes: 8
Reputation: 54984
You want %05d. Also String#% as a shortcut:
"%05d" % "123" #=> 00123
Upvotes: 6
Reputation: 80065
Don't torture yourself with sprintf
:
puts "123".rjust(5, '0') # => 00123
Upvotes: 27
Reputation: 16521
While this approach does in fact not use sprintf
, it should solve your problem in 1 line of code:
zipcode.insert 0, '0' * (5 - zipcode.length)
Upvotes: 0