How to properly use read!?


#1

MWE:

julia> open("/tmp/tst", "w") do f write(f, ones(Int64, 2)) end
16

julia> a = Array{Int64}(uninitialized, 4)
4-element Array{Int64,1}:
 140008466987408
 140008481884256
 140008466986384
               0

julia> rr = read!("/tmp/tst", a)
ERROR: EOFError: read end of file
Stacktrace:
 [1] unsafe_read at ./iostream.jl:409 [inlined]
 [2] unsafe_read at ./io.jl:588 [inlined]
 [3] macro expansion at ./gcutils.jl:82 [inlined]
 [4] read! at ./io.jl:606 [inlined]
 [5] #282 at ./io.jl:299 [inlined]
 [6] #open#316(::Base.Iterators.Pairs{Union{},Union{},Tuple{},NamedTuple{(),Tuple{}}}, ::Function, ::getfield(Base, Symbol("##282#283")){Array{Int64,1}}, ::String, ::Vararg{String,N} where N) at ./iostream.jl:369
 [7] open at ./iostream.jl:367 [inlined]
 [8] read!(::String, ::Array{Int64,1}) at ./io.jl:299
 [9] top-level scope

julia> rr
ERROR: UndefVarError: rr not defined

julia> a  # a is partially changed
4-element Array{Int64,1}:
               1
               1
 140008466986384
               0

How can we get number of bytes or elements read?


#2

I think read! is using var a to determine the file size, or at least it assumes there is enough data in the file to fill the variable

stat("tmp/tst").size will give the number of bytes in the file


#3

stat is not allways usable. One example:

read!(stdin, a)

julia> stat(stdin)
ERROR: MethodError: no method matching joinpath(::Base.TTY)

Assuming that there is enough data? I don’t think it is good programmer practice!

I probably found source of this problem:

function unsafe_read(s::IOStream, p::Ptr{UInt8}, nb::UInt)
    if ccall(:ios_readall, Csize_t,
             (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), s, p, nb) != nb
        throw(EOFError())
    end
    nothing
end

C code seems to be correct but encapsulation to Julia hides useful information.

help doesn’t say it but read!(io, array) returns array:

function read!(s::IO, a::Array{UInt8})
    GC.@preserve a unsafe_read(s, pointer(a), sizeof(a))
    return a
end

function read!(s::IO, a::Array{T}) where T
    if isbits(T)
        GC.@preserve a unsafe_read(s, pointer(a), sizeof(a))
    else
        for i in eachindex(a)
            a[i] = read(s, T)
        end
    end
    return a
end

I think it is redundant because array is known and could be better to make here backward incompatible change before 1.0


#4

I think fx!() might generally be expected to fill/use all of the overwrite argument space, otherwise it probably wouldn’t have the Throw(EOFError()) (// possibly better off using a non-! version?). There’s also lstat() though it doesn’t work for STDIN, but you should probably just be using readline with STDIN and parsing, i don’t think you can get a pair of float64 direct from an ordinary keyboard :wink: