如何解析 ovs-vsctl get interface statistics 的结果?

huangapple go评论160阅读模式
英文:

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

问题

结果示例:

{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}

这种格式类似于JSON,但不是JSON。

有没有一种简单的方法将其解析为map[string]int?类似于json.Unmarshal(data, &value)

英文:

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, &amp;value).

答案1

得分: 1

如果该传输格式没有递归定义,即一个键不能开始一个子结构,那么它的语言是正则语言。因此,你可以使用Go的标准regexp包来正确解析它:

Playground链接

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)
}
英文:

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 (
	&quot;fmt&quot;
	&quot;regexp&quot;
	&quot;strconv&quot;
)

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(&quot;%#v\n&quot;, vs)
}

答案2

得分: 0

Regexp by @thwd是一个优雅的解决方案。

你可以通过使用strings.Split()来更高效地解决问题,通过逗号空格(", ")进行分割以获取键值对,然后再通过等号("=")进行分割以获取键值对。然后,你只需要将它们放入一个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
}

使用它:

s := "{collisions=0, rx_bytes=258, ...}"

fmt.Println(Parse(s))

Go Playground上尝试一下。

注意: 如果性能很重要,可以通过在外部循环中不使用strings.Split()来进行改进,而是手动搜索逗号,并维护索引,只提取表示实际键和值的子字符串(但这种解决方案会更复杂)。

另一种解决方案...

...但这个选项速度较慢,只有在性能不是关键要求时才可行:你可以将输入字符串转换为有效的JSON格式,然后可以使用json.Unmarshal()。省略了错误检查:

s := "{collisions=0, rx_bytes=258, ...}"

// 转换为有效的JSON:
s = strings.Replace(s, "=", "\":", -1)
s = strings.Replace(s, ", ", ", \"", -1)
s = strings.Replace(s, "{", "{\"", -1)

// 然后简单地解组:
m := make(map[string]int)
json.Unmarshal([]byte(s), &m)
fmt.Println(m)

这种解决方案的优点是,如果你解组的目标值是一个struct,它也可以正常工作:

// 解组为一个结构体(你不需要关心所有字段)
st := struct {
    Collisions int `json:"collisions"`
    Rx_bytes   int `json:"rx_bytes"`
}{}
json.Unmarshal([]byte(s), &st)
fmt.Printf("%+v\n", st)

Go Playground上尝试一下。

英文:

Regexp by @thwd is an elegant solution.

You can get a more efficient solution by using strings.Split() to split by comma-space (&quot;, &quot;) to get the pairs, and then split again by the equal sign (&quot;=&quot;) 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) &lt; 2 || s[0] != &#39;{&#39; || s[len(s)-1] != &#39;}&#39; {
        return nil, fmt.Errorf(&quot;Invalid input, no wrapping brackets!&quot;)
    }

    m = make(map[string]int)
    for _, v := range strings.Split(s[1:len(s)-1], &quot;, &quot;) {
        parts := strings.Split(v, &quot;=&quot;)
        if len(parts) != 2 {
            return nil, fmt.Errorf(&quot;Equal sign not found in: %s&quot;, v)
        }
        if m[parts[0]], err = strconv.Atoi(parts[1]); err != nil {
            return nil, err
        }
    }
    return
}

Using it:

s := &quot;{collisions=0, rx_bytes=258, ...}&quot;

fmt.Println(Parse(s))

Try it on the <kbd>Go Playground</kbd>.

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 := &quot;{collisions=0, rx_bytes=258, ...}&quot;

// Turn into valid JSON:
s = strings.Replace(s, `=`, `&quot;:`, -1)
s = strings.Replace(s, `, `, `, &quot;`, -1)
s = strings.Replace(s, `{`, `{&quot;`, -1)

// And now simply unmarshal:
m := make(map[string]int)
json.Unmarshal([]byte(s), &amp;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&#39;t have to care about all fields)
st := struct {
    Collisions int `json:&quot;collisions&quot;`
    Rx_bytes   int `json:&quot;rx_bytes&quot;`
}{}
json.Unmarshal([]byte(s), &amp;st)
fmt.Printf(&quot;%+v\n&quot;, st)

Try these on the <kbd>Go Playground</kbd>.

huangapple
  • 本文由 发表于 2015年10月13日 17:36:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/33099162.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定