How to repeat the format in @printf?

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

5 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

Swap the first two arguments and you can do

delimiter="\t"
numbers=randn(20)
fmt=Format("%.2f")
open("file.tsv", "w") do file
    join(file, numbers, delimiter) do io, x
        format(io, fmt, x)
    end
end

Not sure why you’d want to, but it looks pretty cool.

1 Like

You can use the splat operator.

Printf.format(io, f, a...)
1 Like

I’m confused about this one. When I tried it I got

julia> x=rand(3);

julia> fmt=Printf.Format("%.2f");

julia> Printf.format(fmt,x...)
ERROR: ArgumentError: mismatch between # of format specifiers and provided args: 1 != 3

What have I missed?