How to disable fmt-Error messages

2,012 views
Skip to first unread message

Ondekoza

unread,
Jan 31, 2011, 6:46:59 AM1/31/11
to golang-nuts
The fmt-package has a convinent error report method.

According to

http://golang.org/pkg/fmt/

a string printed with fmt.Printf will add

Too many arguments: %!(EXTRA type=value)
Printf("hi", "guys"): hi%!(EXTRA string=guys)

to the output if too many variables are supplied.

Now the following problem:

I always retrieve three numbers as int's and a string from an external
source that I want to print and that is beyond my control - that might
or might not contain placeholders for the insertion of the three ints.
When I call fmt.Sprintf(mystring, int1, int2, int3) and the number of
%-placeholder does not match, I'll get the EXTRA-error. How can I
count the number of replacer-items, so that I can call Printf with the
correct number of arguments?

Obviously I cannot count the '%', since the input string might contain
'%' signs that are no replacers. Additionally I cannot guarantuee that
the string from the external source does not
contain '%!'.

Can I disable/discard the error-message somehow?

yours
Stefan Schroeder

roger peppe

unread,
Jan 31, 2011, 6:55:25 AM1/31/11
to Ondekoza, golang-nuts

can't you just use fmt.Print or fmt.Println?

chris dollin

unread,
Jan 31, 2011, 7:19:58 AM1/31/11
to roger peppe, Ondekoza, golang-nuts
On 31 January 2011 11:55, roger peppe <rogp...@gmail.com> wrote:

> can't you just use fmt.Print or fmt.Println?

I understood the OP to be saying that the string potentially contained
formatting items and wasn't to be printed literally.

I'd scan the string looking for %s and counting them, but not counting
%%.

(And I'd call fmt.Printf with a suitable slice as its last argument
rather than with "the right number of arguments".)

(Can the format specify that it wasnt, say, the first and third -- but not
the second -- of the integers?)

Chris

--
Chris "allusive" Dollin

roger peppe

unread,
Jan 31, 2011, 7:43:10 AM1/31/11
to chris dollin, Ondekoza, golang-nuts
On 31 January 2011 12:19, chris dollin <ehog....@googlemail.com> wrote:
> On 31 January 2011 11:55, roger peppe <rogp...@gmail.com> wrote:
>
>> can't you just use fmt.Print or fmt.Println?
>
> I understood the OP to be saying that the string potentially contained
> formatting items and wasn't to be printed literally.

if the input string contains formatting items (by which i presume
you mean % specifiers) but also he "cannot count the '%', since the
input string might contain
'%' signs that are no[t] replacers" then it's an impossible
problem, because the problem is ambiguous.

you're probably right that he does want to have some % signs
treated as Printf specifiers in the input.
i think in that case i'd work out some unambiguous way
of working out what's a specifier and what is not,
scan the input string, and pass the correct number
of parameters.

it depends a lot on what the actual underlying problem is.
personally, i think i'd define my own format and transform
to printf format (or just do the concatenation myself).

> I'd scan the string looking for %s and counting them, but not counting
> %%.

you'd have to scan for %! too. and what other invalid formats,
such as %p or %t? and would you allow %o or %#0+*x ?

> (And I'd call fmt.Printf with a suitable slice as its last argument
> rather than with "the right number of arguments".)
>
> (Can the format specify that it wasnt, say, the first and third -- but not
> the second -- of the integers?)

i don't think so.

Ondekoza

unread,
Jan 31, 2011, 12:26:25 PM1/31/11
to golang-nuts


> > I understood the OP to be saying that the string potentially contained
> > formatting items and wasn't to be printed literally.
Correct.

>
> if the input string contains formatting items (by which i presume
> you mean % specifiers) but also he "cannot count the '%', since the
> input string might contain
> '%' signs that are no[t] replacers" then it's an impossible
> problem, because the problem is ambiguous.
Well, the problem is not ambiguous, otherwise the compiler would not
know how
to actually insert variables in place of % statements.

I'd only wanted to know if there is a way to simply ignore the
automatic error
message without counting the %-signs, subtracting the '%%' signs and
- based on that - adding a slice of the integers

> you're probably right that he does want to have some % signs
> treated as Printf specifiers in the input.
Yes. (BTW is it common in these parts to talk about participants in
third person?)

> you'd have to scan for %! too. and what other invalid formats,
> such as %p or %t? and would you allow %o or %#0+*x ?
Indeed, that's the question.

> > (And I'd call fmt.Printf with a suitable slice as its last argument
> > rather than with "the right number of arguments".)
Good point, didn't know about that possibility.

> > (Can the format specify that it wasnt, say, the first and third -- but not
> > the second -- of the integers?)
>
> i don't think so.
Correct, the optional int's can only occur in order:
int1
or
int1 and int2
or
int1 and int2 and int3
or
no number at all.

Scenario:
str, i1, i2, i3 := get_from_source()
Now str could be:
"number1=%d and number2=%x"
That should be printed with
fmt.Fprintf(str, i1, i2) but not with
fmt.Fprintf(str, i1, i2, i3)

More suggestions?




David Roundy

unread,
Jan 31, 2011, 12:34:16 PM1/31/11
to Ondekoza, golang-nuts
On Mon, Jan 31, 2011 at 9:26 AM, Ondekoza <onde...@gmail.com> wrote:
> More suggestions?

Just that stirngs.Count(str, "%") - 2*strings.Count(str, "%%") doesn't
seem too hard, particularly given the fact that you can use slices, so
the code comes out to:

var i123 [3]int

str, i[0], i[1], i[2] := get_from_source()
fmt.Printf(str, i[0:stirngs.Count(str, "%") - 2*strings.Count(str, "%%")]...)

which doesn't look so bad to me, and seems simpler and clearer than
modifying the fmt package to ignore some arguments.
--
David Roundy

chris dollin

unread,
Jan 31, 2011, 12:44:33 PM1/31/11
to Ondekoza, golang-nuts
On 31 January 2011 17:26, Ondekoza <onde...@gmail.com> wrote:
>
>
>> > I understood the OP to be saying that the string potentially contained
>> > formatting items and wasn't to be printed literally.
> Correct.
>
>> if the input string contains formatting items (by which i presume
>> you mean % specifiers) but also he "cannot count the '%', since the
>> input string might contain
>> '%' signs that are no[t] replacers" then it's an impossible
>> problem, because the problem is ambiguous.
> Well, the problem is not ambiguous,

Which is good, but it does mean that you'll have to do a complete
scan of the string you've been given to count the argument insertions.

> otherwise the compiler would not know how
> to actually insert variables in place of % statements.

Not that it matters here, but the compiler doesn't know how to
do this and therefore doesn't do it. The format strings are
interpreted by fmt.Printf (and its friends) at run-time.

> I'd only wanted to know if there is a way to simply ignore the
> automatic error
> message without counting the %-signs, subtracting the '%%' signs and
> - based on that - adding a slice of the integers

Doesn't look like it. (Also, "%%" doesn't seem to be documented in fmt -- did
I miss it?)

>> you're probably right that he does want to have some % signs
>> treated as Printf specifiers in the input.
> Yes. (BTW is it common in these parts to talk about participants in
> third person?)

I can't tell if it's common, but when A is responding to B about
something C did/said, using a third-person pronoun to refer to C
doesn't seem unreasonable. If it's made you uncomfortable,
I assure you that [my part in it, and I expect Roger's also] wasn't
intentional and I apologise.

Peter Bourgon

unread,
Jan 31, 2011, 12:45:06 PM1/31/11
to Ondekoza, golang-nuts
> More suggestions?

A format string is meaningless without a defined set of values
accompanying it, in a tuple. In order to be semantically correct,
whatever source provides the format string should also provide a count
of how many integers it expects. Otherwise, every solution is a hack,
and we are just evaluating hacky-ness.

roger peppe

unread,
Jan 31, 2011, 12:52:57 PM1/31/11
to Ondekoza, golang-nuts
On 31 January 2011 17:26, Ondekoza <onde...@gmail.com> wrote:
>
>

if you were wanting to allow most legitimate format specifications,
you could do something like this:

package main
import (
"regexp"
"os"
"fmt"
"strings"
)
var fspec = regexp.MustCompile("%[0-9]*(\\.[0-9]*)?[ #+-0*]*.")

func main() {
s, err := format("%d %x %% %% %% %23.56x", 99, 100, 3146)
if err != nil {
fmt.Printf("%v\n", err)
}else{
fmt.Printf("%q\n", s)
}
}

func format(f string, a, b, c int) (string, os.Error) {
m := fspec.FindAllString(f, -1)
n := 0
for _, s := range m {
if strings.IndexRune(s, '*') >= 0 {
return "", os.ErrorString("invalid format")
}
switch s[len(s)-1] {
case 'd', 'x', 'o', 'v':
n++
break
case '%':
default:
return "", os.ErrorString("invalid format")
}
}
if n > 3 {
return "", os.ErrorString("too many format specifiers")
}
return fmt.Sprintf(f, []interface{}{a, b, c}[0:n]...), nil
}

pgavlin

unread,
Jan 31, 2011, 1:52:25 PM1/31/11
to golang-nuts
On Jan 31, 11:52 am, roger peppe <rogpe...@gmail.com> wrote:
Here's another one:

package main
import (
"fmt"
"regexp"
)
var mre = regexp.MustCompile(`%.\(MISSING\)`);

func format(f string, a ...interface{}) (string, bool) {
if n := len(mre.FindAllString(fmt.Sprintf(f), -1)); n <= len(a) {
return fmt.Sprintf(f, a[0:n]...), true
}
return "", false
}

func main() {
if s, ok := format("%d %x %% %% %% %23.56x", 99, 100, 3146); ok {
fmt.Printf("%q\n", s)
} else {
fmt.Printf("Too many format specifiers\n")
}
}

Ondekoza

unread,
Jan 31, 2011, 1:53:49 PM1/31/11
to golang-nuts
Whoaa, thank you roger for the effort!

@chris: Haven't seen %% either. I guess it's an extrapolation from the
behavior you'd expect in C.

While David's approach looks quite readable to me, I would like to
avoid the
runtime penalty of compiling a regular expression. The number of '%'-
specifiers
is determined and therefore I wouldn't have chosen to use '.' for the
letter!?
Nevertheless I looks like a beautiful example of a cookbook recipe to
me, I
will definitely write that one done.

>Not that it matters here, but the compiler doesn't know how to
>do this and therefore doesn't do it. The format strings are
>interpreted by fmt.Printf (and its friends) at run-time.
True, my mistake.


roger peppe

unread,
Jan 31, 2011, 2:09:05 PM1/31/11
to Ondekoza, golang-nuts
On 31 January 2011 18:53, Ondekoza <onde...@gmail.com> wrote:
> While David's approach looks quite readable to me, I would like to
> avoid the
> runtime penalty of compiling a regular expression.

if you only do it once, the penalty is likely to be negligible.


> The number of '%'-
> specifiers
> is determined and therefore I wouldn't have chosen to use '.' for the
> letter!?

i suppose the question that should have been asked first is:
where is the format string coming from? if it's being generated
by the program itself, then it's reasonable to assume a well
formed string, and David's approach would work fine
(but that raises the question: why do it this way in the first place?);
if it comes from an external source, then that's not
quite good enough, as there are ways of fooling it.

e.g.

"%*d %34%"

Ondekoza

unread,
Jan 31, 2011, 2:32:17 PM1/31/11
to golang-nuts
It's an external source. I think, I can assume that the input is well-
behaved in general,
but I have to defend Go against more traditional approaches (written
in C), therefore
I'd like to make some points wrt/ robustness. My goal is to write a
decoder application,
that creates plain text messages from a binary log-file, interspersed
with the aformentioned
messages in strings. The C program is damn fast, but it has to do all
the string, list and memory
management by hand, which is a fuss...

Let me put this way: Make the usual case fast and the rare/faulty case
not fatal.

Stefan Schroeder


roger peppe

unread,
Jan 31, 2011, 2:38:33 PM1/31/11
to Ondekoza, golang-nuts
On 31 January 2011 19:32, Ondekoza <onde...@gmail.com> wrote:
> My goal is to write a decoder application,
> that creates plain text messages from a binary log-file, interspersed
> with the aformentioned messages in strings.

if the log file is binary, where do the Printf format strings come from?

chris dollin

unread,
Jan 31, 2011, 2:37:48 PM1/31/11
to Ondekoza, golang-nuts
On 31 January 2011 19:32, Ondekoza <onde...@gmail.com> wrote:
> It's an external source.

Does the extrernal source know it's generating Go format strings?

If not, if it's generating format strings according to its own arcane
rules (which might be C's), then you have anyway to do a decoding/recoding
step, just to be sure and because there are differences between
C format strings and Go format strings -- eg there's no "unsigned integer"
verb, because fmt gets the unsignedness by reflection.

Ondekoza

unread,
Feb 1, 2011, 5:35:50 AM2/1/11
to golang-nuts
> if the log file is binary, where do the Printf format strings come from?

The log file is mostly binary (95%), telegram based, and can include
(5%)
free form ASCII-strings with up to three integers, where the string
can
include the format-specifiers for the three ints. This mode was added
for
developer diagnosis that has not restrict itself to the interface
specifications
for the binary telegrams...

> If not, if it's generating format strings according to its own arcane
> rules (which might be C's), then you have anyway to do a decoding/recoding

It's a clearly defined subset of C's format strings.

Yo.
Stefan Schroeder

roger peppe

unread,
Feb 1, 2011, 6:21:09 AM2/1/11
to Ondekoza, golang-nuts

in that case (and assuming it's a subset of fmt.Printf's format strings
too), something like David's approach should be fine.
(you could speed it up about 30% with something like this:
func fmtcount(s string) int {
waspercent := false
n := 0
for _, c := range s {
if waspercent {
if c != '%' {
n++
}
waspercent = false
}else{
waspercent = c == '%'
}
}
return n
}
but the cost will be lost in the noise)

if someone gets the format wrong in the log file,
it won't crash anything - they'll just get an appropriate
error string in the output - no real problem.

Björn Karge

unread,
Aug 10, 2018, 9:59:45 AM8/10/18
to golang-nuts
This is a Q&D workaround. A maintenance nightmare, but it works.
Sprintf("Hello"+"%[2]s", "World", "")
Reply all
Reply to author
Forward
0 new messages