Skip to content

Commit 62e3b0c

Browse files
authored
Jd/dev (#26)
* bump version * truncate Numeric types to 20 characters in a smarter way * fix typo
1 parent eb47544 commit 62e3b0c

File tree

3 files changed

+36
-9
lines changed

3 files changed

+36
-9
lines changed

Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ version = "1.2.2"
44

55
[deps]
66
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
7+
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
78
Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
89
WeakRefStrings = "ea10d353-3f73-51f8-a26c-33c1cb351aa5"
910

src/DBFTables.jl

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module DBFTables
22

33
import Tables, WeakRefStrings
44
using Dates
5+
using Printf: @sprintf
56

67
"Field/column descriptor, part of the Header"
78
struct FieldDescriptor
@@ -96,12 +97,25 @@ dbf_value(::Val{'C'}, len::UInt8, ::Missing) = ' ' ^ len
9697
dbf_value(::Val{'L'}, ::UInt8, x::Bool) = x ? 'T' : 'F'
9798
dbf_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
105119
end
106120
dbf_value(::Val{'N'}, ::UInt8, ::Missing) = ' ' ^ 20
107121

@@ -121,7 +135,7 @@ julia_type(::Val{'O'}, ndec::UInt8) = Float64
121135
julia_type(::Val{'I'}, ndec::UInt8) = Int32
122136
julia_type(::Val{'+'}, ndec::UInt8) = Int64
123137
julia_type(::Val{'L'}, ndec::UInt8) = Bool
124-
julia_type(::Val{'M'}, ndec::UInt8) = String
138+
julia_type(::Val{'M'}, ndec::UInt8) = String
125139
function 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
177191
end
@@ -258,7 +272,7 @@ end
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)

test/runtests.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,16 @@ row, st = iterate(dbf)
118118
@test !DBFTables.isdeleted(dbf, 3)
119119
end
120120

121+
@testset "Numeric 20-character Limit Nonsense" begin
122+
big = BigInt(99999_99999_99999_99999)
123+
@test DBFTables.dbf_value(Val('N'), 0x01, big) == string(big)
124+
@test_throws Exception DBFTables.dbf_value(Val('N'), 0x01, big + 1)
125+
126+
negbig = -BigInt(99999_99999_99999_9999) # one less digit for the minus sign
127+
@test DBFTables.dbf_value(Val('N'), 0x01, negbig) == string(negbig)
128+
@test_throws Exception DBFTables.dbf_value(Val('N'), 0x01, negbig - 1)
129+
130+
@test_warn r"DBF limitation" DBFTables.dbf_value(Val('N'), 0x01, prevfloat(Inf))
131+
@test_warn r"DBF limitation" DBFTables.dbf_value(Val('N'), 0x01, nextfloat(-Inf))
132+
end
121133
end # testset "DBFTables"

0 commit comments

Comments
 (0)