I'm trying to validate the user input. If the user inputs an integer number it works like expected. However, if the user inputs a non-integer string, the variable userTickets gets assigned value 0, but prints Try again! It must be more than zero: many times. To be exact, it prints len(input) times and I don't understand why.
Also tried achieving desired result using fmt.Scanf("%d", &usertickets) but got an identical result.
Why does it behave this way and how can I write a workaround for it?
package main
import "fmt"
func main() {
var remainingTickets uint = 50
var userTickets uint
fmt.Print("Enter the number of tickets you want to purchase: ")
fmt.Scan(&userTickets)
for userTickets > remainingTickets {
fmt.Printf("We only have %v tickets available!\n", remainingTickets)
fmt.Print("Try again! Enter the number of tickets: ")
fmt.Scan(&userTickets)
}
for userTickets == 0 {
fmt.Print("Try again! It must be more than zero: ")
fmt.Scan(&userTickets)
}
fmt.Printf("Remaining tickets: %v\n", remainingTickets-userTickets)
}
Scan is able to determine that the input isn't numeric without reading the entire contents of stdin. This is why you validation logic loops for len(input) when non-numeric. While you can use a Scanner as well (and people do recommend that approach), below is an approach similar to yours. Note that all validation checking is done within a single "for" loop as well:
package main
import (
"fmt"
"strconv"
)
func main() {
var remainingTickets uint64 = 50
fmt.Print("Enter the number of tickets you want to purchase: ")
for {
var userTickets string
fmt.Scanln(&userTickets)
// parse to make sure we get a positive (unsigned) integer
u64, err := strconv.ParseUint(userTickets,10,64)
// make sure it's a postive integer and not equal to zero
if err != nil || u64==0{
fmt.Print("Try again! You must enter a number greater than zero: ")
continue
}
// check to make sure we have enough tickets left
if u64 > remainingTickets {
fmt.Printf("We only have %v tickets available!\n", remainingTickets)
fmt.Print("Try again! Enter the number of tickets: ")
continue
}
// update remaining tickets
remainingTickets -= u64
break
}
fmt.Printf("Remaining tickets: %d\n", remainingTickets)
}
Related
Am a beginner of GoLang here is a sample code from a tutorial.
func main() {
for {
var name string
var email string
var userTickets uint
// ask user for info
fmt.Println("Input your Name please")
fmt.Scan(&name)
fmt.Println("Input your Email please")
fmt.Scan(&email)
// ask user for number of tickets
fmt.Println("Input number of ticket")
if _, err := fmt.Scanln(&userTickets); err != nil {
fmt.Println(err)
}
}
}
Here is an interesting thing I found:
if I entered "-1" in "Input number of ticket". It will throw an error since userTickets is uint. With that err it will also put an "enter/next line" for "Input your Name please" in the next loop.
The result would look like this
Input your Name please
Test
Input your Email please
Test
Input number of tickect
-1
expected integer
Input your Name please <= this input is skipped
Input your Email please
So just wondering why this heppened? How can I resolve that (without changing type from uint to int)?
So just wondering why this heppened?
Because -1 cannot be stored in an uint.
How can I resolve that (without changing type from uint to int)?
You cannot.
(The right thing to do is not to use fmt.Scan but to always read whole lines into a string and parse the string.)
I want to parse a string like "12.49" into a *big.Int in Go. The resulting *big.Int should represent the amount of cents in the given value, in this case 1249. Here are some more examples of inputs and their expected outputs:
"3": 300
"3.1": 310
".19": 19
I already tried working with *big.Float and its Int function, but realized, that *big.Float does not provide arbitrary precision.
Right now I'm using this algorithm, but it seems fragile (Go Playground link):
func eurToCents(in string) *big.Int {
missingZerosUntilCents := 2
i := strings.Index(in, ".")
if i > -1 {
missingZerosUntilCents -= len(in) - i - 1
if missingZerosUntilCents < 0 {
panic("too many decimal places")
}
}
in = strings.Replace(in, ".", "", 1)
in += strings.Repeat("0", missingZerosUntilCents)
out, ok := big.NewInt(0).SetString(in, 10)
if !ok {
panic(fmt.Sprintf("could not parse '%s' as an interger", in))
}
return out
}
Is there a standard library function or other common way to parse currencies in Go? An external library is not an option.
PS: I'm parsing Nano cryptocurrency values, which have 30 decimal places and a maximum value of 133,248,297.0. That's why I'm asking for *big.Int and not uint64.
Update: Seems like this solution is still buggy, because an inaccurate result is reported after multiplication: https://play.golang.org/p/RS-DC6SeRwz
After revisiting the solution with *big.Float, I realized, that it does work perfectly fine. I think I forgot to use SetPrec on rawPerNano previously. I'm going to provide an example for the Nano cryptocurrency, because it requires many decimal places.
This works as expected (Go Playground link):
func nanoToRaw(in string) *big.Int {
f, _ := big.NewFloat(0).SetPrec(128).SetString(in)
rawPerNano, _ := big.NewFloat(0).SetPrec(128).SetString("1000000000000000000000000000000")
f.Mul(f, rawPerNano)
i, _ := f.Int(big.NewInt(0))
return i
}
Thanks #hymns-for-disco for nudging me in the right direction!
First the poor code:
package main
import (
"fmt"
)
type rocketship struct {
passengers string
gear string
func mapper(separators map[string]string) rocketship {
maps := rocketship{
passengers: separators["passengers"],
gear: separators["gear"],
}
return maps
}
func main() {
// How to pull the string
for _, passer := range passengers {
// count each passenger
sum = passer++
if err != nil {
fmt.Println("No passengers coming today", err)
return
}
fmt.Println("There are", sum, "passengers onboard today")
}
What are the steps here?
Pull a long name out of a struct (if a passenger is on the ship), can be "John Fitzgerald Kennedy" or "J.F.K" or "JFK".
Add this passenger in the tally
Continue till all passengers are counted
Sum the passengers and print how many there were.
The issue:
How can a string from a struct be converted to one occurrence?
How can the occurrences be summed together to be printed?
The final print could be "There are 6 passengers onboard today"
I have been looking and been struggling with this for a bit. I found this other Stack Overflow question which put me in the right direction but isn't working: Quick way to detect empty values via reflection in Go.
My current code looks like this:
structIterator := reflect.ValueOf(user)
for i := 0; i < structIterator.NumField(); i++ {
field := structIterator.Type().Field(i).Name
val := structIterator.Field(i).Interface()
// Check if the field is zero-valued, meaning it won't be updated
if reflect.DeepEqual(val, reflect.Zero(structIterator.Field(i).Type()).Interface()) {
fmt.Printf("%v is non-zero, adding to update\n", field)
values = append(values, val)
}
}
However I have fmt.Printf which prints out the val and the reflect.Zero I have, and even when they both are the same, it still goes into the if statement and every single field is read as non-zero even though that is clearly not the case. What am I doing wrong? I don't need to update the fields, just add them to the slice values if they aren't zero.
For starters, you are adding val to the values slice if val IS the zero value, not if it isn't. So you should probably check if !reflect.DeepEqual(... instead of what you have. Other than that, your code seems to work fine:
package main
import (
"fmt"
"reflect"
)
type User struct {
Name string
Age int
Email string
}
func main() {
user, values := User{Name: "Bob", Age: 32}, []interface{}(nil)
structIterator := reflect.ValueOf(user)
for i := 0; i < structIterator.NumField(); i++ {
field := structIterator.Type().Field(i).Name
val := structIterator.Field(i).Interface()
// Check if the field is zero-valued, meaning it won't be updated
if !reflect.DeepEqual(val, reflect.Zero(structIterator.Field(i).Type()).Interface()) {
fmt.Printf("%v is non-zero, adding to update\n", field)
values = append(values, val)
}
}
}
outputs the following (Go Playground Link):
Name is non-zero, adding to update
Age is non-zero, adding to update
So it is correctly seeing that the Email field is not initialized (or more correctly, contains the zero value for string).
Is there a way to convert a string (which is essentially a huge number) from string to Big int in Go?
I tried to first convert it into bytes array
array := []byte(string)
Then converting the array into BigInt.
I thought that worked, however, the output was different than the original input. So I'm guessing the conversion didn't do the right thing for some reason.
The numbers I'm dealing with are more than 300 digits long, so I don't think I can use regular int.
Any suggestions of what is the best approach for this?
Package big
import "math/big"
func (*Int) SetString
func (z *Int) SetString(s string, base int) (*Int, bool)
SetString sets z to the value of s, interpreted in the given base, and
returns z and a boolean indicating success. The entire string (not
just a prefix) must be valid for success. If SetString fails, the
value of z is undefined but the returned value is nil.
The base argument must be 0 or a value between 2 and MaxBase. If the
base is 0, the string prefix determines the actual conversion base. A
prefix of “0x” or “0X” selects base 16; the “0” prefix selects base 8,
and a “0b” or “0B” prefix selects base 2. Otherwise the selected base
is 10.
For example,
package main
import (
"fmt"
"math/big"
)
func main() {
n := new(big.Int)
n, ok := n.SetString("314159265358979323846264338327950288419716939937510582097494459", 10)
if !ok {
fmt.Println("SetString: error")
return
}
fmt.Println(n)
}
Playground: https://play.golang.org/p/ZaSOQoqZB_
Output:
314159265358979323846264338327950288419716939937510582097494459
See Example for string to big int conversion.
package main
import (
"fmt"
"log"
"math/big"
)
func main() {
i := new(big.Int)
_, err := fmt.Sscan("18446744073709551617", i)
if err != nil {
log.Println("error scanning value:", err)
} else {
fmt.Println(i)
}
}
Output:
18446744073709551617