# This file is a part of Julia. License is MIT: https://julialang.org/license ## core text I/O ## """ print(io::IO, x) Write to `io` (or to the default output stream [`STDOUT`](@ref) if `io` is not given) a canonical (un-decorated) text representation of a value if there is one, otherwise call [`show`](@ref). The representation used by `print` includes minimal formatting and tries to avoid Julia-specific details. ```jldoctest julia> print("Hello World!") Hello World! julia> io = IOBuffer(); julia> print(io, "Hello World!") julia> String(take!(io)) "Hello World!" ``` """ function print(io::IO, x) lock(io) try show(io, x) finally unlock(io) end return nothing end function print(io::IO, xs...) lock(io) try for x in xs print(io, x) end finally unlock(io) end return nothing end """ println(io::IO, xs...) Print (using [`print`](@ref)) `xs` followed by a newline. If `io` is not supplied, prints to [`STDOUT`](@ref). """ println(io::IO, xs...) = print(io, xs..., '\n') ## conversion of general objects to strings ## function sprint(size::Integer, f::Function, args...; env=nothing) s = IOBuffer(StringVector(size), true, true) # specialized version of truncate(s,0) s.size = 0 s.ptr = 1 if env !== nothing f(IOContext(s, env), args...) else f(s, args...) end String(resize!(s.data, s.size)) end """ sprint(f::Function, args...) Call the given function with an I/O stream and the supplied extra arguments. Everything written to this I/O stream is returned as a string. ```jldoctest julia> sprint(showcompact, 66.66666) "66.6667" ``` """ sprint(f::Function, args...) = sprint(0, f, args...) tostr_sizehint(x) = 0 tostr_sizehint(x::AbstractString) = endof(x) tostr_sizehint(x::Float64) = 20 tostr_sizehint(x::Float32) = 12 function print_to_string(xs...; env=nothing) # specialized for performance reasons s = IOBuffer(StringVector(tostr_sizehint(xs[1])), true, true) # specialized version of truncate(s,0) s.size = 0 s.ptr = 1 if env !== nothing env_io = IOContext(s, env) for x in xs print(env_io, x) end else for x in xs print(s, x) end end String(resize!(s.data, s.size)) end string_with_env(env, xs...) = print_to_string(xs...; env=env) """ string(xs...) Create a string from any values using the [`print`](@ref) function. ```jldoctest julia> string("a", 1, true) "a1true" ``` """ string(xs...) = print_to_string(xs...) print(io::IO, s::AbstractString) = (write(io, s); nothing) write(io::IO, s::AbstractString) = (len = 0; for c in s; len += write(io, c); end; len) show(io::IO, s::AbstractString) = print_quoted(io, s) write(to::AbstractIOBuffer, s::SubString{String}) = s.endof==0 ? 0 : unsafe_write(to, pointer(s.string, s.offset + 1), UInt(nextind(s, s.endof) - 1)) ## printing literal quoted string data ## # this is the inverse of print_unescaped_chars(io, s, "\\\") function print_quoted_literal(io, s::AbstractString) print(io, '"') for c = s; c == '"' ? print(io, "\\\"") : print(io, c); end print(io, '"') end """ repr(x) Create a string from any value using the [`showall`](@ref) function. """ function repr(x) s = IOBuffer() showall(s, x) String(take!(s)) end # IOBuffer views of a (byte)string: """ IOBuffer(string::String) Create a read-only `IOBuffer` on the data underlying the given string. ```jldoctest julia> io = IOBuffer("Haho"); julia> String(take!(io)) "Haho" julia> String(take!(io)) "Haho" ``` """ IOBuffer(str::String) = IOBuffer(Vector{UInt8}(str)) IOBuffer(s::SubString{String}) = IOBuffer(view(Vector{UInt8}(s.string), s.offset + 1 : s.offset + sizeof(s))) # join is implemented using IO """ join(io::IO, strings, delim, [last]) Join an array of `strings` into a single string, inserting the given delimiter between adjacent strings. If `last` is given, it will be used instead of `delim` between the last two strings. For example, ```jldoctest julia> join(["apples", "bananas", "pineapples"], ", ", " and ") "apples, bananas and pineapples" ``` `strings` can be any iterable over elements `x` which are convertible to strings via `print(io::IOBuffer, x)`. `strings` will be printed to `io`. """ function join(io::IO, strings, delim, last) i = start(strings) if done(strings,i) return end str, i = next(strings,i) print(io, str) is_done = done(strings,i) while !is_done str, i = next(strings,i) is_done = done(strings,i) print(io, is_done ? last : delim) print(io, str) end end function join(io::IO, strings, delim) i = start(strings) is_done = done(strings,i) while !is_done str, i = next(strings,i) is_done = done(strings,i) print(io, str) if !is_done print(io, delim) end end end join(io::IO, strings) = join(io, strings, "") join(args...) = sprint(join, args...) ## string escaping & unescaping ## need_full_hex(s::AbstractString, i::Int) = !done(s,i) && isxdigit(next(s,i)[1]) escape_nul(s::AbstractString, i::Int) = !done(s,i) && '0' <= next(s,i)[1] <= '7' ? "\\x00" : "\\0" """ escape_string([io,] str::AbstractString[, esc::AbstractString]) -> AbstractString General escaping of traditional C and Unicode escape sequences. Any characters in `esc` are also escaped (with a backslash). See also [`unescape_string`](@ref). """ function escape_string(io, s::AbstractString, esc::AbstractString) i = start(s) while !done(s,i) c, j = next(s,i) c == '\0' ? print(io, escape_nul(s,j)) : c == '\e' ? print(io, "\\e") : c == '\\' ? print(io, "\\\\") : c in esc ? print(io, '\\', c) : '\a' <= c <= '\r' ? print(io, '\\', "abtnvfr"[Int(c)-6]) : isprint(c) ? print(io, c) : c <= '\x7f' ? print(io, "\\x", hex(c, 2)) : c <= '\uffff' ? print(io, "\\u", hex(c, need_full_hex(s,j) ? 4 : 2)) : print(io, "\\U", hex(c, need_full_hex(s,j) ? 8 : 4)) i = j end end escape_string(s::AbstractString) = sprint(endof(s), escape_string, s, "\"") function print_quoted(io, s::AbstractString) print(io, '"') escape_string(io, s, "\"\$") #"# work around syntax highlighting problem print(io, '"') end # bare minimum unescaping function unescapes only given characters function print_unescaped_chars(io, s::AbstractString, esc::AbstractString) if !('\\' in esc) esc = string("\\", esc) end i = start(s) while !done(s,i) c, i = next(s,i) if c == '\\' && !done(s,i) && s[i] in esc c, i = next(s,i) end print(io, c) end end unescape_chars(s::AbstractString, esc::AbstractString) = sprint(endof(s), print_unescaped_chars, s, esc) # general unescaping of traditional C and Unicode escape sequences """ unescape_string([io,] s::AbstractString) -> AbstractString General unescaping of traditional C and Unicode escape sequences. Reverse of [`escape_string`](@ref). """ function unescape_string(io, s::AbstractString) i = start(s) while !done(s,i) c, i = next(s,i) if !done(s,i) && c == '\\' c, i = next(s,i) if c == 'x' || c == 'u' || c == 'U' n = k = 0 m = c == 'x' ? 2 : c == 'u' ? 4 : 8 while (k+=1) <= m && !done(s,i) c, j = next(s,i) n = '0' <= c <= '9' ? n<<4 + c-'0' : 'a' <= c <= 'f' ? n<<4 + c-'a'+10 : 'A' <= c <= 'F' ? n<<4 + c-'A'+10 : break i = j end if k == 1 throw(ArgumentError("invalid $(m == 2 ? "hex (\\x)" : "unicode (\\u)") escape sequence used in $(repr(s))")) end if m == 2 # \x escape sequence write(io, UInt8(n)) else print(io, Char(n)) end elseif '0' <= c <= '7' k = 1 n = c-'0' while (k+=1) <= 3 && !done(s,i) c, j = next(s,i) n = ('0' <= c <= '7') ? n<<3 + c-'0' : break i = j end if n > 255 throw(ArgumentError("octal escape sequence out of range")) end write(io, UInt8(n)) else print(io, c == 'a' ? '\a' : c == 'b' ? '\b' : c == 't' ? '\t' : c == 'n' ? '\n' : c == 'v' ? '\v' : c == 'f' ? '\f' : c == 'r' ? '\r' : c == 'e' ? '\e' : c) end else print(io, c) end end end unescape_string(s::AbstractString) = sprint(endof(s), unescape_string, s) macro b_str(s); :(Vector{UInt8}($(unescape_string(s)))); end macro raw_str(s); s; end ## multiline strings ## """ Calculate the width of leading blank space, and also return if string is blank Returns: * width of leading whitespace, flag if string is totally blank """ function indentation(str::AbstractString; tabwidth=8) count = 0 for ch in str if ch == ' ' count += 1 elseif ch == '\t' count = div(count + tabwidth, tabwidth) * tabwidth else return count, false end end count, true end """ Removes leading indentation from string Returns: * `String` of multiline string, with leading indentation of `indent` removed """ function unindent(str::AbstractString, indent::Int; tabwidth=8) indent == 0 && return str pos = start(str) endpos = endof(str) # Note: this loses the type of the original string buf = IOBuffer(StringVector(endpos), true, true) truncate(buf,0) cutting = true col = 0 # current column (0 based) while pos <= endpos ch, pos = next(str,pos) if cutting if ch == ' ' col += 1 elseif ch == '\t' col = div(col + tabwidth, tabwidth) * tabwidth elseif ch == '\n' # Now we need to output enough indentation for i = 1:col-indent write(buf, ' ') end col = 0 write(buf, '\n') else cutting = false # Now we need to output enough indentation to get to # correct place for i = 1:col-indent write(buf, ' ') end col += 1 write(buf, ch) end elseif ch == '\t' # Handle internal tabs upd = div(col + tabwidth, tabwidth) * tabwidth # output the number of spaces that would have been seen # with original indentation for i = 1:(upd-col) write(buf, ' ') end col = upd elseif ch == '\n' cutting = true col = 0 write(buf, '\n') else col += 1 write(buf, ch) end end # If we were still "cutting" when we hit the end of the string, # we need to output the right number of spaces for the indentation if cutting for i = 1:col-indent write(buf, ' ') end end String(take!(buf)) end function convert(::Type{String}, chars::AbstractVector{Char}) sprint(length(chars), io->begin state = start(chars) while !done(chars, state) c, state = next(chars, state) if '\ud7ff' < c && c + 1024 < '\ue000' d, state = next(chars, state) if '\ud7ff' < d - 1024 && d < '\ue000' c = Char(0x10000 + ((UInt32(c) & 0x03ff) << 10) | (UInt32(d) & 0x03ff)) else write(io, c) c = d end end write(io, c) end end) end