Using a variable to format printf not allowed?

Hi!

I’m trying to write a simple text file where each line follows one of a few specific formats. Is it not permitted to use a variable to specify the format for @printf? Example:

julia> fname = "abc"
"abc"

julia> @printf("%s %s", "File name: ", fname)
File name:  abc

works fine but the following does not work:

julia> fmt = "%s %s"
"%s %s"

julia> fname = "abc"
"abc"

julia> @printf(fmt, "File name: ", fname)
ERROR: MethodError: no method matching format(::String, ::Printf.Format{Base.CodeUnits{UInt8, String}, Tuple{}}, ::String)
Closest candidates are:
  format(::IO, ::Printf.Format, ::Any...) at ~/.julia/juliaup/julia-1.8.5+0.x64.apple.darwin14/share/julia/stdlib/v1.8/Printf/src/Printf.jl:820
  format(::Printf.Format, ::Any...) at ~/.julia/juliaup/julia-1.8.5+0.x64.apple.darwin14/share/julia/stdlib/v1.8/Printf/src/Printf.jl:828
Stacktrace:
 [1] top-level scope
   @ REPL[41]:1

The same thing occurs with @sprintf. Thanks for clarifications on this matter.

A way to pass variable to @printf is to eval an interpolated expression:

julia> var = "%d";
julia> num = 10;
julia> eval(:(@printf($var, num)))
10

Note that:

julia> expr = :(@printf($var, num))
:(#= REPL[4]:1 =# @printf "%d" num)

It would have been more convenient to be able to pass variable, though.

julia> using Printf

julia> fmt = Printf.Format("%s %s")
Printf.Format{Base.CodeUnits{UInt8, String}, Tuple{Printf.Spec{Val{'s'}}, Printf.Spec{Val{'s'}}}}(UInt8[0x25, 0x73, 0x20, 0x25, 0x73], UnitRange{Int64}[1:0, 3:3, 6:5], (%s, %s))

julia> fname = "abc"
"abc"

julia> Printf.format(fmt, "File name: ", fname)
"File name:  abc"
11 Likes

Thanks a lot to both @Barget and @fatteneder.

Do you know where I can find the documentation for Printf.Format and Printf.format? Most URL links on the Internet lead to the page Printf · The Julia Language which does not say anything about the formats, or how this differs from printf… Thanks again.

You can find the documentation using the REPL, by triggering the help context (by typing ?):

help?> Printf.Format
  Printf.Format(format_str)
[...]

Unfortunately, this is not put in Julia’s documentation, I assume because Format is not exported from Printf stdlib.

Once you’re used to Open Source world, you may take the habit to have a look at the source code. For instance, the @printf macro makes use of Format.

… but admittedly, this would have been hard to guess :slight_smile:

1 Like

Thanks again, @Barget. While the Julia documentation is sometimes hard to decipher, I must say the community is really helpful!

1 Like

Like @Barget said, try the ? operator:

help?> Printf.Format
  Printf.Format(format_str)

  Create a C printf-compatible format object that can be used for formatting values.

  The input format_str can include any valid format specifier character and modifiers.

  A Format object can be passed to Printf.format(f::Format, args...) to produce a formatted
  string, or Printf.format(io::IO, f::Format, args...) to print the formatted string directly
  to io.

  For convenience, the Printf.format"..." string macro form can be used for building a
  Printf.Format object at macro-expansion-time.

  │ Julia 1.6
  │
  │  Printf.Format requires Julia 1.6 or later.

Thanks @fatteneder. I’m currently able to print what I want with statements like

fmt3 = Printf.Format("%8s   " * "%14d   "^8 * "\n")

for cam = 1:misr_specs.ncameras
    Printf.format(out_stream, fmt3, misr_specs.camera_names[cam],
    stats[cam, 1], stats[cam, 2], stats[cam, 3], stats[cam, 4],
    stats[cam, 5], stats[cam, 6], stats[cam, 7], stats[cam, 8])
end

which works fine. However, I must still list each individual matrix element explicitly: statements like

Printf.format(out_stream, fmt3, misr_specs.camera_names[cam], stats[cam, :])

always generate an error message complaining about the mismatch between the format specification and the number of items to be printed. Yet, stats[cam, :] is a vector of 8 elements…

This works fine for small numbers of elements, but would be a chore (and look ugly) for dozens or hundreds of elements. I’ve tried to “explode” the vector into its components with Tuple(stats[cam, :]), or x -> (x in stats[cam, :]), or to use a vector notation in the format string itself, but that does not work either.

Any suggestion about how to iterate over the vector argument without having to list all its elements? Thanks again.

Maybe try splatting the arguments with ..., e.g.

julia> using Printf

julia> fmt = Printf.Format("%d %d %d")
Printf.Format{Base.CodeUnits{UInt8, String}, Tuple{Printf.Spec{Val{'d'}}, Printf.Spec{Val{'d'}}, Printf.Spec{Val{'d'}}}}(UInt8[0x25, 0x64, 0x20, 0x25, 0x64, 0x20, 0x25, 0x64], UnitRange{Int64}[1:0, 3:3, 6:6, 9:8], (%.0d, %.0d, %.0d))

julia> x = [1,2,3]
3-element Vector{Int64}:
 1
 2
 3

julia> Printf.format(fmt, x...)
"1 2 3"

Hi @fatteneder,

Thanks a lot for introducing me to the concept of “splatting”. I’m not sure I fully grasp the solution you proposed, but after reading about this and experimenting I found out that the following works well for me (so far):

fmt3 = Printf.Format("%8s   " * "%14d   "^8 * "\n")

for cam = 1:misr_specs.ncameras
    Printf.format(out_stream, fmt3, misr_specs.camera_names[cam], stats[cam, 1:8]...)
end

So, if I understand this correctly, adding those three dots after a vector (or array) transforms that vector (or array) into a list of individual elements, though it appears to work only inside a function call.

Thanks again for your input.