From 22fd56ee6e5788f35b93a7dc6c611e3cf9d97ba3 Mon Sep 17 00:00:00 2001 From: Johan Montelius Date: Fri, 9 Jul 2021 13:52:55 -0400 Subject: [PATCH 1/7] add dynamic width and precision to printf --- stdlib/Printf/src/Printf.jl | 136 +++++++++++-- stdlib/Printf/test/runtests.jl | 354 +++++++++++++++++++++++++++++++++ 2 files changed, 470 insertions(+), 20 deletions(-) diff --git a/stdlib/Printf/src/Printf.jl b/stdlib/Printf/src/Printf.jl index 9f14961aa2acf..b01bf5254f0b5 100644 --- a/stdlib/Printf/src/Printf.jl +++ b/stdlib/Printf/src/Printf.jl @@ -34,19 +34,29 @@ struct Spec{T} # T => %type => Val{'type'} hash::Bool width::Int precision::Int + dynamic_width::Bool + dynamic_precision::Bool end # recreate the format specifier string from a typed Spec Base.string(f::Spec{T}; modifier::String="") where {T} = - string("%", f.leftalign ? "-" : "", f.plus ? "+" : "", f.space ? " " : "", - f.zero ? "0" : "", f.hash ? "#" : "", f.width > 0 ? f.width : "", - f.precision == 0 ? ".0" : f.precision > 0 ? ".$(f.precision)" : "", modifier, char(T)) + string("%", + f.leftalign ? "-" : "", + f.plus ? "+" : "", + f.space ? " " : "", + f.zero ? "0" : "", + f.hash ? "#" : "", + f.dynamic_width ? "*" : (f.width > 0 ? f.width : ""), + f.dynamic_precision ? ".*" : (f.precision == 0 ? ".0" : (f.precision > 0 ? ".$(f.precision)" : "")), + modifier, + char(T)) + Base.show(io::IO, f::Spec) = print(io, string(f)) floatfmt(s::Spec{T}) where {T} = - Spec{Val{'f'}}(s.leftalign, s.plus, s.space, s.zero, s.hash, s.width, 0) + Spec{Val{'f'}}(s.leftalign, s.plus, s.space, s.zero, s.hash, s.width, 0, s.dynamic_width, s.dynamic_precision) ptrfmt(s::Spec{T}, x) where {T} = - Spec{Val{'x'}}(s.leftalign, s.plus, s.space, s.zero, true, s.width, sizeof(x) == 8 ? 16 : 8) + Spec{Val{'x'}}(s.leftalign, s.plus, s.space, s.zero, true, s.width, sizeof(x) == 8 ? 16 : 8, s.dynamic_width, s.dynamic_precision) """ Printf.Format(format_str) @@ -75,6 +85,7 @@ struct Format{S, T} # and so on, then at the end, str[substringranges[end]] substringranges::Vector{UnitRange{Int}} formats::T # Tuple of Specs + numarguments::Int # required for dynamic format specifiers end # what number base should be used for a given format specifier? @@ -115,6 +126,8 @@ function Format(f::AbstractString) bytes = codeunits(f) len = length(bytes) pos = 1 + numarguments = 0 + b = 0x00 local last_percent_pos @@ -165,26 +178,43 @@ function Format(f::AbstractString) end # parse width width = 0 - while b - UInt8('0') < 0x0a - width = 10 * width + (b - UInt8('0')) + dynamic_width = false + if b == UInt8('*') + dynamic_width = true + numarguments += 1 b = bytes[pos] pos += 1 - pos > len && break + else + while b - UInt8('0') < 0x0a + width = 10 * width + (b - UInt8('0')) + b = bytes[pos] + pos += 1 + pos > len && break + end end # parse precision precision = 0 parsedprecdigits = false + dynamic_precision = false if b == UInt8('.') pos > len && throw(InvalidFormatStringError("Precision specifier is missing precision", f, last_percent_pos, pos-1)) parsedprecdigits = true b = bytes[pos] pos += 1 if pos <= len - while b - UInt8('0') < 0x0a - precision = 10precision + (b - UInt8('0')) + if b == UInt8('*') + dynamic_precision = true + numarguments += 1 b = bytes[pos] pos += 1 - pos > len && break + else + precision = 0 + while b - UInt8('0') < 0x0a + precision = 10precision + (b - UInt8('0')) + b = bytes[pos] + pos += 1 + pos > len && break + end end end end @@ -208,6 +238,8 @@ function Format(f::AbstractString) !(b in b"diouxXDOUeEfFgGaAcCsSpn") && throw(InvalidFormatStringError("'$(Char(b))' is not a valid type specifier", f, last_percent_pos, pos-1)) type = Val{Char(b)} if type <: Ints && precision > 0 + # note - we should also set zero to false if dynamic precison > 0 + # this is taken care of in fmt() for Ints zero = false elseif (type <: Strings || type <: Chars) && !parsedprecdigits precision = -1 @@ -216,7 +248,8 @@ function Format(f::AbstractString) elseif type <: Floats && !parsedprecdigits precision = 6 end - push!(fmts, Spec{type}(leftalign, plus, space, zero, hash, width, precision)) + numarguments += 1 + push!(fmts, Spec{type}(leftalign, plus, space, zero, hash, width, precision, dynamic_width, dynamic_precision)) start = pos while pos <= len b = bytes[pos] @@ -235,7 +268,7 @@ function Format(f::AbstractString) end push!(strs, start:pos - 1 - (b == UInt8('%'))) end - return Format(bytes, strs, Tuple(fmts)) + return Format(bytes, strs, Tuple(fmts), numarguments) end macro format_str(str) @@ -257,6 +290,46 @@ const HEX = b"0123456789ABCDEF" return pos end + +@inline function rmdynamic(spec::Spec{T}, args, argp) where {T} + zero, width, precision = spec.zero, spec.width, spec.precision + if spec.dynamic_width + width = args[argp] + argp += 1 + end + if spec.dynamic_precision + precision = args[argp] + if zero && T <: Ints && precision > 0 + zero = false + end + argp += 1 + end + (Spec{T}(spec.leftalign, spec.plus, spec.space, zero, spec.hash, width, precision, false, false), argp) +end + + +@inline function rmdynamic(spec::Spec{T}, args, argp) where {T} + zero, width, precision = spec.zero, spec.width, spec.precision + if spec.dynamic_width + width = args[argp] + argp += 1 + end + if spec.dynamic_precision + precision = args[argp] + if zero && T <: Ints && precision > 0 + zero = false + end + argp += 1 + end + (Spec{T}(spec.leftalign, spec.plus, spec.space, zero, spec.hash, width, precision, false, false), argp) +end + + +@inline function fmt(buf, pos, args, argp, spec::Spec{T}) where {T} + spec, argp = rmdynamic(spec, args, argp) + (fmt(buf, pos, args[argp], spec), argp+1) +end + @inline function fmt(buf, pos, arg, spec::Spec{T}) where {T <: Chars} leftalign, width = spec.leftalign, spec.width c = Char(first(arg)) @@ -772,9 +845,10 @@ const UNROLL_UPTO = 16 # for each format, write out arg and next substring # unroll up to 16 formats N = length(f.formats) + argp = 1 Base.@nexprs 16 i -> begin if N >= i - pos = fmt(buf, pos, args[i], f.formats[i]) + pos, argp = fmt(buf, pos, args, argp, f.formats[i]) for j in f.substringranges[i + 1] b = f.str[j] if !escapechar @@ -789,7 +863,7 @@ const UNROLL_UPTO = 16 end if N > 16 for i = 17:length(f.formats) - pos = fmt(buf, pos, args[i], f.formats[i]) + pos, argp = fmt(buf, pos, args, argp, f.formats[i]) for j in f.substringranges[i + 1] b = f.str[j] if !escapechar @@ -805,11 +879,20 @@ const UNROLL_UPTO = 16 return pos end + +@inline function plength(f::Spec{T}, args, argp) where {T} + f, argp = rmdynamic(f, args, argp) + (plength(f, args[argp]), argp+1) +end + + + function plength(f::Spec{T}, x) where {T <: Chars} c = Char(first(x)) w = textwidth(c) return max(f.width, w) + (ncodeunits(c) - w) end + plength(f::Spec{Pointer}, x) = max(f.width, 2 * sizeof(x) + 2) function plength(f::Spec{T}, x) where {T <: Strings} @@ -837,14 +920,17 @@ plength(::Spec{PositionCounter}, x) = 0 len = sum(length, substringranges) N = length(formats) # unroll up to 16 formats + argp = 1 Base.@nexprs 16 i -> begin if N >= i - len += plength(formats[i], args[i]) + l, argp = plength(formats[i], args, argp) + len += l end end if N > 16 for i = 17:length(formats) - len += plength(formats[i], args[i]) + l, argp = plength(formats[i], args, argp) + len += l end end return len @@ -864,7 +950,7 @@ for more details on C `printf` support. function format end function format(io::IO, f::Format, args...) # => Nothing - length(f.formats) == length(args) || argmismatch(length(f.formats), length(args)) + f.numarguments == length(args) || argmismatch(f.numarguments, length(args)) buf = Base.StringVector(computelen(f.substringranges, f.formats, args)) pos = format(buf, 1, f, args...) write(io, resize!(buf, pos - 1)) @@ -872,7 +958,7 @@ function format(io::IO, f::Format, args...) # => Nothing end function format(f::Format, args...) # => String - length(f.formats) == length(args) || argmismatch(length(f.formats), length(args)) + f.numarguments == length(args) || argmismatch(f.numarguments, length(args)) buf = Base.StringVector(computelen(f.substringranges, f.formats, args)) pos = format(buf, 1, f, args...) return String(resize!(buf, pos - 1)) @@ -906,8 +992,12 @@ Padded with zeros to length 6 000123 julia> @printf "Use shorter of decimal or scientific %g %g" 1.23 12300000.0 Use shorter of decimal or scientific 1.23 1.23e+07 -``` +julia> @printf "Use dynamic width and precision %*.*f" 10 2 0.12345 +Use dynamic width and precision 0.12 + + +``` For a systematic specification of the format, see [here](https://www.cplusplus.com/reference/cstdio/printf/). See also [`@sprintf`](@ref) to get the result as a `String` instead of it being printed. @@ -924,6 +1014,12 @@ Inf Inf NaN NaN julia> @printf "%.0f %.1f %f" 0.5 0.025 -0.0078125 0 0.0 -0.007812 + +!!! compat "Julia 1.7" + Starting in Julia 1.7, `%s` (string) and `%c` (character) widths are computed + using [`textwidth`](@ref), which e.g. ignores zero-width characters + (such as combining characters for diacritical marks) and treats certain + "wide" characters (e.g. emoji) as width `2`. ``` !!! compat "Julia 1.8" diff --git a/stdlib/Printf/test/runtests.jl b/stdlib/Printf/test/runtests.jl index 40a6a763e4eac..999130d858cbe 100644 --- a/stdlib/Printf/test/runtests.jl +++ b/stdlib/Printf/test/runtests.jl @@ -775,11 +775,365 @@ end @test Printf.@sprintf("%40d", typemax(Int128)) == " 170141183460469231731687303715884105727" end + @testset "%n" begin x = Ref{Int}() @test (Printf.@sprintf("%d4%n", 123, x); x[] == 4) @test (Printf.@sprintf("%s%n", "😉", x); x[] == 4) @test (Printf.@sprintf("%s%n", "1234", x); x[] == 4) + + +@testset "dynamic" begin + + # dynamic width and precision + @test Printf.@sprintf("%*d", 10, 12) == " 12" + @test Printf.@sprintf("%.*d", 4, 12) == "0012" + @test Printf.@sprintf("%*.*d", 10, 4, 12) == " 0012" + @test Printf.@sprintf("%+*.*d", 10, 4, 12) == " +0012" + @test Printf.@sprintf("%0*.*d", 10, 4, 12) == " 0012" + + @test Printf.@sprintf("%*d%*d%*d", 4, 12, 4, 13, 4, 14) == " 12 13 14" + @test Printf.@sprintf("%*d%*d%*d", 4, 12, 5, 13, 6, 14) == " 12 13 14" + + # dynamic should return whatever the static width and precision returns + + + # pointers + @test Printf.@sprintf("%*p", 20, 0) == Printf.@sprintf("%20p", 0) + @test Printf.@sprintf("%-*p", 20, 0) == Printf.@sprintf("%-20p", 0) + @test Printf.@sprintf("%*p", 20, C_NULL) == Printf.@sprintf("%20p", C_NULL) + @test Printf.@sprintf("%-*p", 20, C_NULL) == Printf.@sprintf("%-20p", C_NULL) + + # hex float + @test Printf.@sprintf("%.*a", 0, 3.14) == Printf.@sprintf("%.0a", 3.14) + @test Printf.@sprintf("%.*a", 1, 3.14) == Printf.@sprintf("%.1a", 3.14) + @test Printf.@sprintf("%.*a", 2, 3.14) == Printf.@sprintf("%.2a", 3.14) + @test Printf.@sprintf("%#.*a", 0, 3.14) == Printf.@sprintf("%#.0a", 3.14) + @test Printf.@sprintf("%#.*a", 1, 3.14) == Printf.@sprintf("%#.1a", 3.14) + @test Printf.@sprintf("%#.*a", 2, 3.14) == Printf.@sprintf("%#.2a", 3.14) + @test Printf.@sprintf("%.*a", 6, 1.5) == Printf.@sprintf("%.6a", 1.5) + + # "%g" + @test Printf.@sprintf("%*.*g", 10, 5, -123.4 ) == Printf.@sprintf( "%10.5g", -123.4 ) + @test Printf.@sprintf("%0*.*g", 10, 5, -123.4 ) == Printf.@sprintf( "%010.5g", -123.4 ) + @test Printf.@sprintf("%.*g", 6, 12340000.0 ) == Printf.@sprintf( "%.6g", 12340000.0 ) + @test Printf.@sprintf("%#.*g", 6, 12340000.0 ) == Printf.@sprintf( "%#.6g", 12340000.0 ) + @test Printf.@sprintf("%*.*g", 10, 5, big"-123.4" ) == Printf.@sprintf( "%10.5g", big"-123.4" ) + @test Printf.@sprintf("%0*.*g", 10, 5, big"-123.4" ) == Printf.@sprintf( "%010.5g", big"-123.4" ) + @test Printf.@sprintf("%.*g", 6, big"12340000.0" ) == Printf.@sprintf( "%.6g", big"12340000.0" ) + @test Printf.@sprintf("%#.*g", 6, big"12340000.0") == Printf.@sprintf( "%#.6g", big"12340000.0") + + @test Printf.@sprintf("%.*g", 5, 42) == Printf.@sprintf( "%.5g", 42) + @test Printf.@sprintf("%#.*g", 2, 42) == Printf.@sprintf( "%#.2g", 42) + @test Printf.@sprintf("%#.*g", 5, 42) == Printf.@sprintf( "%#.5g", 42) + + @test Printf.@sprintf("%.*g", 15, 0) == Printf.@sprintf("%.15g", 0) + @test Printf.@sprintf("%#.*g", 15, 0) == Printf.@sprintf("%#.15g", 0) + + # "%f" + @test Printf.@sprintf("%.*f", 0, 3e142) == Printf.@sprintf( "%.0f", 3e142) + @test Printf.@sprintf("%.*f", 2, 1.234) == Printf.@sprintf("%.2f", 1.234) + @test Printf.@sprintf("%.*f", 2, 1.235) == Printf.@sprintf("%.2f", 1.235) + @test Printf.@sprintf("%.*f", 2, 0.235) == Printf.@sprintf("%.2f", 0.235) + @test Printf.@sprintf("%*.*f", 4, 1, 1.234) == Printf.@sprintf("%4.1f", 1.234) + @test Printf.@sprintf("%*.*f", 8, 1, 1.234) == Printf.@sprintf("%8.1f", 1.234) + @test Printf.@sprintf("%+*.*f", 8, 1, 1.234) == Printf.@sprintf("%+8.1f", 1.234) + @test Printf.@sprintf("% *.*f", 8, 1, 1.234) == Printf.@sprintf("% 8.1f", 1.234) + @test Printf.@sprintf("% *.*f", 7, 1, 1.234) == Printf.@sprintf("% 7.1f", 1.234) + @test Printf.@sprintf("% 0*.*f", 8, 1, 1.234) == Printf.@sprintf("% 08.1f", 1.234) + @test Printf.@sprintf("%0*.*f", 8, 1, 1.234) == Printf.@sprintf("%08.1f", 1.234) + @test Printf.@sprintf("%-0*.*f", 8, 1, 1.234) == Printf.@sprintf("%-08.1f", 1.234) + @test Printf.@sprintf("%-*.*f", 8, 1, 1.234) == Printf.@sprintf("%-8.1f", 1.234) + @test Printf.@sprintf("%0*.*f", 8, 1, -1.234) == Printf.@sprintf("%08.1f", -1.234) + @test Printf.@sprintf("%0*.*f", 9, 1, -1.234) == Printf.@sprintf("%09.1f", -1.234) + @test Printf.@sprintf("%0*.*f", 9, 1, 1.234) == Printf.@sprintf("%09.1f", 1.234) + @test Printf.@sprintf("%+0*.*f", 9, 1, 1.234) == Printf.@sprintf("%+09.1f", 1.234) + @test Printf.@sprintf("% 0*.*f", 9, 1, 1.234) == Printf.@sprintf("% 09.1f", 1.234) + @test Printf.@sprintf("%+ 0*.*f", 9, 1, 1.234) == Printf.@sprintf("%+ 09.1f", 1.234) + @test Printf.@sprintf("%+ 0*.*f", 9, 1, 1.234) == Printf.@sprintf("%+ 09.1f", 1.234) + @test Printf.@sprintf("%+ 0*.*f", 9, 0, 1.234) == Printf.@sprintf("%+ 09.0f", 1.234) + @test Printf.@sprintf("%+ #0*.*f", 9, 0, 1.234) == Printf.@sprintf("%+ #09.0f", 1.234) + + # "%e" + @test Printf.@sprintf("%*.*e", 10, 4, Inf) == Printf.@sprintf("%10.4e", Inf) + @test Printf.@sprintf("%*.*e", 10, 4, NaN) == Printf.@sprintf("%10.4e", NaN) + @test Printf.@sprintf("%*.*e", 10, 4, big"Inf") == Printf.@sprintf("%10.4e", big"Inf") + @test Printf.@sprintf("%*.*e", 10, 4, big"NaN") == Printf.@sprintf("%10.4e", big"NaN") + + @test Printf.@sprintf("%.*e", 0, 3e142) == Printf.@sprintf("%.0e",3e142) + @test Printf.@sprintf("%#.*e", 0, 3e142) == Printf.@sprintf("%#.0e", 3e142) + @test Printf.@sprintf("%.*e", 0, big"3e142") == Printf.@sprintf("%.0e", big"3e142") + + @test Printf.@sprintf("%#.*e", 0, big"3e142") == Printf.@sprintf("%#.0e", big"3e142") + @test Printf.@sprintf("%.*e", 0, big"3e1042") == Printf.@sprintf("%.0e", big"3e1042") + + @test Printf.@sprintf("%.*e", 2, 1.234) == Printf.@sprintf("%.2e", 1.234) + @test Printf.@sprintf("%.*e", 2, 1.235) == Printf.@sprintf("%.2e", 1.235) + @test Printf.@sprintf("%.*e", 2, 0.235) == Printf.@sprintf("%.2e", 0.235) + @test Printf.@sprintf("%*.*e", 4, 1, 1.234) == Printf.@sprintf("%4.1e", 1.234) + @test Printf.@sprintf("%*.*e", 8, 1, 1.234) == Printf.@sprintf("%8.1e", 1.234) + @test Printf.@sprintf("%+*.*e", 8, 1, 1.234) == Printf.@sprintf("%+8.1e", 1.234) + @test Printf.@sprintf("% *.*e", 8, 1, 1.234) == Printf.@sprintf("% 8.1e", 1.234) + @test Printf.@sprintf("% *.*e", 7, 1, 1.234) == Printf.@sprintf("% 7.1e", 1.234) + @test Printf.@sprintf("% 0*.*e", 8, 1, 1.234) == Printf.@sprintf("% 08.1e", 1.234) + @test Printf.@sprintf("%0*.*e", 8, 1, 1.234) == Printf.@sprintf("%08.1e", 1.234) + @test Printf.@sprintf("%-0*.*e", 8, 1, 1.234) == Printf.@sprintf("%-08.1e", 1.234) + @test Printf.@sprintf("%-*.*e", 8, 1, 1.234) == Printf.@sprintf("%-8.1e", 1.234) + @test Printf.@sprintf("%-*.*e", 8, 1, 1.234) == Printf.@sprintf("%-8.1e", 1.234) + @test Printf.@sprintf("%0*.*e", 8, 1, -1.234) == Printf.@sprintf("%08.1e", -1.234) + @test Printf.@sprintf("%0*.*e", 9, 1, -1.234) == Printf.@sprintf("%09.1e", -1.234) + @test Printf.@sprintf("%0*.*e", 9, 1, 1.234) == Printf.@sprintf("%09.1e", 1.234) + @test Printf.@sprintf("%+0*.*e", 9, 1, 1.234) == Printf.@sprintf("%+09.1e", 1.234) + @test Printf.@sprintf("% 0*.*e", 9, 1, 1.234) == Printf.@sprintf("% 09.1e", 1.234) + @test Printf.@sprintf("%+ 0*.*e", 9, 1, 1.234) == Printf.@sprintf("%+ 09.1e", 1.234) + @test Printf.@sprintf("%+ 0*.*e", 9, 1, 1.234) == Printf.@sprintf("%+ 09.1e", 1.234) + @test Printf.@sprintf("%+ 0*.*e", 9, 0, 1.234) == Printf.@sprintf("%+ 09.0e", 1.234) + @test Printf.@sprintf("%+ #0*.*e", 9, 0, 1.234) == Printf.@sprintf("%+ #09.0e", 1.234) + + # strings + @test Printf.@sprintf("%.*s", 1, "foo") == Printf.@sprintf("%.1s", "foo") + @test Printf.@sprintf("%*s", 1, "Hallo heimur") == Printf.@sprintf("%1s", "Hallo heimur") + @test Printf.@sprintf("%*s", 20, "Hallo") == Printf.@sprintf("%20s", "Hallo") + @test Printf.@sprintf("%-*s", 20, "Hallo") == Printf.@sprintf("%-20s", "Hallo") + @test Printf.@sprintf("%0-*s", 20, "Hallo") == Printf.@sprintf("%0-20s", "Hallo") + @test Printf.@sprintf("%.*s", 20, "Hallo heimur") == Printf.@sprintf("%.20s", "Hallo heimur") + @test Printf.@sprintf("%*.*s", 20, 5, "Hallo heimur") == Printf.@sprintf("%20.5s", "Hallo heimur") + @test Printf.@sprintf("%.*s", 0, "Hallo heimur") == Printf.@sprintf("%.0s", "Hallo heimur") + @test Printf.@sprintf("%*.*s", 20, 0, "Hallo heimur") == Printf.@sprintf("%20.0s", "Hallo heimur") + @test Printf.@sprintf("%.s", "Hallo heimur") == Printf.@sprintf("%.s", "Hallo heimur") + @test Printf.@sprintf("%*.s", 20, "Hallo heimur") == Printf.@sprintf("%20.s", "Hallo heimur") + @test Printf.@sprintf("%*sø", 4, "ø") == Printf.@sprintf("%4sø", "ø") + @test Printf.@sprintf("%-*sø", 4, "ø") == Printf.@sprintf("%-4sø", "ø") + + @test Printf.@sprintf("%*s", 8, "test") == Printf.@sprintf("%8s", "test") + @test Printf.@sprintf("%-*s", 8, "test") == Printf.@sprintf("%-8s", "test") + + @test Printf.@sprintf("%#*s", 8, :test) == Printf.@sprintf("%#8s", :test) + @test Printf.@sprintf("%#-*s", 8, :test) == Printf.@sprintf("%#-8s", :test) + + @test Printf.@sprintf("%*.*s", 8, 3, "test") == Printf.@sprintf("%8.3s", "test") + @test Printf.@sprintf("%#*.*s", 8, 3, "test") == Printf.@sprintf("%#8.3s", "test") + @test Printf.@sprintf("%-*.*s", 8, 3, "test") == Printf.@sprintf("%-8.3s", "test") + @test Printf.@sprintf("%#-*.*s", 8, 3, "test") == Printf.@sprintf("%#-8.3s", "test") + @test Printf.@sprintf("%.*s", 3, "test") == Printf.@sprintf("%.3s", "test") + @test Printf.@sprintf("%#.*s", 3, "test") == Printf.@sprintf("%#.3s", "test") + @test Printf.@sprintf("%-.*s", 3, "test") == Printf.@sprintf("%-.3s", "test") + @test Printf.@sprintf("%#-.*s", 3, "test") == Printf.@sprintf("%#-.3s", "test") + + # chars + @test Printf.@sprintf("%*c", 3, 'a') == Printf.@sprintf("%3c", 'a') + @test Printf.@sprintf("%*c", 1, 'x') == Printf.@sprintf("%1c", 'x') + @test Printf.@sprintf("%*c" , 20, 'x') == Printf.@sprintf("%20c" , 'x') + @test Printf.@sprintf("%-*c" , 20, 'x') == Printf.@sprintf("%-20c" , 'x') + @test Printf.@sprintf("%-0*c", 20, 'x') == Printf.@sprintf("%-020c", 'x') + @test Printf.@sprintf("%*c", 3, 'A') == Printf.@sprintf("%3c", 'A') + @test Printf.@sprintf("%-*c", 3, 'A') == Printf.@sprintf("%-3c", 'A') + + # more than 16 formats/args + @test Printf.@sprintf("%*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f", 4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345) == Printf.@sprintf("%4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f", 1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345) + + # Check bug with trailing nul printing BigFloat + @test (Printf.@sprintf("%.*f", 330, BigFloat(1)))[end] != '\0' + + # Check bug with precision > length of string + @test Printf.@sprintf("%*.*s", 4, 2, "a") == Printf.@sprintf("%4.2s", "a") + + # issue #29662 + @test Printf.@sprintf("%*.*e", 12, 3, pi*1e100) == Printf.@sprintf("%12.3e", pi*1e100) + @test Printf.@sprintf("%*d", 2, 3.14) == Printf.@sprintf("%*d", 2, 3.14) + @test Printf.@sprintf("%*d", 2, big(3.14)) == Printf.@sprintf("%*d", 2, big(3.14)) + + # 37539 + @test Printf.@sprintf(" %.*e\n", 1, 0.999) == Printf.@sprintf(" %.1e\n", 0.999) + @test Printf.@sprintf(" %.*f", 1, 9.999) == Printf.@sprintf(" %.1f", 9.999) + + # integers + @test Printf.@sprintf("%*d", 10, 12) == (Printf.@sprintf("%10d", 12)) + @test Printf.@sprintf("%.*d", 4, 12) == (Printf.@sprintf("%.4d", 12)) + @test Printf.@sprintf("%*.*d", 10, 4, 12) == (Printf.@sprintf("%10.4d", 12)) + @test Printf.@sprintf("%+*.*d", 10, 4, 12) == (Printf.@sprintf("%+10.4d", 12)) + @test Printf.@sprintf("%0*.*d", 10, 4, 12) == (Printf.@sprintf("%010.4d", 12)) + + @test Printf.@sprintf( "% *d", 5, 42) == Printf.@sprintf( "% 5d", 42) + @test Printf.@sprintf( "% *d", 5, -42) == Printf.@sprintf( "% 5d", -42) + @test Printf.@sprintf( "% *d", 15, 42) == Printf.@sprintf( "% 15d", 42) + @test Printf.@sprintf( "% *d", 15, -42) == Printf.@sprintf( "% 15d", -42) + + @test Printf.@sprintf("%+*d", 5, 42) == Printf.@sprintf("%+5d", 42) + @test Printf.@sprintf("%+*d", 5, -42) == Printf.@sprintf("%+5d", -42) + @test Printf.@sprintf("%+*d", 15, 42) == Printf.@sprintf("%+15d", 42) + @test Printf.@sprintf("%+*d", 15, -42) == Printf.@sprintf("%+15d", -42) + @test Printf.@sprintf( "%*d", 0, 42) == Printf.@sprintf( "%0d", 42) + @test Printf.@sprintf( "%*d", 0, -42) == Printf.@sprintf( "%0d", -42) + + @test Printf.@sprintf("%-*d", 5, 42) == Printf.@sprintf("%-5d", 42) + @test Printf.@sprintf("%-*d", 5, -42) == Printf.@sprintf("%-5d", -42) + @test Printf.@sprintf("%-*d", 15, 42) == Printf.@sprintf("%-15d", 42) + @test Printf.@sprintf("%-*d", 15, -42) == Printf.@sprintf("%-15d", -42) + + @test Printf.@sprintf("%+*lld", 8, 100) == Printf.@sprintf("%+8lld", 100) + @test Printf.@sprintf("%+.*lld", 8, 100) == Printf.@sprintf("%+.8lld", 100) + @test Printf.@sprintf("%+*.*lld", 10, 8, 100) == Printf.@sprintf("%+10.8lld", 100) + + @test Printf.@sprintf("%-*.*lld", 1, 5, -100) == Printf.@sprintf("%-1.5lld", -100) + @test Printf.@sprintf("%*lld", 5, 100) == Printf.@sprintf("%5lld", 100) + @test Printf.@sprintf("%*lld", 5, -100) == Printf.@sprintf("%5lld", -100) + @test Printf.@sprintf("%-*lld", 5, 100) == Printf.@sprintf("%-5lld", 100) + @test Printf.@sprintf("%-*lld", 5, -100) == Printf.@sprintf("%-5lld", -100) + @test Printf.@sprintf("%-.*lld", 5, 100) == Printf.@sprintf("%-.5lld", 100) + @test Printf.@sprintf("%-.*lld", 5, -100) == Printf.@sprintf("%-.5lld", -100) + @test Printf.@sprintf("%-*.*lld", 8, 5, 100) == Printf.@sprintf("%-8.5lld", 100) + @test Printf.@sprintf("%-*.*lld", 8, 5, -100) == Printf.@sprintf("%-8.5lld", -100) + @test Printf.@sprintf("%0*lld", 5, 100) == Printf.@sprintf("%05lld", 100) + @test Printf.@sprintf("%0*lld", 5, -100) == Printf.@sprintf("%05lld", -100) + @test Printf.@sprintf("% *lld", 5, 100) == Printf.@sprintf("% 5lld", 100) + @test Printf.@sprintf("% *lld", 5, -100) == Printf.@sprintf("% 5lld", -100) + @test Printf.@sprintf("% .*lld", 5, 100) == Printf.@sprintf("% .5lld", 100) + @test Printf.@sprintf("% .*lld", 5, -100) == Printf.@sprintf("% .5lld", -100) + @test Printf.@sprintf("% *.*lld", 8, 5, 100) == Printf.@sprintf("% 8.5lld", 100) + @test Printf.@sprintf("% *.*lld", 8, 5, -100) == Printf.@sprintf("% 8.5lld", -100) + @test Printf.@sprintf("%.*lld", 0, 0) == Printf.@sprintf("%.0lld", 0) + @test Printf.@sprintf("%#+*.*llx", 21, 18, -100) == Printf.@sprintf("%#+21.18llx", -100) + @test Printf.@sprintf("%#.*llo", 25, -100) == Printf.@sprintf("%#.25llo", -100) + @test Printf.@sprintf("%#+*.*llo", 24, 20, -100) == Printf.@sprintf("%#+24.20llo", -100) + @test Printf.@sprintf("%#+*.*llX", 18, 21, -100) == Printf.@sprintf("%#+18.21llX", -100) + @test Printf.@sprintf("%#+*.*llo", 20, 24, -100) == Printf.@sprintf("%#+20.24llo", -100) + @test Printf.@sprintf("%#+*.*llu", 25, 22, -1) == Printf.@sprintf("%#+25.22llu", -1) + @test Printf.@sprintf("%#+*.*llu", 30, 25, -1) == Printf.@sprintf("%#+30.25llu", -1) + @test Printf.@sprintf("%+#*.*lld", 25, 22, -1) == Printf.@sprintf("%+#25.22lld", -1) + @test Printf.@sprintf("%#-*.*llo", 8, 5, 100) == Printf.@sprintf("%#-8.5llo", 100) + @test Printf.@sprintf("%#-+ 0*.*lld", 8, 5, 100) == Printf.@sprintf("%#-+ 08.5lld", 100) + @test Printf.@sprintf("%#-+ 0*.*lld", 8, 5, 100) == Printf.@sprintf("%#-+ 08.5lld", 100) + @test Printf.@sprintf("%.*lld", 40, 1) == Printf.@sprintf("%.40lld", 1) + @test Printf.@sprintf("% .*lld", 40, 1) == Printf.@sprintf("% .40lld", 1) + @test Printf.@sprintf("% .*d", 40, 1) == Printf.@sprintf("% .40d", 1) + + @test Printf.@sprintf("%#0*x", 12, 1) == Printf.@sprintf("%#012x", 1) + @test Printf.@sprintf("%#0*.*x", 4, 8, 1) == Printf.@sprintf("%#04.8x", 1) + + @test Printf.@sprintf("%#-0*.*x", 8, 2, 1) == Printf.@sprintf("%#-08.2x", 1) + @test Printf.@sprintf("%#0*o", 8, 1) == Printf.@sprintf("%#08o", 1) + + @test Printf.@sprintf("%*d", 20, 1024) == Printf.@sprintf("%20d", 1024) + @test Printf.@sprintf("%*d", 20,-1024) == Printf.@sprintf("%20d", -1024) + @test Printf.@sprintf("%*i", 20, 1024) == Printf.@sprintf("%20i", 1024) + @test Printf.@sprintf("%*i", 20,-1024) == Printf.@sprintf("%20i", -1024) + @test Printf.@sprintf("%*u", 20, 1024) == Printf.@sprintf("%20u", 1024) + @test Printf.@sprintf("%*u", 20, UInt(4294966272)) == Printf.@sprintf("%20u", UInt(4294966272)) + @test Printf.@sprintf("%*o", 20, 511) == Printf.@sprintf("%20o", 511) + @test Printf.@sprintf("%*o", 20, UInt(4294966785)) == Printf.@sprintf("%20o", UInt(4294966785)) + @test Printf.@sprintf("%*x", 20, 305441741) == Printf.@sprintf("%20x", 305441741) + @test Printf.@sprintf("%*x", 20, UInt(3989525555)) == Printf.@sprintf("%20x", UInt(3989525555)) + @test Printf.@sprintf("%*X", 20, 305441741) == Printf.@sprintf("%20X", 305441741) + @test Printf.@sprintf("%*X", 20, UInt(3989525555)) == Printf.@sprintf("%20X", UInt(3989525555)) + @test Printf.@sprintf("%-*d", 20, 1024) == Printf.@sprintf("%-20d", 1024) + @test Printf.@sprintf("%-*d", 20,-1024) == Printf.@sprintf("%-20d", -1024) + @test Printf.@sprintf("%-*i", 20, 1024) == Printf.@sprintf("%-20i", 1024) + @test Printf.@sprintf("%-*i", 20,-1024) == Printf.@sprintf("%-20i", -1024) + @test Printf.@sprintf("%-*u", 20, 1024) == Printf.@sprintf("%-20u", 1024) + @test Printf.@sprintf("%-*u", 20, UInt(4294966272)) == Printf.@sprintf("%-20u", UInt(4294966272)) + @test Printf.@sprintf("%-*o", 20, 511) == Printf.@sprintf("%-20o", 511) + @test Printf.@sprintf("%-*o", 20, UInt(4294966785)) == Printf.@sprintf("%-20o", UInt(4294966785)) + @test Printf.@sprintf("%-*x", 20, 305441741) == Printf.@sprintf("%-20x", 305441741) + @test Printf.@sprintf("%-*x", 20, UInt(3989525555)) == Printf.@sprintf("%-20x", UInt(3989525555)) + @test Printf.@sprintf("%-*X", 20, 305441741) == Printf.@sprintf("%-20X", 305441741) + @test Printf.@sprintf("%-*X", 20, UInt(3989525555)) == Printf.@sprintf("%-20X", UInt(3989525555)) + @test Printf.@sprintf("%0*d", 20, 1024) == Printf.@sprintf("%020d", 1024) + @test Printf.@sprintf("%0*d", 20,-1024) == Printf.@sprintf("%020d", -1024) + @test Printf.@sprintf("%0*i", 20, 1024) == Printf.@sprintf("%020i", 1024) + @test Printf.@sprintf("%0*i", 20,-1024) == Printf.@sprintf("%020i", -1024) + @test Printf.@sprintf("%0*u", 20, 1024) == Printf.@sprintf("%020u", 1024) + @test Printf.@sprintf("%0*u", 20, UInt(4294966272)) == Printf.@sprintf("%020u", UInt(4294966272)) + @test Printf.@sprintf("%0*o", 20, 511) == Printf.@sprintf("%020o", 511) + @test Printf.@sprintf("%0*o", 20, UInt(4294966785)) == Printf.@sprintf("%020o", UInt(4294966785)) + @test Printf.@sprintf("%0*x", 20, 305441741) == Printf.@sprintf("%020x", 305441741) + @test Printf.@sprintf("%0*x", 20, UInt(3989525555)) == Printf.@sprintf("%020x", UInt(3989525555)) + @test Printf.@sprintf("%0*X", 20, 305441741) == Printf.@sprintf("%020X", 305441741) + @test Printf.@sprintf("%0*X", 20, UInt(3989525555)) == Printf.@sprintf("%020X", UInt(3989525555)) + @test Printf.@sprintf("%#*o", 20, 511) == Printf.@sprintf("%#20o", 511) + @test Printf.@sprintf("%#*o", 20, UInt(4294966785)) == Printf.@sprintf("%#20o", UInt(4294966785)) + @test Printf.@sprintf("%#*x", 20, 305441741) == Printf.@sprintf("%#20x", 305441741) + @test Printf.@sprintf("%#*x", 20, UInt(3989525555)) == Printf.@sprintf("%#20x", UInt(3989525555)) + @test Printf.@sprintf("%#*X", 20, 305441741) == Printf.@sprintf("%#20X", 305441741) + @test Printf.@sprintf("%#*X", 20, UInt(3989525555)) == Printf.@sprintf("%#20X", UInt(3989525555)) + @test Printf.@sprintf("%#0*o", 20, 511) == Printf.@sprintf("%#020o", 511) + @test Printf.@sprintf("%#0*o", 20, UInt(4294966785)) == Printf.@sprintf("%#020o", UInt(4294966785)) + @test Printf.@sprintf("%#0*x", 20, 305441741) == Printf.@sprintf("%#020x", 305441741) + @test Printf.@sprintf("%#0*x", 20, UInt(3989525555)) == Printf.@sprintf("%#020x", UInt(3989525555)) + @test Printf.@sprintf("%#0*X", 20, 305441741) == Printf.@sprintf("%#020X", 305441741) + @test Printf.@sprintf("%#0*X", 20, UInt(3989525555)) == Printf.@sprintf("%#020X", UInt(3989525555)) + @test Printf.@sprintf("%0-*d", 20, 1024) == Printf.@sprintf("%0-20d", 1024) + @test Printf.@sprintf("%0-*d", 20,-1024) == Printf.@sprintf("%0-20d", -1024) + @test Printf.@sprintf("%0-*i", 20, 1024) == Printf.@sprintf("%0-20i", 1024) + @test Printf.@sprintf("%0-*i", 20,-1024) == Printf.@sprintf("%0-20i", -1024) + @test Printf.@sprintf("%0-*u", 20, 1024) == Printf.@sprintf("%0-20u", 1024) + @test Printf.@sprintf("%0-*u", 20, UInt(4294966272)) == Printf.@sprintf("%0-20u", UInt(4294966272)) + @test Printf.@sprintf("%-0*o", 20, 511) == Printf.@sprintf("%-020o", 511) + @test Printf.@sprintf("%-0*o", 20, UInt(4294966785)) == Printf.@sprintf("%-020o", UInt(4294966785)) + @test Printf.@sprintf("%-0*x", 20, 305441741) == Printf.@sprintf("%-020x", 305441741) + @test Printf.@sprintf("%-0*x", 20, UInt(3989525555)) == Printf.@sprintf("%-020x", UInt(3989525555)) + @test Printf.@sprintf("%-0*X", 20, 305441741) == Printf.@sprintf("%-020X", 305441741) + @test Printf.@sprintf("%-0*X", 20, UInt(3989525555)) == Printf.@sprintf("%-020X", UInt(3989525555)) + @test Printf.@sprintf("%.*d", 20, 1024) == Printf.@sprintf("%.20d", 1024) + @test Printf.@sprintf("%.*d", 20,-1024) == Printf.@sprintf("%.20d", -1024) + @test Printf.@sprintf("%.*i", 20, 1024) == Printf.@sprintf("%.20i", 1024) + @test Printf.@sprintf("%.*i", 20,-1024) == Printf.@sprintf("%.20i", -1024) + @test Printf.@sprintf("%.*u", 20, 1024) == Printf.@sprintf("%.20u", 1024) + @test Printf.@sprintf("%.*u", 20, UInt(4294966272)) == Printf.@sprintf("%.20u", UInt(4294966272)) + @test Printf.@sprintf("%.*o", 20, 511) == Printf.@sprintf("%.20o", 511) + @test Printf.@sprintf("%.*o", 20, UInt(4294966785)) == Printf.@sprintf("%.20o", UInt(4294966785)) + @test Printf.@sprintf("%.*x", 20, 305441741) == Printf.@sprintf("%.20x", 305441741) + @test Printf.@sprintf("%.*x", 20, UInt(3989525555)) == Printf.@sprintf("%.20x", UInt(3989525555)) + @test Printf.@sprintf("%.*X", 20, 305441741) == Printf.@sprintf("%.20X", 305441741) + @test Printf.@sprintf("%.*X", 20, UInt(3989525555)) == Printf.@sprintf("%.20X", UInt(3989525555)) + @test Printf.@sprintf("%*.*d", 20, 5, 1024) == Printf.@sprintf("%20.5d", 1024) + @test Printf.@sprintf("%*.*d", 20, 5, -1024) == Printf.@sprintf("%20.5d", -1024) + @test Printf.@sprintf("%*.*i", 20, 5, 1024) == Printf.@sprintf("%20.5i", 1024) + @test Printf.@sprintf("%*.*i", 20, 5,-1024) == Printf.@sprintf("%20.5i", -1024) + @test Printf.@sprintf("%*.*u", 20, 5, 1024) == Printf.@sprintf("%20.5u", 1024) + @test Printf.@sprintf("%*.*u", 20, 5, UInt(4294966272)) == Printf.@sprintf("%20.5u", UInt(4294966272)) + @test Printf.@sprintf("%*.*o", 20, 5, 511) == Printf.@sprintf("%20.5o", 511) + @test Printf.@sprintf("%*.*o", 20, 5, UInt(4294966785)) == Printf.@sprintf("%20.5o", UInt(4294966785)) + @test Printf.@sprintf("%*.*x", 20, 5, 305441741) == Printf.@sprintf("%20.5x", 305441741) + @test Printf.@sprintf("%*.*x", 20, 10, UInt(3989525555)) == Printf.@sprintf("%20.10x", UInt(3989525555)) + @test Printf.@sprintf("%*.*X", 20, 5, 305441741) == Printf.@sprintf("%20.5X", 305441741) + @test Printf.@sprintf("%*.*X", 20, 10, UInt(3989525555)) == Printf.@sprintf("%20.10X", UInt(3989525555)) + @test Printf.@sprintf("%0*.*d", 20, 5, 1024) == Printf.@sprintf("%020.5d", 1024) + @test Printf.@sprintf("%0*.*d", 20, 5,-1024) == Printf.@sprintf("%020.5d", -1024) + @test Printf.@sprintf("%0*.*i", 20, 5, 1024) == Printf.@sprintf("%020.5i", 1024) + @test Printf.@sprintf("%0*.*i", 20, 5,-1024) == Printf.@sprintf("%020.5i", -1024) + @test Printf.@sprintf("%0*.*u", 20, 5, 1024) == Printf.@sprintf("%020.5u", 1024) + @test Printf.@sprintf("%0*.*u", 20, 5, UInt(4294966272)) == Printf.@sprintf("%020.5u", UInt(4294966272)) + @test Printf.@sprintf("%0*.*o", 20, 5, 511) == Printf.@sprintf("%020.5o", 511) + @test Printf.@sprintf("%0*.*o", 20, 5, UInt(4294966785)) == Printf.@sprintf("%020.5o", UInt(4294966785)) + @test Printf.@sprintf("%0*.*x", 20, 5, 305441741) == Printf.@sprintf("%020.5x", 305441741) + @test Printf.@sprintf("%0*.*x", 20, 10, UInt(3989525555)) == Printf.@sprintf("%020.10x", UInt(3989525555)) + @test Printf.@sprintf("%0*.*X", 20, 5, 305441741) == Printf.@sprintf("%020.5X", 305441741) + @test Printf.@sprintf("%0*.*X", 20, 10, UInt(3989525555)) == Printf.@sprintf("%020.10X", UInt(3989525555)) + @test Printf.@sprintf("%*.0d", 20, 1024) == Printf.@sprintf("%20.0d", 1024) + @test Printf.@sprintf("%*.d", 20,-1024) == Printf.@sprintf("%20.d", -1024) + @test Printf.@sprintf("%*.d", 20, 0) == Printf.@sprintf("%20.d", 0) + @test Printf.@sprintf("%*.0i", 20, 1024) == Printf.@sprintf("%20.0i", 1024) + @test Printf.@sprintf("%*.i", 20,-1024) == Printf.@sprintf("%20.i", -1024) + @test Printf.@sprintf("%*.i", 20, 0) == Printf.@sprintf("%20.i", 0) + @test Printf.@sprintf("%*.u", 20, 1024) == Printf.@sprintf("%20.u", 1024) + @test Printf.@sprintf("%*.0u", 20, UInt(4294966272)) == Printf.@sprintf("%20.0u", UInt(4294966272)) + @test Printf.@sprintf("%*.u", 20, UInt(0)) == Printf.@sprintf("%20.u", UInt(0)) + @test Printf.@sprintf("%*.o", 20, 511) == Printf.@sprintf("%20.o", 511) + @test Printf.@sprintf("%*.0o", 20, UInt(4294966785)) == Printf.@sprintf("%20.0o", UInt(4294966785)) + @test Printf.@sprintf("%*.o", 20, UInt(0)) == Printf.@sprintf("%20.o", UInt(0)) + @test Printf.@sprintf("%*.x", 20, 305441741) == Printf.@sprintf("%20.x", 305441741) + @test Printf.@sprintf("%*.0x", 20, UInt(3989525555)) == Printf.@sprintf("%20.0x", UInt(3989525555)) + @test Printf.@sprintf("%*.x", 20, UInt(0)) == Printf.@sprintf("%20.x", UInt(0)) + @test Printf.@sprintf("%*.X", 20, 305441741) == Printf.@sprintf("%20.X", 305441741) + @test Printf.@sprintf("%*.0X", 20, UInt(3989525555)) == Printf.@sprintf("%20.0X", UInt(3989525555)) + @test Printf.@sprintf("%*.X", 20, UInt(0)) == Printf.@sprintf("%20.X", UInt(0)) + + x = Ref{Int}() + y = Ref{Int}() + @test (Printf.@sprintf("%10s%n", "😉", x); Printf.@sprintf("%*s%n", 10, "😉", y); x[] == y[]) + @test (Printf.@sprintf("%10s%n", "1234", x); Printf.@sprintf("%*s%n", 10, "1234", y); x[] == y[]) + end @testset "length modifiers" begin From 87ae14bac5a91162c3a00237d8831c21caecaa25 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Fri, 27 Jan 2023 14:12:40 -0500 Subject: [PATCH 2/7] fixed compat notice --- stdlib/Printf/src/Printf.jl | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/stdlib/Printf/src/Printf.jl b/stdlib/Printf/src/Printf.jl index b01bf5254f0b5..caa61b6a57c66 100644 --- a/stdlib/Printf/src/Printf.jl +++ b/stdlib/Printf/src/Printf.jl @@ -1014,12 +1014,6 @@ Inf Inf NaN NaN julia> @printf "%.0f %.1f %f" 0.5 0.025 -0.0078125 0 0.0 -0.007812 - -!!! compat "Julia 1.7" - Starting in Julia 1.7, `%s` (string) and `%c` (character) widths are computed - using [`textwidth`](@ref), which e.g. ignores zero-width characters - (such as combining characters for diacritical marks) and treats certain - "wide" characters (e.g. emoji) as width `2`. ``` !!! compat "Julia 1.8" @@ -1027,6 +1021,9 @@ julia> @printf "%.0f %.1f %f" 0.5 0.025 -0.0078125 using [`textwidth`](@ref), which e.g. ignores zero-width characters (such as combining characters for diacritical marks) and treats certain "wide" characters (e.g. emoji) as width `2`. + +!!! compat "Julia 1.10" + Dynamic width specifiers like `%*s` and `%0*.*f` require Julia 1.10. """ macro printf(io_or_fmt, args...) if io_or_fmt isa String From f821ebd111b77fd2ad139f93ae91cef7203ef000 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Fri, 27 Jan 2023 14:19:13 -0500 Subject: [PATCH 3/7] rm blank lines --- stdlib/Printf/src/Printf.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/stdlib/Printf/src/Printf.jl b/stdlib/Printf/src/Printf.jl index caa61b6a57c66..3f3179eb3cd0f 100644 --- a/stdlib/Printf/src/Printf.jl +++ b/stdlib/Printf/src/Printf.jl @@ -995,8 +995,6 @@ Use shorter of decimal or scientific 1.23 1.23e+07 julia> @printf "Use dynamic width and precision %*.*f" 10 2 0.12345 Use dynamic width and precision 0.12 - - ``` For a systematic specification of the format, see [here](https://www.cplusplus.com/reference/cstdio/printf/). See also [`@sprintf`](@ref) to get the result as a `String` instead of it being printed. From 243e954870ae60b284de0f617bf528fdea3a08f9 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Fri, 27 Jan 2023 14:21:43 -0500 Subject: [PATCH 4/7] news --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 6b2234b2fba68..52b8339752874 100644 --- a/NEWS.md +++ b/NEWS.md @@ -48,7 +48,7 @@ Standard library changes #### Printf - +* Format specifiers now support dynamic width and precision, e.g. `%*s` and `%*.*g` ([#40105]). #### Profile From bc61a78120d80e5909bfd018ddbef5e726a7087e Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Fri, 27 Jan 2023 21:34:34 -0600 Subject: [PATCH 5/7] Update stdlib/Printf/test/runtests.jl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mosè Giordano --- stdlib/Printf/test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/Printf/test/runtests.jl b/stdlib/Printf/test/runtests.jl index 999130d858cbe..96d61b61d02e3 100644 --- a/stdlib/Printf/test/runtests.jl +++ b/stdlib/Printf/test/runtests.jl @@ -781,7 +781,7 @@ end @test (Printf.@sprintf("%d4%n", 123, x); x[] == 4) @test (Printf.@sprintf("%s%n", "😉", x); x[] == 4) @test (Printf.@sprintf("%s%n", "1234", x); x[] == 4) - +end @testset "dynamic" begin From 00633f0986cef9bf4662fe2e90937f027bf17b9b Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 1 Feb 2023 09:17:45 -0500 Subject: [PATCH 6/7] rm whitespace --- stdlib/Printf/src/Printf.jl | 3 --- 1 file changed, 3 deletions(-) diff --git a/stdlib/Printf/src/Printf.jl b/stdlib/Printf/src/Printf.jl index 3f3179eb3cd0f..33449702ed83d 100644 --- a/stdlib/Printf/src/Printf.jl +++ b/stdlib/Printf/src/Printf.jl @@ -879,14 +879,11 @@ const UNROLL_UPTO = 16 return pos end - @inline function plength(f::Spec{T}, args, argp) where {T} f, argp = rmdynamic(f, args, argp) (plength(f, args[argp]), argp+1) end - - function plength(f::Spec{T}, x) where {T <: Chars} c = Char(first(x)) w = textwidth(c) From 059595a52de0fa1f7383c29f8cec9b779f9a86fe Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 1 Feb 2023 10:33:27 -0500 Subject: [PATCH 7/7] Update stdlib/Printf/src/Printf.jl --- stdlib/Printf/src/Printf.jl | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/stdlib/Printf/src/Printf.jl b/stdlib/Printf/src/Printf.jl index 33449702ed83d..62a84d7d36984 100644 --- a/stdlib/Printf/src/Printf.jl +++ b/stdlib/Printf/src/Printf.jl @@ -307,24 +307,6 @@ end (Spec{T}(spec.leftalign, spec.plus, spec.space, zero, spec.hash, width, precision, false, false), argp) end - -@inline function rmdynamic(spec::Spec{T}, args, argp) where {T} - zero, width, precision = spec.zero, spec.width, spec.precision - if spec.dynamic_width - width = args[argp] - argp += 1 - end - if spec.dynamic_precision - precision = args[argp] - if zero && T <: Ints && precision > 0 - zero = false - end - argp += 1 - end - (Spec{T}(spec.leftalign, spec.plus, spec.space, zero, spec.hash, width, precision, false, false), argp) -end - - @inline function fmt(buf, pos, args, argp, spec::Spec{T}) where {T} spec, argp = rmdynamic(spec, args, argp) (fmt(buf, pos, args[argp], spec), argp+1)