How to repeat the format in @printf?

Thank you so much! That is so cool!
Nice, this should be way to go!
This is similar with what is in Fortran, like, I can define a format first (with variable in it), like

write(fmt_muk_iter, '(a, i0, a)') '(''Mu_k ='',t20,',kmixin,'(  f12.5,'' +-'',f12.5,1x,''|''   ))' 

Then I can use the format fmt_muk_iter that I defined to do output

write(6,fmt_muk_iter) (valmusigma(k,2),errormusigma(k,2),k=1,kmix)   

What you said looks similar!

The @sprintf macro produces a string (instead of printing it), and then you can use something like this:

julia> using Printf

julia> x = rand(5);

julia> println([@sprintf(" %5.2f",x) for x in x]...)
  0.22  0.51  0.82  0.49  0.90


I think this is less “efficient”, but for printing a bunch of tables in non-critical code that’s practical.

3 Likes

A workaround I used to use for this before discovering the solution suggested by @heliosdrm. This is probably not suggested, but is very convenient for code interpolation in macros.

julia> using Printf

julia> @eval @printf($("%9.3f "^4), 1.0, 2.0, 3.0, 4.0)
    1.000     2.000     3.000     4.000 
2 Likes

The disadvantage of using @eval is that it evaluates the expression in the global scope, so it’s not great for using in functions etc.

1 Like

Yes, that’s true. It would be nice to have a macro which achieves similar functionality, i.e just evaluates only the code inside $(...). I don’t think that would be hard. Atleast for me, I have needed to use @eval for this purpose frequently for evaluating some code which I need to pass to another macro. But I haven’t seen this proposed anywhere.

1 Like

@heliosdrm, that is really nice.
We can then even broadcast Printf to arrays using your solution:

v = rand(10)
f = Ref(Printf.Format("%5.2f"));
Printf.format.(f,v)
4 Likes

Also, simply map:

map(x->@printf("%9.3f ",x), (1.0,2.0,3.0,4.0));
    1.000     2.000     3.000     4.000
7 Likes

One more way with piping:

(1.0,2.0,3.0,4.0).|>(x->@sprintf("%9.3f",x))|>join
"    1.000    2.000    3.000    4.000"
2 Likes

I like this way as well:

julia> using Printf

julia> myformat(x) = @sprintf("%5.2f",x)
myformat (generic function with 1 method)

julia> x = rand(3);

julia> println(myformat.(x)...)
 0.15 0.68 0.58

4 Likes

A quick thing, is it possible to print these stuff in a txt file?

I tried things like

f = Printf.Format("%9.3f"^4)
io = open("myfile.txt", "w")
for i in 1:10
    @printf(io, f, 1,2,3,4)
end
close(io)

But it does not work.

I expect to have a file called myfile.txt in which it should have such 5 lines,

1.000     2.000     3.000     4.000
1.000     2.000     3.000     4.000
1.000     2.000     3.000     4.000
1.000     2.000     3.000     4.000
1.000     2.000     3.000     4.000

Thank you!

Note that if you are writing tabular data you can just use CSV.jl and Dataframes. Writing and reading text files can be quite complicated (lots of corner cases) so it’s better to use a library that is well tested.

3 Likes

@CRquantum, you could write like this (added carriage return for better formatting):

f = Printf.Format("%9.3f"^4*"\n")
io = open("myfile2.txt", "w")
for i in 1:10
    Printf.format(io, f, 1,2,3,4)
end
close(io)
1 Like

Thank you very much!

One more question, in the exactly the same example in the quote, if I put 1, 2, 3, 4 as an array a

a = [1.0, 2.0, 3.0, 4.0]

Then

Printf.format(io, f, a)

give ma an error.

Is there a way, exactly the same as this example, just that I could print array a instead of manually inputting 1,2,3,4 here?

This could be very useful because in the code it was very likely that we need to print some array with certain format.

In this case, have you tried the broadcasting solution above but without Ref and replacing by the longer formatting sequence?

Thank you very much!
Yes I tried,

a = [1.0, 2.0, 3.0, 4.0]
f = Ref(Printf.Format("%5.2f"));
str = Printf.format.(f,a)

This gives me a 4-element Vector{String} which should be correct. However, when I write to a file by,

io = open("fileout.txt", "a") 
write(io, str)
close(io)

It gives an error

`write` is not supported on non-isbits arrays

It seems write can only operator on one string? I am sure write can handle an array but I am not sure how to do it.

@CRquantum, you can add join:
(using also join trick from @gustaphe):

a = [1.0, 2.0, 3.0, 4.0]
f = Ref(Printf.Format("%5.2f"));
open("fileout0.txt", "a") do io
    join(io, Printf.format.(f,a))
end
2 Likes

Now I want a join(io::IO, f::Format , x, delim) method…

1 Like

join takes iterables, so something like

let f = Printf.Format("%5.2f")
    join(io, (Printf.format(f, a) for a in a))
end

should work fine.

Yes, but that generates strings. format can operate on an IO, so it wouldn’t be too hard to define such a function that doesn’t go via strings at all.

1 Like

You are right. For avoiding even the intermediate strings, something like

function join2(io, f, itr, delim, last = delim)
    for (i, a) in enumerate(a)
        i == 0 || print(io, delim)
        f(io, a)
    end
    print(io, last)
end

which takes a two-argument function but encapsulates the logic of join would be nice.

2 Likes