I'm very new to Go, and am trying to get some experience by re-writing some of my Python code in Go. Below is a function that takes an IP address (ipv4) in integer form and returns the string version. From 572779703
to 34.35.236.183
for example.
func int2ip(num int) (string) {
result := [4]int{}
for i, n := range []uint{24, 16, 8, 0} {
result[i] = (num >> n) & 255
}
return fmt.Sprintf("%d.%d.%d.%d", result[0], result[1], result[2], result[3])
}
I'm not only curious if there is a more straightforward or concise way to do this with Go, but am very curious if there is a way to use the []result
array in Sprintf()
without having to type out every index.
Something similar to:
fmt.Sprintf("%d.%d.%d.%d", result)
2 Answers 2
The proper way of doing this is to use the tools the language already gave you.
IPv4 usually are stored as big endian numbers, and Go already have an IP
type:
func int2ip(num int) net.IP {
var b [4]byte
binary.BigEndian.PutUint32(b[:], uint32(num))
return net.IPv4(b[0], b[1], b[2], b[3])
}
//or if you don't care about returning net.IP
func int2ipstr(num int) string {
var b [4]byte
binary.BigEndian.PutUint32(b[:], uint32(num))
return fmt.Sprintf("%d.%d.%d.%d", b[0], b[1], b[2], b[3])
}
//edit adding more details following @Schism's advice
Using Sprintf is usually fine and they worked on optimizing it fairly well, if you want to squeeze every drop of performance out of it, you could use a more "buffered" alternative like :
func int2ipb(num int) string {
b, buf := [4]byte{}, make([]byte, 0, 15) //15 = max number of bytes in an ip
binary.BigEndian.PutUint32(b[:], uint32(num))
for _, n := range b {
buf = append(buf, '.')
buf = append(buf, strconv.Itoa(int(n))[:]...)
}
return string(buf[1:])
}
-
\$\begingroup\$ Thats great! I did not think to look if Go already had an IP type. \$\endgroup\$tMC– tMC2014年08月25日 16:08:43 +00:00Commented Aug 25, 2014 at 16:08
Another way to do this would be to change result
to a string
slice and then use strings.Join
.
I'm not super familiar with Go, but this works for me:
func int2ip(num int) (string) {
var result = make([]string, 4)
for i, n := range []uint{24, 16, 8, 0} {
result[i] = strconv.Itoa((num >> n) & 255)
}
return strings.Join(result, ".")
}
Alternatively, leave the first line as var result [4]string
, and change the last line to use result[:]
. (This coerces the array into a slice.)
-
\$\begingroup\$ Creating 2 slices is just inefficient, specially a string slice. \$\endgroup\$OneOfOne– OneOfOne2014年08月23日 03:25:53 +00:00Commented Aug 23, 2014 at 3:25
-
1\$\begingroup\$ @OneOfOne I appreciate your input; as mentioned, I'm not familiar with Go. However, I note that your answer specifically does not address the part that the OP was "very curious" about. Could you please add an explanation in your answer regarding why it's better to leave in the
Sprintf
? \$\endgroup\$Schism– Schism2014年08月23日 21:20:11 +00:00Commented Aug 23, 2014 at 21:20 -
\$\begingroup\$ Mainly because of
[]string
there's more copying and moving things around, I'd need to benchmark it though, your method might be good. \$\endgroup\$OneOfOne– OneOfOne2014年08月23日 22:02:24 +00:00Commented Aug 23, 2014 at 22:02 -
\$\begingroup\$ @OneOfOne No, I trust yours is probably better; I expect that checkmark to move to yours. I was just suggesting you add that discussion to your answer to make it even better :-) \$\endgroup\$Schism– Schism2014年08月24日 00:15:44 +00:00Commented Aug 24, 2014 at 0:15
-
\$\begingroup\$ I updated it, I'm terrible at explaining stuff :| \$\endgroup\$OneOfOne– OneOfOne2014年08月24日 00:38:27 +00:00Commented Aug 24, 2014 at 0:38