From 7c407d69fea75b2210bb303bb56839c4ffab68e9 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 22 Nov 2017 17:01:31 -0500 Subject: [PATCH 01/13] add parse(Complex{T}, s) --- base/mpfr.jl | 1 + base/parse.jl | 50 +++++++++++++++++++++++++++++++++++++++++++++++--- test/mpfr.jl | 20 ++++---------------- test/parse.jl | 17 +++++++++++++++++ 4 files changed, 69 insertions(+), 19 deletions(-) diff --git a/base/mpfr.jl b/base/mpfr.jl index 3048657ee59d3..8bff353fdca7f 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -125,6 +125,7 @@ convert(::Type{BigFloat}, x::Union{Float16,Float32}) = BigFloat(Float64(x)) convert(::Type{BigFloat}, x::Rational) = BigFloat(numerator(x)) / BigFloat(denominator(x)) function tryparse(::Type{BigFloat}, s::AbstractString, base::Int=0) + !isempty(s) && isspace(s[end]) && return tryparse(BigFloat, rstrip(s), base) z = BigFloat() err = ccall((:mpfr_set_str, :libmpfr), Int32, (Ref{BigFloat}, Cstring, Int32, Int32), z, s, base, ROUNDING_MODE[]) err == 0 ? Nullable(z) : Nullable{BigFloat}() diff --git a/base/parse.jl b/base/parse.jl index d4fbff975321d..73f3d1410cb72 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -215,7 +215,6 @@ function parse(::Type{T}, s::AbstractString) where T<:Integer get(tryparse_internal(T, s, start(s), endof(s), 0, true)) # Zero means, "figure it out" end - ## string to float functions ## tryparse(::Type{Float64}, s::String) = ccall(:jl_try_substrtod, Nullable{Float64}, (Ptr{UInt8},Csize_t,Csize_t), s, 0, sizeof(s)) @@ -228,11 +227,56 @@ tryparse(::Type{T}, s::AbstractString) where {T<:Union{Float32,Float64}} = trypa tryparse(::Type{Float16}, s::AbstractString) = convert(Nullable{Float16}, tryparse(Float32, s)) -function parse(::Type{T}, s::AbstractString) where T<:AbstractFloat + +## string to complex functions ## + +function tryparse(::Type{Complex{T}}, s::Union{String,SubString{String}}) where {T<:Real} + # skip initial whitespace + i = start(s) + e = endof(s) + while i ≤ e && isspace(s[i]) + i = nextind(s, i) + end + i > e && return Nullable{Complex{T}}() + + # find index of ± separating real/imaginary parts (if any) + i₊ = search(s, ('+','-'), i) + if i₊ == i # leading ± sign + i₊ = search(s, ('+','-'), i₊+1) + end + if i₊ != 0 && s[i₊-1] in ('e','E') # exponent sign + i₊ = search(s, ('+','-'), i₊+1) + end + if i₊ == 0 # purely real value + return Nullable{Complex{T}}(tryparse(T, s)) + end + + # find trailing im/i/j + iᵢ = rsearch(s, ('m','i','j'), e) + iᵢ < i₊ && return Nullable{Complex{T}}() + if s[iᵢ] == 'm' # im + iᵢ -= 1 + s[iᵢ] == 'i' || return Nullable{Complex{T}}() + end + + # parse real part + re = tryparse(T, SubString(s, i, i₊-1)) + isnull(re) && return Nullable{Complex{T}}() + + # parse imaginary part + im = tryparse(T, SubString(s, i₊+1, iᵢ-1)) + isnull(im) && return Nullable{Complex{T}}() + + return Nullable{Complex{T}}(Complex{T}(get(re), s[i₊]=='-' ? -get(im) : get(im))) +end + +# the ±1 indexing above for ascii chars is specific to String, so convert: +tryparse(T::Type{<:Complex}, s::AbstractString) = tryparse(T, String(s)) + +function parse(::Type{T}, s::AbstractString) where T<:Union{AbstractFloat,Complex} result = tryparse(T, s) if isnull(result) throw(ArgumentError("cannot parse $(repr(s)) as $T")) end return unsafe_get(result) end - diff --git a/test/mpfr.jl b/test/mpfr.jl index 5dc38118ae3f9..2c73f7e6ea7d2 100644 --- a/test/mpfr.jl +++ b/test/mpfr.jl @@ -7,22 +7,10 @@ import Base.MPFR x = BigFloat(12) end x = BigFloat(12) - y = BigFloat(x) - @test x ≈ y - y = BigFloat(0xc) - @test x ≈ y - y = BigFloat(12.) - @test x ≈ y - y = BigFloat(BigInt(12)) - @test x ≈ y - y = BigFloat(BigFloat(12)) - @test x ≈ y - y = parse(BigFloat,"12") - @test x ≈ y - y = BigFloat(Float32(12.)) - @test x ≈ y - y = BigFloat(12//1) - @test x ≈ y + @test x == BigFloat(x) == BigFloat(0xc) == BigFloat(12.) == + BigFloat(BigInt(12)) == BigFloat(BigFloat(12)) == parse(BigFloat,"12") == + parse(BigFloat,"12 ") == parse(BigFloat," 12") == parse(BigFloat," 12 ") == + BigFloat(Float32(12.)) == BigFloat(12//1) @test typeof(BigFloat(typemax(Int8))) == BigFloat @test typeof(BigFloat(typemax(Int16))) == BigFloat diff --git a/test/parse.jl b/test/parse.jl index e338020bf0f72..4fddc6ebacff9 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -229,3 +229,20 @@ end @test tryparse(Float32, "1.23") === Nullable(1.23f0) @test tryparse(Float16, "1.23") === Nullable(Float16(1.23)) +# parsing complex numbers (#22250) +@testset "complex parsing" begin + for r in (1,0,-1), i in (1,0,-1), sign in ('-','+'), Im in ("i","j","im") + for s1 in (""," "), s2 in (""," "), s3 in (""," "), s4 in (""," ") + n = Complex(r, sign == '+' ? i : -i) + s = string(s1, r, s2, sign, s3, i, Im, s4) + @test n === parse(Complex{Int}, s) + for T in (Float64, BigFloat) + nT = parse(Complex{T}, s) + @test nT isa Complex{T} + @test nT == n + @test n == parse(Complex{T}, string(s1, r, ".0", s2, sign, s3, i, ".0", Im, s4)) + @test n*parse(T,"1e-3") == parse(Complex{T}, string(s1, r, "e-3", s2, sign, s3, i, "e-3", Im, s4)) + end + end + end +end From 98d8265e4971cf953a1c448591f77530ddfb28de Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 22 Nov 2017 17:06:08 -0500 Subject: [PATCH 02/13] NEWS --- NEWS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS.md b/NEWS.md index d3a5986905af1..ccfab8f2b889b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -325,6 +325,8 @@ Library improvements * The function `randn` now accepts complex arguments (`Complex{T <: AbstractFloat}`) ([#21973]). + * `parse(Complex{T}, string)` is now implemented ([#24713]). + * The function `rand` can now pick up random elements from strings, associatives and sets ([#22228], [#21960], [#18155], [#22224]). From b798bef0ff27e3d24530e69ec072ddd4a188d704 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 22 Nov 2017 17:09:36 -0500 Subject: [PATCH 03/13] added a substring test --- test/parse.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/parse.jl b/test/parse.jl index 4fddc6ebacff9..77caac4df6dfc 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -245,4 +245,5 @@ end end end end + @test parse(Complex{Int}, SubString("xxxxxx1+2imxxxx", 7, 10)) === 1+2im end From b6454b6f9c1842ee58c7024b7437f59d32310cd9 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 22 Nov 2017 17:18:44 -0500 Subject: [PATCH 04/13] doc --- base/parse.jl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/base/parse.jl b/base/parse.jl index 73f3d1410cb72..754e4095bfba3 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -7,9 +7,11 @@ import Base.Checked: add_with_overflow, mul_with_overflow """ parse(type, str, [base]) -Parse a string as a number. If the type is an integer type, then a base can be specified -(the default is 10). If the type is a floating point type, the string is parsed as a decimal -floating point number. If the string does not contain a valid number, an error is raised. +Parse a string as a number. For `Integer` types, a base can be specified +(the default is 10). For floating-point types, the string is parsed as a decimal +floating-point number. `Complex` types are parsed from decimal strings +of the form `R±Iim` as a `Complex(R,I)` of the requested type; `i` or `j` can also be +used instead of `im`. If the string does not contain a valid number, an error is raised. ```jldoctest julia> parse(Int, "1234") From 5399141f9c7568fa863ce0236ae8e443cfaad92b Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 22 Nov 2017 17:19:14 -0500 Subject: [PATCH 05/13] doc --- base/parse.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/parse.jl b/base/parse.jl index 754e4095bfba3..b17f67e66e55d 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -10,7 +10,7 @@ import Base.Checked: add_with_overflow, mul_with_overflow Parse a string as a number. For `Integer` types, a base can be specified (the default is 10). For floating-point types, the string is parsed as a decimal floating-point number. `Complex` types are parsed from decimal strings -of the form `R±Iim` as a `Complex(R,I)` of the requested type; `i` or `j` can also be +of the form `"R±Iim"` as a `Complex(R,I)` of the requested type; `i` or `j` can also be used instead of `im`. If the string does not contain a valid number, an error is raised. ```jldoctest From c535c3db1fdc7379510b247a9502adecc435d700 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 22 Nov 2017 17:19:36 -0500 Subject: [PATCH 06/13] doc --- base/parse.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/parse.jl b/base/parse.jl index b17f67e66e55d..aee78feaecd81 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -10,8 +10,8 @@ import Base.Checked: add_with_overflow, mul_with_overflow Parse a string as a number. For `Integer` types, a base can be specified (the default is 10). For floating-point types, the string is parsed as a decimal floating-point number. `Complex` types are parsed from decimal strings -of the form `"R±Iim"` as a `Complex(R,I)` of the requested type; `i` or `j` can also be -used instead of `im`. If the string does not contain a valid number, an error is raised. +of the form `"R±Iim"` as a `Complex(R,I)` of the requested type; `"i"` or `"j"` can also be +used instead of `"im"`. If the string does not contain a valid number, an error is raised. ```jldoctest julia> parse(Int, "1234") From 2f203f562528dbb900f76088f5fabf3580a50138 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 22 Nov 2017 20:47:48 -0500 Subject: [PATCH 07/13] support purely imaginary expressions like 3j, add more tests --- base/parse.jl | 25 +++++++++++++++++++------ test/parse.jl | 6 ++++++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/base/parse.jl b/base/parse.jl index aee78feaecd81..ca5214dad0f94 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -11,7 +11,8 @@ Parse a string as a number. For `Integer` types, a base can be specified (the default is 10). For floating-point types, the string is parsed as a decimal floating-point number. `Complex` types are parsed from decimal strings of the form `"R±Iim"` as a `Complex(R,I)` of the requested type; `"i"` or `"j"` can also be -used instead of `"im"`. If the string does not contain a valid number, an error is raised. +used instead of `"im"`, and `"R"` or `"Iim"` are also permitted. +If the string does not contain a valid number, an error is raised. ```jldoctest julia> parse(Int, "1234") @@ -25,6 +26,9 @@ julia> parse(Int, "afc", 16) julia> parse(Float64, "1.2e-3") 0.0012 + +julia> parse(Complex{Float64}, "3.2e-1 + 4.5im") +0.32 + 4.5im ``` """ parse(T::Type, str, base=Int) @@ -249,18 +253,27 @@ function tryparse(::Type{Complex{T}}, s::Union{String,SubString{String}}) where if i₊ != 0 && s[i₊-1] in ('e','E') # exponent sign i₊ = search(s, ('+','-'), i₊+1) end - if i₊ == 0 # purely real value - return Nullable{Complex{T}}(tryparse(T, s)) - end # find trailing im/i/j iᵢ = rsearch(s, ('m','i','j'), e) - iᵢ < i₊ && return Nullable{Complex{T}}() - if s[iᵢ] == 'm' # im + if iᵢ > 0 && s[iᵢ] == 'm' # im iᵢ -= 1 s[iᵢ] == 'i' || return Nullable{Complex{T}}() end + if i₊ == 0 # purely real or imaginary value + if iᵢ > 0 # purely imaginary + x_ = tryparse(T, SubString(s, i, iᵢ-1)) + isnull(x_) && return Nullable{Complex{T}}() + x = get(x_) + return Nullable{Complex{T}}(Complex{T}(zero(x),x)) + else # purely real + return Nullable{Complex{T}}(tryparse(T, s)) + end + end + + iᵢ < i₊ && return Nullable{Complex{T}}() # no imaginary part + # parse real part re = tryparse(T, SubString(s, i, i₊-1)) isnull(re) && return Nullable{Complex{T}}() diff --git a/test/parse.jl b/test/parse.jl index 77caac4df6dfc..258346e8a0bed 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -236,6 +236,8 @@ end n = Complex(r, sign == '+' ? i : -i) s = string(s1, r, s2, sign, s3, i, Im, s4) @test n === parse(Complex{Int}, s) + @test Complex(r) === parse(Complex{Int}, string(s1, r, s2)) + @test Complex(0,i) === parse(Complex{Int}, string(s3, i, Im, s4)) for T in (Float64, BigFloat) nT = parse(Complex{T}, s) @test nT isa Complex{T} @@ -246,4 +248,8 @@ end end end @test parse(Complex{Int}, SubString("xxxxxx1+2imxxxx", 7, 10)) === 1+2im + for T in (Int, Float64), bad in ("3 + 4*im", "3 + 4", "1+2ij", "1im-3im", "++4im") + @test_throws ArgumentError parse(Complex{T}, bad) + end + @test_throws ArgumentError parse(Complex{Int}, "3 + 4.2im") end From e560ba136fede7476b0fee89df73af6febf10d36 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 22 Nov 2017 22:13:41 -0500 Subject: [PATCH 08/13] extend tryparse_internal for Complex and Float --- base/parse.jl | 56 ++++++++++++++------- stdlib/DelimitedFiles/src/DelimitedFiles.jl | 21 +++----- stdlib/DelimitedFiles/test/runtests.jl | 4 ++ test/parse.jl | 1 + 4 files changed, 51 insertions(+), 31 deletions(-) diff --git a/base/parse.jl b/base/parse.jl index ca5214dad0f94..b476103665d46 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -225,25 +225,31 @@ end tryparse(::Type{Float64}, s::String) = ccall(:jl_try_substrtod, Nullable{Float64}, (Ptr{UInt8},Csize_t,Csize_t), s, 0, sizeof(s)) tryparse(::Type{Float64}, s::SubString{String}) = ccall(:jl_try_substrtod, Nullable{Float64}, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset, s.endof) +tryparse_internal(::Type{Float64}, s::String, startpos::Int, endpos::Int) = ccall(:jl_try_substrtod, Nullable{Float64}, (Ptr{UInt8},Csize_t,Csize_t), s, startpos-1, endpos-startpos+1) +tryparse_internal(::Type{Float64}, s::SubString{String}, startpos::Int, endpos::Int) = ccall(:jl_try_substrtod, Nullable{Float64}, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset+startpos-1, s.offset+endpos-startpos+1) tryparse(::Type{Float32}, s::String) = ccall(:jl_try_substrtof, Nullable{Float32}, (Ptr{UInt8},Csize_t,Csize_t), s, 0, sizeof(s)) tryparse(::Type{Float32}, s::SubString{String}) = ccall(:jl_try_substrtof, Nullable{Float32}, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset, s.endof) +tryparse_internal(::Type{Float32}, s::String, startpos::Int, endpos::Int) = ccall(:jl_try_substrtof, Nullable{Float32}, (Ptr{UInt8},Csize_t,Csize_t), s, startpos-1, endpos-startpos+1) +tryparse_internal(::Type{Float32}, s::SubString{String}, startpos::Int, endpos::Int) = ccall(:jl_try_substrtof, Nullable{Float32}, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset+startpos-1, s.offset+endpos-startpos+1) tryparse(::Type{T}, s::AbstractString) where {T<:Union{Float32,Float64}} = tryparse(T, String(s)) tryparse(::Type{Float16}, s::AbstractString) = convert(Nullable{Float16}, tryparse(Float32, s)) - +tryparse_internal(::Type{Float16}, s::SubString{String}, startpos::Int, endpos::Int) = + convert(Nullable{Float16}, tryparse_internal(Float32, s, startpos, endpos)) ## string to complex functions ## -function tryparse(::Type{Complex{T}}, s::Union{String,SubString{String}}) where {T<:Real} +function tryparse_internal(::Type{Complex{T}}, s::Union{String,SubString{String}}, i::Int, e::Int, raise::Bool) where {T<:Real} # skip initial whitespace - i = start(s) - e = endof(s) while i ≤ e && isspace(s[i]) i = nextind(s, i) end - i > e && return Nullable{Complex{T}}() + if i > e + raise && throw(ArgumentError("input string is empty or only contains whitespace")) + return Nullable{Complex{T}}() + end # find index of ± separating real/imaginary parts (if any) i₊ = search(s, ('+','-'), i) @@ -258,40 +264,54 @@ function tryparse(::Type{Complex{T}}, s::Union{String,SubString{String}}) where iᵢ = rsearch(s, ('m','i','j'), e) if iᵢ > 0 && s[iᵢ] == 'm' # im iᵢ -= 1 - s[iᵢ] == 'i' || return Nullable{Complex{T}}() + if s[iᵢ] != 'i' + raise && throw(ArgumentError("expected trailing \"im\", found only \"m\"")) + return Nullable{Complex{T}}() + end end if i₊ == 0 # purely real or imaginary value if iᵢ > 0 # purely imaginary - x_ = tryparse(T, SubString(s, i, iᵢ-1)) + x_ = tryparse_internal(T, s, i, iᵢ-1, raise) isnull(x_) && return Nullable{Complex{T}}() - x = get(x_) + x = unsafe_get(x_) return Nullable{Complex{T}}(Complex{T}(zero(x),x)) else # purely real - return Nullable{Complex{T}}(tryparse(T, s)) + return Nullable{Complex{T}}(tryparse_internal(T, s, i, e, raise)) end end - iᵢ < i₊ && return Nullable{Complex{T}}() # no imaginary part + if iᵢ < i₊ + raise && throw(ArgumentError("missing imaginary unit")) + return Nullable{Complex{T}}() # no imaginary part + end # parse real part - re = tryparse(T, SubString(s, i, i₊-1)) + re = tryparse_internal(T, s, i, i₊-1, raise) isnull(re) && return Nullable{Complex{T}}() # parse imaginary part - im = tryparse(T, SubString(s, i₊+1, iᵢ-1)) + im = tryparse_internal(T, s, i₊+1, iᵢ-1, raise) isnull(im) && return Nullable{Complex{T}}() - return Nullable{Complex{T}}(Complex{T}(get(re), s[i₊]=='-' ? -get(im) : get(im))) + return Nullable{Complex{T}}(Complex{T}(unsafe_get(re), s[i₊]=='-' ? -unsafe_get(im) : unsafe_get(im))) end # the ±1 indexing above for ascii chars is specific to String, so convert: tryparse(T::Type{<:Complex}, s::AbstractString) = tryparse(T, String(s)) -function parse(::Type{T}, s::AbstractString) where T<:Union{AbstractFloat,Complex} - result = tryparse(T, s) - if isnull(result) - throw(ArgumentError("cannot parse $(repr(s)) as $T")) +# fallback methods for tryparse_internal +tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos::Int) where T<:Real = + startpos == start(s) && endpos == endof(s) ? tryparse(T, s) : tryparse(T, SubString(s, startpos, endpos)) +function tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos::Int, raise::Bool) where T<:Real + result = tryparse_internal(T, s, startpos, endpos) + if raise && isnull(result) + throw(ArgumentError("cannot parse $(repr(s[startpos:endpos])) as $T")) end - return unsafe_get(result) + return result end +tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos::Int, raise::Bool) where T<:Integer = + tryparse_internal(T, s, startpos, endpos, 10, raise) + +parse(::Type{T}, s::AbstractString) where T<:Union{Real,Complex} = + unsafe_get(tryparse_internal(T, s, start(s), endof(s), true)) diff --git a/stdlib/DelimitedFiles/src/DelimitedFiles.jl b/stdlib/DelimitedFiles/src/DelimitedFiles.jl index 9326ac78938db..964bec6e98dac 100644 --- a/stdlib/DelimitedFiles/src/DelimitedFiles.jl +++ b/stdlib/DelimitedFiles/src/DelimitedFiles.jl @@ -393,22 +393,17 @@ end function colval(sbuff::String, startpos::Int, endpos::Int, cells::Array{Bool,2}, row::Int, col::Int) n = tryparse_internal(Bool, sbuff, startpos, endpos, 0, false) - isnull(n) || (cells[row, col] = get(n)) + isnull(n) || (cells[row, col] = unsafe_get(n)) isnull(n) end function colval(sbuff::String, startpos::Int, endpos::Int, cells::Array{T,2}, row::Int, col::Int) where T<:Integer n = tryparse_internal(T, sbuff, startpos, endpos, 0, false) - isnull(n) || (cells[row, col] = get(n)) + isnull(n) || (cells[row, col] = unsafe_get(n)) isnull(n) end -function colval(sbuff::String, startpos::Int, endpos::Int, cells::Array{Float64,2}, row::Int, col::Int) - n = ccall(:jl_try_substrtod, Nullable{Float64}, (Ptr{UInt8},Csize_t,Csize_t), sbuff, startpos-1, endpos-startpos+1) - isnull(n) || (cells[row, col] = get(n)) - isnull(n) -end -function colval(sbuff::String, startpos::Int, endpos::Int, cells::Array{Float32,2}, row::Int, col::Int) - n = ccall(:jl_try_substrtof, Nullable{Float32}, (Ptr{UInt8}, Csize_t, Csize_t), sbuff, startpos-1, endpos-startpos+1) - isnull(n) || (cells[row, col] = get(n)) +function colval(sbuff::String, startpos::Int, endpos::Int, cells::Array{T,2}, row::Int, col::Int) where T<:Union{Real,Complex} + n = tryparse_internal(T, sbuff, startpos, endpos, false) + isnull(n) || (cells[row, col] = unsafe_get(n)) isnull(n) end function colval(sbuff::String, startpos::Int, endpos::Int, cells::Array{<:AbstractString,2}, row::Int, col::Int) @@ -421,15 +416,15 @@ function colval(sbuff::String, startpos::Int, endpos::Int, cells::Array{Any,2}, if len > 0 # check Inteter ni64 = tryparse_internal(Int, sbuff, startpos, endpos, 0, false) - isnull(ni64) || (cells[row, col] = get(ni64); return false) + isnull(ni64) || (cells[row, col] = unsafe_get(ni64); return false) # check Bool nb = tryparse_internal(Bool, sbuff, startpos, endpos, 0, false) - isnull(nb) || (cells[row, col] = get(nb); return false) + isnull(nb) || (cells[row, col] = unsafe_get(nb); return false) # check float64 nf64 = ccall(:jl_try_substrtod, Nullable{Float64}, (Ptr{UInt8}, Csize_t, Csize_t), sbuff, startpos-1, endpos-startpos+1) - isnull(nf64) || (cells[row, col] = get(nf64); return false) + isnull(nf64) || (cells[row, col] = unsafe_get(nf64); return false) end cells[row, col] = SubString(sbuff, startpos, endpos) false diff --git a/stdlib/DelimitedFiles/test/runtests.jl b/stdlib/DelimitedFiles/test/runtests.jl index f41bd7d0728fa..092f6aa87f815 100644 --- a/stdlib/DelimitedFiles/test/runtests.jl +++ b/stdlib/DelimitedFiles/test/runtests.jl @@ -290,3 +290,7 @@ let d = TextDisplay(IOBuffer()) display(d, "text/csv", [3 1 4]) @test String(take!(d.io)) == "3,1,4\n" end + +@testset "complex" begin + @test readdlm(IOBuffer("3+4im, 4+5im"), ',', Complex{Int}) == [3+4im 4+5im] +end diff --git a/test/parse.jl b/test/parse.jl index 258346e8a0bed..2792058e18237 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -247,6 +247,7 @@ end end end end + @test parse(Complex{Float16}, "3.3+4i") === Complex{Float16}(3.3+4im) @test parse(Complex{Int}, SubString("xxxxxx1+2imxxxx", 7, 10)) === 1+2im for T in (Int, Float64), bad in ("3 + 4*im", "3 + 4", "1+2ij", "1im-3im", "++4im") @test_throws ArgumentError parse(Complex{T}, bad) From 9c800762468b5ed7e7fb2da69b394e06f8304e35 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 22 Nov 2017 22:24:10 -0500 Subject: [PATCH 09/13] typo --- base/parse.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/parse.jl b/base/parse.jl index b476103665d46..bd36add392d98 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -236,7 +236,7 @@ tryparse_internal(::Type{Float32}, s::SubString{String}, startpos::Int, endpos:: tryparse(::Type{T}, s::AbstractString) where {T<:Union{Float32,Float64}} = tryparse(T, String(s)) tryparse(::Type{Float16}, s::AbstractString) = convert(Nullable{Float16}, tryparse(Float32, s)) -tryparse_internal(::Type{Float16}, s::SubString{String}, startpos::Int, endpos::Int) = +tryparse_internal(::Type{Float16}, s::AbstractString, startpos::Int, endpos::Int) = convert(Nullable{Float16}, tryparse_internal(Float32, s, startpos, endpos)) ## string to complex functions ## From b6f8a1419f2d23611373a6c9c9184ff942381aed Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 22 Nov 2017 22:33:26 -0500 Subject: [PATCH 10/13] whoops --- base/parse.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/parse.jl b/base/parse.jl index bd36add392d98..470b5abb7b8cc 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -298,7 +298,8 @@ function tryparse_internal(::Type{Complex{T}}, s::Union{String,SubString{String} end # the ±1 indexing above for ascii chars is specific to String, so convert: -tryparse(T::Type{<:Complex}, s::AbstractString) = tryparse(T, String(s)) +tryparse_internal(T::Type{<:Complex}, s::AbstractString, i::Int, e::Int, raise::Bool) = + tryparse_internal(T, String(s), i, e, raise) # fallback methods for tryparse_internal tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos::Int) where T<:Real = From 0b977eadf3d091fb37a868df2f540c03bc0afbe5 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 22 Nov 2017 22:35:09 -0500 Subject: [PATCH 11/13] clearly, SubString{String} was intended here --- base/parse.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/parse.jl b/base/parse.jl index 470b5abb7b8cc..1518a5e41787f 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -157,7 +157,7 @@ function tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos:: return Nullable{T}(n) end -function tryparse_internal(::Type{Bool}, sbuff::Union{String,SubString}, +function tryparse_internal(::Type{Bool}, sbuff::Union{String,SubString{String}}, startpos::Int, endpos::Int, base::Integer, raise::Bool) if isempty(sbuff) raise && throw(ArgumentError("input string is empty")) From b253a01d8be5607db82317b4fb73d7d2ca8c3943 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 23 Nov 2017 10:01:24 -0500 Subject: [PATCH 12/13] fix strings/basic test failure --- base/parse.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/parse.jl b/base/parse.jl index 1518a5e41787f..87447ba0a0a90 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -226,12 +226,12 @@ end tryparse(::Type{Float64}, s::String) = ccall(:jl_try_substrtod, Nullable{Float64}, (Ptr{UInt8},Csize_t,Csize_t), s, 0, sizeof(s)) tryparse(::Type{Float64}, s::SubString{String}) = ccall(:jl_try_substrtod, Nullable{Float64}, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset, s.endof) tryparse_internal(::Type{Float64}, s::String, startpos::Int, endpos::Int) = ccall(:jl_try_substrtod, Nullable{Float64}, (Ptr{UInt8},Csize_t,Csize_t), s, startpos-1, endpos-startpos+1) -tryparse_internal(::Type{Float64}, s::SubString{String}, startpos::Int, endpos::Int) = ccall(:jl_try_substrtod, Nullable{Float64}, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset+startpos-1, s.offset+endpos-startpos+1) +tryparse_internal(::Type{Float64}, s::SubString{String}, startpos::Int, endpos::Int) = ccall(:jl_try_substrtod, Nullable{Float64}, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset+startpos-1, endpos-startpos+1) tryparse(::Type{Float32}, s::String) = ccall(:jl_try_substrtof, Nullable{Float32}, (Ptr{UInt8},Csize_t,Csize_t), s, 0, sizeof(s)) tryparse(::Type{Float32}, s::SubString{String}) = ccall(:jl_try_substrtof, Nullable{Float32}, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset, s.endof) tryparse_internal(::Type{Float32}, s::String, startpos::Int, endpos::Int) = ccall(:jl_try_substrtof, Nullable{Float32}, (Ptr{UInt8},Csize_t,Csize_t), s, startpos-1, endpos-startpos+1) -tryparse_internal(::Type{Float32}, s::SubString{String}, startpos::Int, endpos::Int) = ccall(:jl_try_substrtof, Nullable{Float32}, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset+startpos-1, s.offset+endpos-startpos+1) +tryparse_internal(::Type{Float32}, s::SubString{String}, startpos::Int, endpos::Int) = ccall(:jl_try_substrtof, Nullable{Float32}, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset+startpos-1, endpos-startpos+1) tryparse(::Type{T}, s::AbstractString) where {T<:Union{Float32,Float64}} = tryparse(T, String(s)) From 7a406d7d5c614b288ed8c931fb43423557d7e89a Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Tue, 5 Dec 2017 10:11:44 -0500 Subject: [PATCH 13/13] Update NEWS.md --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index ccfab8f2b889b..a7004d6aba73b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -325,7 +325,7 @@ Library improvements * The function `randn` now accepts complex arguments (`Complex{T <: AbstractFloat}`) ([#21973]). - * `parse(Complex{T}, string)` is now implemented ([#24713]). + * `parse(Complex{T}, string)` can parse complex numbers in common formats ([#24713]). * The function `rand` can now pick up random elements from strings, associatives and sets ([#22228], [#21960], [#18155], [#22224]).