How to parse result from ovs-vsctl get interface <interface> statistics - parsing

Result example:
{collisions=0, rx_bytes=258, rx_crc_err=0, rx_dropped=0, rx_errors=0, rx_frame_err=0, rx_over_err=0, rx_packets=3, tx_bytes=648, tx_dropped=0, tx_errors=0, tx_packets=8}
This format is like JSON, but not JSON.
Is there an easy way to parse this into map[string]int? Like json.Unmarshal(data, &value).

If that transport format is not recursively defined, i.e. a key cannot start a sub-structure, then its language is regular. As such, you can soundly parse it with Go's standard regexp package:
Playground link.
package main
import (
"fmt"
"regexp"
"strconv"
)
const data = `{collisions=0, rx_bytes=258, rx_crc_err=0, rx_dropped=0, rx_errors=0, rx_frame_err=0, rx_over_err=0, rx_packets=3, tx_bytes=648, tx_dropped=0, tx_errors=0, tx_packets=8}`
const regex = `([a-z_]+)=([0-9]+)`
func main() {
ms := regexp.MustCompile(regex).FindAllStringSubmatch(data, -1)
vs := make(map[string]int)
for _, m := range ms {
v, _ := strconv.Atoi(m[2])
vs[m[1]] = v
}
fmt.Printf("%#v\n", vs)
}

Regexp by #thwd is an elegant solution.
You can get a more efficient solution by using strings.Split() to split by comma-space (", ") to get the pairs, and then split again by the equal sign ("=") to get the key-value pairs. After that you just need to put these into a map:
func Parse(s string) (m map[string]int, err error) {
if len(s) < 2 || s[0] != '{' || s[len(s)-1] != '}' {
return nil, fmt.Errorf("Invalid input, no wrapping brackets!")
}
m = make(map[string]int)
for _, v := range strings.Split(s[1:len(s)-1], ", ") {
parts := strings.Split(v, "=")
if len(parts) != 2 {
return nil, fmt.Errorf("Equal sign not found in: %s", v)
}
if m[parts[0]], err = strconv.Atoi(parts[1]); err != nil {
return nil, err
}
}
return
}
Using it:
s := "{collisions=0, rx_bytes=258, ...}"
fmt.Println(Parse(s))
Try it on the Go Playground.
Note: If performance is important, this can be improved by not using strings.Split() in the outer loop, but instead searching for the comma "manually" and maintaining indices, and only "take out" substrings that represent actual keys and values (but this solution would be more complex).
Another solution...
...but this option is much slower so it is only viable if performance is not a key requirement: you can turn your input string into a valid JSON format and after that you can use json.Unmarshal(). Error checks omitted:
s := "{collisions=0, rx_bytes=258, ...}"
// Turn into valid JSON:
s = strings.Replace(s, `=`, `":`, -1)
s = strings.Replace(s, `, `, `, "`, -1)
s = strings.Replace(s, `{`, `{"`, -1)
// And now simply unmarshal:
m := make(map[string]int)
json.Unmarshal([]byte(s), &m)
fmt.Println(m)
Advantage of this solution is that this also works if the destination value you unmarhsal into is a struct:
// Unmarshal into a struct (you don't have to care about all fields)
st := struct {
Collisions int `json:"collisions"`
Rx_bytes int `json:"rx_bytes"`
}{}
json.Unmarshal([]byte(s), &st)
fmt.Printf("%+v\n", st)
Try these on the Go Playground.

Related

Mask sensitive url query params

Say I have this url
https://example.com:8080?private-token=foo&authenticity_token=bar
And I have a function to determine whether to mask a param.
How can I mask the url, but maintaining the order of params.
Currently I have
u, err := url.Parse(originalURL)
if err != nil {
panic(err)
}
m, _ := url.ParseQuery(u.RawQuery)
for key := range m {
if toMask(key) {
m.Set(key, "FILTERED")
}
}
u.RawQuery = m.Encode()
return u.String()
But this would return url with the params being switched around.
https://example.com:8080?authenticity_token=FILTERED&private-token=FILTERED
First, the order of the params should not be of any importance.
But I can see some situation where this rule does not apply (eg when you hash an URL). In this case, you should normalize the URL before using it.
Finally to respond to your question, you cannot keep the order if using Query, as Values is a map, and map don't bother with ordering. You should thus work on the query using u.RawQuery.
u, err := url.Parse(originalURL)
if err != nil {
panic(err)
}
newQuery := ""
for i, queryPart := range strings.Split(u.RawQuery, ";") {
// you now have a slice of string ["private-token=foo", "authenticity_token=bar"]
splitParam := strings.Split(queryPart, "=")
if toMask(splitParam[0]) {
splitParam[1] = "FILTERED"
}
if i != 0 {
newQuery = newQuery + ";"
}
newQuery = splitParam[0] + "=" + splitParam[1]
}
u.RawQuery = newQuery
return u.String()
This code is just example. You have to better check for special cases or errors. You can also use regexp if you want to.

Accessing to a comment within a function in Go

I'm currently working on documentation generator that will parse Go code to produce a custom documentation. I need to access to the comments written inside a function. But I can't retrieve these comments in the AST, neither with the go/doc. Here is an example :
package main
import (
"fmt"
"go/doc"
"go/parser"
"go/token"
)
// GetFoo comments I can find easely
func GetFoo() {
// Comment I would like to access
test := 1
fmt.Println(test)
}
func main() {
fset := token.NewFileSet() // positions are relative to fset
d, err := parser.ParseDir(fset, "./", nil, parser.ParseComments)
if err != nil {
fmt.Println(err)
return
}
for k, f := range d {
fmt.Println("package", k)
p := doc.New(f, "./", 2)
for _, t := range p.Types {
fmt.Println("type", t.Name)
fmt.Println("docs:", t.Doc)
}
for _, v := range p.Vars {
fmt.Println("type", v.Names)
fmt.Println("docs:", v.Doc)
}
for _, f := range p.Funcs {
fmt.Println("type", f.Name)
fmt.Println("docs:", f.Doc)
}
for _, n := range p.Notes {
fmt.Println("body", n[0].Body)
}
}
}
It's easy to find GetFoo comments I can find easely but not Comment I would like to access
I have seen this post quite similar question Go parser not detecting Doc comments on struct type but for exported types
Is there any way to do that ? Thank you !
The problem is that the doc.New functionality is only parsing for documentation strings, and the comment inside the function is not part of the "documentation".
You'll want to directly iterate the ast of the files in the package.
package main
import (
"fmt"
"go/parser"
"go/token"
)
// GetFoo comments I can find easely
func GetFoo() {
// Comment I would like to access
test := 1
fmt.Println(test)
}
func main() {
fset := token.NewFileSet() // positions are relative to fset
d, err := parser.ParseDir(fset, "./", nil, parser.ParseComments)
if err != nil {
fmt.Println(err)
return
}
for k, f := range d {
fmt.Println("package", k)
for n, f := range f.Files {
fmt.Printf("File name: %q\n", n)
for i, c := range f.Comments {
fmt.Printf("Comment Group %d\n", i)
for i2, c1 := range c.List {
fmt.Printf("Comment %d: Position: %d, Text: %q\n", i2, c1.Slash, c1.Text)
}
}
}
}
}

Parse string into map Golang

I have a string like A=B&C=D&E=F, how to parse it into map in golang?
Here is example on Java, but I don't understand this split part
String text = "A=B&C=D&E=F";
Map<String, String> map = new LinkedHashMap<String, String>();
for(String keyValue : text.split(" *& *")) {
String[] pairs = keyValue.split(" *= *", 2);
map.put(pairs[0], pairs.length == 1 ? "" : pairs[1]);
}
Maybe what you really want is to parse an HTTP query string, and url.ParseQuery does that. (What it returns is, more precisely, a url.Values storing a []string for every key, since URLs sometimes have more than one value per key.) It does things like parse HTML escapes (%0A, etc.) that just splitting doesn't. You can find its implementation if you search in the source of url.go.
However, if you do really want to just split on & and = like that Java code did, there are Go analogues for all of the concepts and tools there:
map[string]string is Go's analog of Map<String, String>
strings.Split can split on & for you. SplitN limits the number of pieces split into like the two-argument version of split() in Java does. Note that there might only be one piece so you should check len(pieces) before trying to access pieces[1] say.
for _, piece := range pieces will iterate the pieces you split.
The Java code seems to rely on regexes to trim spaces. Go's Split doesn't use them, but strings.TrimSpace does something like what you want (specifically, strips all sorts of Unicode whitespace from both sides).
I'm leaving the actual implementation to you, but perhaps these pointers can get you started.
import ( "strings" )
var m map[string]string
var ss []string
s := "A=B&C=D&E=F"
ss = strings.Split(s, "&")
m = make(map[string]string)
for _, pair := range ss {
z := strings.Split(pair, "=")
m[z[0]] = z[1]
}
This will do what you want.
There is a very simple way provided by golang net/url package itself.
Change your string to make it a url with query params text := "method://abc.xyz/A=B&C=D&E=F";
Now just pass this string to Parse function provided by net/url.
import (
netURL "net/url"
)
u, err := netURL.Parse(textURL)
if err != nil {
log.Fatal(err)
}
Now u.Query() will return you a map containing your query params. This will also work for complex types.
Here is a demonstration of a couple of methods:
package main
import (
"fmt"
"net/url"
)
func main() {
{
q, e := url.ParseQuery("west=left&east=right")
if e != nil {
panic(e)
}
fmt.Println(q) // map[east:[right] west:[left]]
}
{
u := url.URL{RawQuery: "west=left&east=right"}
q := u.Query()
fmt.Println(q) // map[east:[right] west:[left]]
}
}
https://golang.org/pkg/net/url#ParseQuery
https://golang.org/pkg/net/url#URL.Query

How do I convert a string value in uint16 in Go lang?

I'm researching a solution in order to convert a string (in my case the string is "05f8") into a uint16.I made research but I don't find a solution.Does anyone know how to do this?
Thanks for your help!
Use strconv.ParseUint (doc).
var s = "05f8"
var base = 16
var size = 16
value, err := strconv.ParseUint(s, base, size)
value2 := uint16(value) // done!
Note that the output value is an uint64, you have to cast it to you type before using it.
Note (bis) that the size parameter control the maximum size of the uint to convert to, so the overflow check is done correctly.
If you are interested in turning a string into []uint16, you can do:
package main
import (
"fmt"
"golang.org/x/sys/windows"
)
func main() {
a, e := windows.UTF16FromString("05f8")
if e != nil {
panic(e)
}
fmt.Printf("%q\n", a) // ['0' '5' 'f' '8' '\x00']
}
or, if you are certain string contains no NUL bytes, you can do this:
package main
import (
"fmt"
"golang.org/x/sys/windows"
)
func main() {
a := windows.StringToUTF16("05f8")
fmt.Printf("%q\n", a) // ['0' '5' 'f' '8' '\x00']
}
https://pkg.go.dev/golang.org/x/sys/windows#StringToUTF16
https://pkg.go.dev/golang.org/x/sys/windows#UTF16FromString
By no means do I claim to be a go developer, and I welcome feedback on this, but I was trying to convert an env variable for a port from a string to uint16. I was able to get it working with:
File: main.go
package main
import (
"log"
"os"
"strconv"
)
var PORT = os.Getenv("PORT")
func main() {
portAsInt, err := strconv.ParseInt(PORT, 0, 16)
if (err != nil) {
log.Fatal(err)
}
// Use as needed with uint16()
log.Println("Listening on port:", uint16(portAsInt))
}
Running application:
PORT=3000 go run main.go;
# Expected Output:
# Listening on port: 3000

time.Parse with custom layout

I'm trying to parse this string pattern "4-JAN-12 9:30:14" into a time.Time.
Tried time.Parse("2-JAN-06 15:04:05", inputString) and many others but cannot get it working.
I've read http://golang.org/pkg/time/#Parse and https://gobyexample.com/time-formatting-parsing but it seems there aren't any examples like this.
Thanks!
Edit:
full code:
type CustomTime time.Time
func (t *CustomTime) UnmarshalJSON(b []byte) error {
auxTime, err := time.Parse("2-JAN-06 15:04:05", string(b))
*t = CustomTime(auxTime)
return err
}
parsing time ""10-JAN-12 11:20:41"" as "2-JAN-06 15:04:05": cannot
parse ""24-JAN-15 10:27:44"" as "2"
Don't know what you did wrong (should post your code), but it is really just a simple function call:
s := "4-JAN-12 9:30:14"
t, err := time.Parse("2-JAN-06 15:04:05", s)
fmt.Println(t, err)
Outputs:
2012-01-04 09:30:14 +0000 UTC <nil>
Try it on the Go Playground.
Note that time.Parse() returns 2 values: the parsed time.Time value (if parsing succeeds) and an optional error value (if parsing fails).
See the following example where I intentionally specify a wrong input string:
s := "34-JAN-12 9:30:14"
if t, err := time.Parse("2-JAN-06 15:04:05", s); err == nil {
fmt.Println("Success:", t)
} else {
fmt.Println("Failure:", err)
}
Output:
Failure: parsing time "34-JAN-12 9:30:14": day out of range
Try it on the Go Playground.
EDIT:
Now that you posted code and error message, your problem is that your input string contains a leading and trailing quotation mark!
Remove the leading and trailing quotation mark and it will work. This is your case:
s := `"4-JAN-12 9:30:14"`
s = s[1 : len(s)-1]
if t, err := time.Parse("2-JAN-06 15:04:05", s); err == nil {
fmt.Println("Success:", t)
} else {
fmt.Println("Failure:", err)
}
Output (try it on the Go Playground):
Success: 2012-01-04 09:30:14 +0000 UTC

Resources