Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to ignore fields with sscanf (%* is rejected)

Tags:

go

I wish to ignore a particular field whilst processing a string with sscanf.

Man page for sscanf says

An optional '*' assignment-suppression character: scanf() reads input as directed by the conversion specification, but discards the input. No corresponding pointer argument is required, and this specification is not included in the count of successful assignments returned by scanf().

Attempting to use this in Golang, to ignore the 3rd field:

if c, err := fmt.Sscanf(str, " %s %d %*d %d ", &iface.Name, &iface.BTx, &iface.BytesRx); err != nil || c != 3 {

compiles OK, but at runtime err is set to:

bad verb %* for integer

Golang doco doesn't specifically mention the %* conversion specification, but it does say,

Package fmt implements formatted I/O with functions analogous to C's printf and scanf.

It doesn't indicate that %* is not implemented, so... Am I doing it wrong? Or has it just been quietly omitted? ...but then, why does it compile?

like image 416
maxfacta Avatar asked Oct 04 '22 12:10

maxfacta


1 Answers

To the best of my knowledge there is no such verb (as the format specifiers are called in the fmt package) for this task. What you can do however, is specifying some verb and ignoring its value. This is not particularly memory friendly, though. Ideally this would work:

fmt.Scan(&a, _, &b)

Sadly, it doesn't. So your next best option would be to declare the variables and ignore the one you don't want:

var a,b,c int
fmt.Scanf("%d %v %d", &a, &b, &c)
fmt.Println(a,c)

%v would read a space separated token. Depending on what you're scanning on, you may fast forward the stream to the position you need to scan on. See this answer for details on seeking in buffers. If you're using stdio or you don't know which length your input may have, you seem to be out of luck here.

It doesn't indicate that %* is not implemented, so... Am I doing it wrong? Or has it just been quietly omitted? ...but then, why does it compile?

It compiles because for the compiler a format string is just a string like any other. The content of that string is evaluated at run time by functions of the fmt package. Some C compilers may check format strings for correctness, but this is a feature, not the norm. With go, the go vet command will try to warn you about format string errors with mismatched arguments.

Edit:

For the special case of needing to parse a row of integers and just caring for some of them, you can use fmt.Scan in combination with a slice of integers. The following example reads 3 integers from stdin and stores them in the slice named vals:

ints := make([]interface{}, 3)
vals := make([]int, len(ints))

for i, _ := range ints {
    ints[i] = interface{}(&vals[i])
}

fmt.Scan(ints...)
fmt.Println(vals)

This is probably shorter than the conventional split/trim/strconv chain. It makes a slice of pointers which each points to a value in vals. fmt.Scan then fills these pointers. With this you can even ignore most of the values by assigning the same pointer over and over for the values you don't want:

ignored := 0

for i, _ := range ints {
    if(i == 0 || i == 2) {
        ints[i] = interface{}(&vals[i])
    } else {
        ints[i] = interface{}(&ignored)
    }
}

The example above would assign the address of ignore to all values except the first and the second, thus effectively ignoring them by overwriting.

like image 69
nemo Avatar answered Oct 13 '22 12:10

nemo