can't you just use fmt.Print or fmt.Println?
> 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
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.
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
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.
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.
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
}
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%"
if the log file is binary, where do the Printf format strings come from?
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.
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.
This is a Q&D workaround. A maintenance nightmare, but it works.
Sprintf("Hello"+"%[2]s", "World", "")