From d349c0f759adc60ad4ecf6487689eadc2ad920f1 Mon Sep 17 00:00:00 2001 From: hustf <hustf@users.noreply.github.com> Date: Sun, 8 Nov 2020 17:23:07 +0100 Subject: [PATCH] modified: README.md Add 'parse' example, quantities from text files modified: src/MechanicalUnits.jl Add 'parse' new file: src/parse.jl New file modified: test/conversion_promotion.jl Add 'parse' tests --- README.md | 18 ++++++++++++ src/MechanicalUnits.jl | 9 +++++- src/parse.jl | 56 ++++++++++++++++++++++++++++++++++++ test/conversion_promotion.jl | 11 +++++++ 4 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 src/parse.jl diff --git a/README.md b/README.md index 88349bf..f967b4b 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,24 @@ Unitfu.FreeUnits{(dyn,), ᴸ∙ ᴹ∙ ᵀ⁻²,nothing} julia> 1dyn |> μm 10kg∙μm∙s⁻² + +julia> # When parsing text file, spaces as multipliers and brackets are allowed. Just specify the numeric type: +julia> lin = "2 [s]\t11364.56982421875 [N]\t-44553.50244140625 [N]\t-26.586366176605225 [N]\t0.0[N mm]\t0.0[N mm]\t0.0[N mm]\t1561.00350618362 [mm]\t-6072.3729133606 [mm]\t2825.15907287598 [mm]" +"2 [s]\t11364.56982421875 [N]\t-44553.50244140625 [N]\t-26.586366176605225 [N]\t0.0[N mm]\t0.0[N mm]\t0.0[N mm]\t1561.00350618362 [mm]\t-6072.3729133606 [mm]\t2825.15907287598 [mm]" + +julia> time, Fx, Fy, Fz, Mx, My, Mz, px, py, pz = parse.(Quantity{Float64}, split(lin, '\t')) +10-element Array{Quantity{Float64,D,U} where U where D,1}: + 2.0s + 11364.56982421875N + -44553.50244140625N + -26.586366176605225N + 0.0mm∙N + 0.0mm∙N + 0.0mm∙N + 1561.00350618362mm + -6072.3729133606mm + 2825.15907287598mm + ``` ## Goals diff --git a/src/MechanicalUnits.jl b/src/MechanicalUnits.jl index edf592e..9612f28 100644 --- a/src/MechanicalUnits.jl +++ b/src/MechanicalUnits.jl @@ -5,6 +5,7 @@ export ∙ # Import / exports for short and parseable type signatures import Unitfu: Time, Length, Mass, Temperature, Current, Luminosity, Amount import Unitfu: ᵀ , ᴸ , ᴹ , ᶿ, ᴶ , ᴺ +import Unitfu: lookup_units export Time, Length, Mass, Temperature, Current, Luminosity, Amount, Level export ᵀ , ᴸ , ᴹ , ᶿ , ᴶ , ᴺ export Quantity, DimensionlessQuantity, NoUnits, NoDims @@ -13,7 +14,7 @@ import Unitfu: export FreeUnits, AffineUnits, Affine, AffineQuantity, Unitlike, Unit, Dimensions, Dimension, Units export Level, Gain -# For importinng from Unitfu, or defining more units +# For importing from Unitfu, or defining more units export @import_expand, @unit, @u_str # Reexported functions from Unitfu @@ -33,6 +34,10 @@ import Unitfu: Area, Acceleration, Force, Pressure, Density import Unitfu: Velocity import Unitfu:ForceFreeUnits, PressureFreeUnits, EnergyFreeUnits, AreaFreeUnits, DensityFreeUnits, VolumeFreeUnits export Area, Acceleration, Force, Pressure, Density, Velocity + +# Extend base. This could perhaps reside in Unitfu +import Base: tryparse_internal, parse + # Units are exported in 'import_export_units.jl'. include("internal_functions.jl") @@ -49,6 +54,7 @@ eval(exponents_superscripts(:ᴺ)) # Used for registering units with Unitfu macros during initialisation. const localunits = Unitfu.basefactors +include("parse.jl") function __init__() # This is for evaluating Unitfu macros in the context of this package. merge!(Unitfu.basefactors, localunits) @@ -60,4 +66,5 @@ function __init__() Sys.isapple() && push!(ENV, "UNITFUL_FANCY_EXPONENTS" => "true") end + end # module diff --git a/src/parse.jl b/src/parse.jl new file mode 100644 index 0000000..970d153 --- /dev/null +++ b/src/parse.jl @@ -0,0 +1,56 @@ +parse(::Type{Quantity{T}}, s::AbstractString; kwargs...) where {T <: Real} = + convert(Quantity{T}, tryparse_internal(Quantity{T}, s, firstindex(s), lastindex(s), 10, true; kwargs...)) + +function tryparse_internal(::Type{Quantity{T}}, sbuff::Union{String,SubString{String}}, + startpos::Int, endpos::Int, base::Integer, raise::Bool) where {T<:Real} + if isempty(sbuff) + raise && throw(ArgumentError("input string is empty")) + return nothing + end + + orig_start = startpos + orig_end = endpos + + # Ignore leading and trailing whitespace + while isspace(sbuff[startpos]) && startpos <= endpos + startpos = nextind(sbuff, startpos) + end + while isspace(sbuff[endpos]) && endpos >= startpos + endpos = prevind(sbuff, endpos) + end + + # Find first character of unit specification + unitpos = startpos + while sbuff[unitpos] ∈ "+-0123456789.," && unitpos <= endpos + unitpos = nextind(sbuff, unitpos) + end + + numlen = unitpos - startpos + 1 + unitlen = endpos - unitpos + 1 + + if numlen > 0 && unitlen > 0 + num = parse(T, sbuff[startpos:unitpos - 1]) + suni = strip(sbuff[unitpos:endpos], ['[', ']', ' ']) + if '/' ∉ suni + suni = replace(suni, r"[∙· *]" => '∙') + sunits = split(suni, '∙') + uni = lookup_units(MechanicalUnits, Symbol(sunits[1])) + for string_unit in sunits[2:lastindex(sunits)] + uni *= lookup_units(MechanicalUnits, Symbol(string_unit)) + end + return num * uni + end + end + + if raise + substr = SubString(sbuff, orig_start, orig_end) # show input string in the error to avoid confusion + if all(isspace, substr) + throw(ArgumentError("input string only contains whitespace")) + elseif '/' ∉ sbuff[unitpos:endpos] + throw(ArgumentError("input string contains '/' unit. Replace input with multiplied units: 'm/s' => 'm∙s⁻¹'")) + else + throw(ArgumentError("invalid quantity representation: $(repr(substr))")) + end + end + return nothing +end \ No newline at end of file diff --git a/test/conversion_promotion.jl b/test/conversion_promotion.jl index 769b4f2..f273354 100644 --- a/test/conversion_promotion.jl +++ b/test/conversion_promotion.jl @@ -48,3 +48,14 @@ end @test (1m, 1m^2/mm) == (1000, 1000000)mm @test (1m, 1m^2/mm) == (1, 1000)m end + +@testset "Quantity parse" begin +@test parse(Quantity{Float64}, "2.0kN") == 2.0kN +@test parse(Quantity{Int64}, "2 kN") == 2kN +@test parse(Quantity{Int64}, "2 [m]") == 2m +@test parse(Quantity{Float64}, "2 [m]") == 2.0m +@test parse(Quantity{Float64}, "2 [N m]") == 2.0Nm +lin = "2 [s]\t11364.56982421875 [N]\t-44553.50244140625 [N]\t-26.586366176605225 [N]\t0.0[N mm]\t0.0[N mm]\t0.0[N mm]\t1561.00350618362 [mm]\t-6072.3729133606 [mm]\t2825.15907287598 [mm]" +data = parse.(Quantity{Float64}, split(lin, '\t')) +@test data == [ 2.0s, 11364.56982421875N, -44553.50244140625N, -26.586366176605225N, 0.0mm∙N, 0.0mm∙N, 0.0mm∙N, 1561.00350618362mm, -6072.3729133606mm, 2825.15907287598mm] +end