@@ -2,6 +2,7 @@ module DBFTables
22
33import Tables, WeakRefStrings
44using Dates
5+ using Printf: @sprintf
56
67" Field/column descriptor, part of the Header"
78struct FieldDescriptor
@@ -96,12 +97,25 @@ dbf_value(::Val{'C'}, len::UInt8, ::Missing) = ' ' ^ len
9697dbf_value (:: Val{'L'} , :: UInt8 , x:: Bool ) = x ? ' T' : ' F'
9798dbf_value (:: Val{'L'} , :: UInt8 , :: Missing ) = ' ?'
9899
99- # Integer & AbstractFloat
100- function dbf_value (:: Val{'N'} , :: UInt8 , x:: Union{AbstractFloat, Integer} )
101- minval = - 9999999999999999999
102- maxval = 99999999999999999999
103- minval ≤ x ≤ maxval && @warn " Due to DBF limitations, a float will be clamped to fit in 20 characters."
104- rpad (clamp (x, minval, maxval), 20 )
100+ # Integer
101+ function dbf_value (:: Val{'N'} , :: UInt8 , x:: Integer )
102+ s = rpad (x, 20 )
103+ length (s) > 20 ? error (" The DBF format cannot save integers >20 characters." ) : s
104+ end
105+
106+ # AbstractFloat
107+ function dbf_value (:: Val{'N'} , :: UInt8 , x:: AbstractFloat )
108+ s = rpad (x, 20 )
109+ length (s) == 20 && return s
110+ # Force into scientific notation with 20 decimal places
111+ s2 = @sprintf " %.20e" x
112+ i = findfirst (' e' , s2)
113+ s_end = replace (s2[i: end ], ' +' => " " )
114+ len = length (s_end)
115+ n = 20 - len
116+ out = s2[1 : n] * s_end
117+ @warn " A DBF limitation has reduced the precision of $x by $(length (s) - 20 ) digits."
118+ return out
105119end
106120dbf_value (:: Val{'N'} , :: UInt8 , :: Missing ) = ' ' ^ 20
107121
@@ -121,7 +135,7 @@ julia_type(::Val{'O'}, ndec::UInt8) = Float64
121135julia_type (:: Val{'I'} , ndec:: UInt8 ) = Int32
122136julia_type (:: Val{'+'} , ndec:: UInt8 ) = Int64
123137julia_type (:: Val{'L'} , ndec:: UInt8 ) = Bool
124- julia_type (:: Val{'M'} , ndec:: UInt8 ) = String
138+ julia_type (:: Val{'M'} , ndec:: UInt8 ) = String
125139function julia_type (:: Val{T} , ndec:: UInt8 ) where {T}
126140 @warn " Unknown DBF type code '$T '. Data will be loaded as `String"
127141 String
@@ -171,7 +185,7 @@ function julia_value(::Type{Bool}, ::Val{'L'}, s::AbstractString)
171185 return true
172186 elseif char in " NnFf"
173187 return false
174- else
188+ else
175189 return missing
176190 end
177191end
258272
259273
260274# ref: https://www.clicketyclick.dk/databases/xbase/format/dbf.html
261- function Base. Base . write (io:: IO , h:: Header )
275+ function Base. write (io:: IO , h:: Header )
262276 out = 0
263277 out += Base. write (io, h. version) # 0
264278 yy = UInt8 (year (h. last_update) - 1900 )
0 commit comments