dbf 5.2.0 → 5.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +15 -7
- data/bin/dbf +2 -54
- data/lib/dbf/cli.rb +95 -0
- data/lib/dbf/column.rb +2 -2
- data/lib/dbf/column_type.rb +14 -8
- data/lib/dbf/encoder.rb +116 -0
- data/lib/dbf/encodings.rb +8 -0
- data/lib/dbf/table.rb +9 -4
- data/lib/dbf/version.rb +1 -1
- data/lib/dbf.rb +2 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d2d1ca393ae41262fd052c74ffe0ec497a963a2c036797686061312a0c492888
|
|
4
|
+
data.tar.gz: 7e92c62ace7713b883b6b2e925c34f7a8114ebd6345b36a81f97fb47109711e2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f0f3b1b02ab53f505ab3efc1dff89d2ad1169698ef7d9443b3eec3b758b921ae2df51bf443d4e877ac072bdef87c547607d1d39d913a5c361b4c31e21fee4b07
|
|
7
|
+
data.tar.gz: 6d826dd35aa6e80b671aeb804dc9991776851bbb9ca0ff5fb9ed6cd7139cbd40b0bad68327fc8f17b03b9ba2f2b77479331b7ad998c53e7ee32e5caee59241e6
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## main branch
|
|
4
|
+
|
|
5
|
+
- Add support for 8 more code pages (issue #98): Mazovia cp620 and Kamenický cp895 via vendored translation tables (new DBF::Encoder), plus macRoman, cp1255, cp1256, macCyrillic, macCentEuro and macGreek
|
|
6
|
+
- Blank Visual FoxPro "T" (DateTime) columns now return nil instead of a Julian day-0 date
|
|
7
|
+
- Blank "F" (Float) columns now return nil instead of 0.0, matching "N" (Number) behavior
|
|
8
|
+
- Extract CLI logic into DBF::CLI class (bin/dbf is now a thin shim)
|
|
9
|
+
- DBF::Table#to_csv now accepts an IO in addition to a String path
|
|
10
|
+
- Fix broken link in README
|
|
11
|
+
|
|
3
12
|
## 5.2.0
|
|
4
13
|
|
|
5
14
|
- Drop support for Ruby 3.1 and 3.2
|
data/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
DBF is a small, fast Ruby library for reading dBase, xBase, Clipper, and FoxPro database files.
|
|
11
11
|
|
|
12
12
|
* Project page: <https://github.com/infused/dbf>
|
|
13
|
-
* API Documentation: <https://
|
|
13
|
+
* API Documentation: <https://rdoc.info/gems/dbf>
|
|
14
14
|
* Report bugs: <https://github.com/infused/dbf/issues>
|
|
15
15
|
* Questions: Email <mailto:keithm@infused.org> and put DBF somewhere in the
|
|
16
16
|
subject line
|
|
@@ -21,7 +21,7 @@ NOTE: Beginning with version 5.2 we have dropped support for Ruby 3.2 and earlie
|
|
|
21
21
|
NOTE: Beginning with version 4.3 we have dropped support for Ruby 3.0 and earlier.
|
|
22
22
|
|
|
23
23
|
NOTE: Beginning with version 4 we have dropped support for Ruby 2.0, 2.1, 2.2, and 2.3. If you need support for these older Rubies,
|
|
24
|
-
please use 3.0.x (<https://github.com/infused
|
|
24
|
+
please use 3.0.x (<https://github.com/infused/dbf/tree/3_stable>)
|
|
25
25
|
|
|
26
26
|
NOTE: Beginning with version 3 we have dropped support for Ruby 1.8 and 1.9. If you need support for older Rubies,
|
|
27
27
|
please use 2.0.x (<https://github.com/infused/dbf/tree/2_stable>)
|
|
@@ -151,6 +151,7 @@ table = DBF::Table.new('dbf/books.dbf', nil, 'cp866')
|
|
|
151
151
|
| 01 | cp437 | U.S. MS–DOS |
|
|
152
152
|
| 02 | cp850 | International MS–DOS |
|
|
153
153
|
| 03 | cp1252 | Windows ANSI |
|
|
154
|
+
| 04 | macRoman | Standard Macintosh |
|
|
154
155
|
| 08 | cp865 | Danish OEM |
|
|
155
156
|
| 09 | cp437 | Dutch OEM |
|
|
156
157
|
| 0a | cp850 | Dutch OEM* |
|
|
@@ -191,6 +192,8 @@ table = DBF::Table.new('dbf/books.dbf', nil, 'cp866')
|
|
|
191
192
|
| 65 | cp866 | Russian MS–DOS |
|
|
192
193
|
| 66 | cp865 | Nordic MS–DOS |
|
|
193
194
|
| 67 | cp861 | Icelandic MS–DOS |
|
|
195
|
+
| 68 | cp895 | Kamenický (Czech) MS–DOS |
|
|
196
|
+
| 69 | cp620 | Mazovia (Polish) MS–DOS |
|
|
194
197
|
| 6a | cp737 | Greek MS–DOS (437G) |
|
|
195
198
|
| 6b | cp857 | Turkish MS–DOS |
|
|
196
199
|
| 6c | cp863 | French–Canadian MS–DOS |
|
|
@@ -199,9 +202,14 @@ table = DBF::Table.new('dbf/books.dbf', nil, 'cp866')
|
|
|
199
202
|
| 7a | cp936 | PRC GBK |
|
|
200
203
|
| 7b | cp932 | Japanese Shift-JIS |
|
|
201
204
|
| 7c | cp874 | Thai Windows/MS–DOS |
|
|
205
|
+
| 7d | cp1255 | Hebrew Windows |
|
|
206
|
+
| 7e | cp1256 | Arabic Windows |
|
|
202
207
|
| 86 | cp737 | Greek OEM |
|
|
203
208
|
| 87 | cp852 | Slovenian OEM |
|
|
204
209
|
| 88 | cp857 | Turkish OEM |
|
|
210
|
+
| 96 | macCyrillic | Russian Macintosh |
|
|
211
|
+
| 97 | macCentEuro | Macintosh EE |
|
|
212
|
+
| 98 | macGreek | Greek Macintosh |
|
|
205
213
|
| c8 | cp1250 | Eastern European Windows |
|
|
206
214
|
| c9 | cp1251 | Russian Windows |
|
|
207
215
|
| ca | cp1254 | Turkish Windows |
|
|
@@ -328,16 +336,16 @@ at this time.
|
|
|
328
336
|
| 04 | dBase IV without memo file | Y | Y | Y | Y | - | - | - | - | - | - | - | - | - | - | - | - | - |
|
|
329
337
|
| 05 | dBase V without memo file | Y | Y | Y | Y | - | - | - | - | - | - | - | - | - | - | - | - | - |
|
|
330
338
|
| 07 | Visual Objects 1.x | Y | Y | Y | Y | - | - | - | - | - | - | - | - | - | - | - | - | - |
|
|
331
|
-
| 30 | Visual FoxPro | Y | Y | Y | Y | Y | Y | Y | Y | N | Y |
|
|
332
|
-
| 31 | Visual FoxPro with AutoIncrement | Y | Y | Y | Y | Y | Y | Y | Y | N | Y |
|
|
333
|
-
| 32 | Visual FoxPro with field type Varchar or Varbinary | Y | Y | Y | Y | Y | Y | Y | Y | N | Y |
|
|
339
|
+
| 30 | Visual FoxPro | Y | Y | Y | Y | Y | Y | Y | Y | N | Y | Y | Y | N | N | N | N | - |
|
|
340
|
+
| 31 | Visual FoxPro with AutoIncrement | Y | Y | Y | Y | Y | Y | Y | Y | N | Y | Y | Y | N | N | N | N | N |
|
|
341
|
+
| 32 | Visual FoxPro with field type Varchar or Varbinary | Y | Y | Y | Y | Y | Y | Y | Y | N | Y | Y | Y | N | N | N | N | N |
|
|
334
342
|
| 7b | dBase IV with memo file | Y | Y | Y | Y | Y | Y | - | - | - | - | - | - | - | - | - | - | - |
|
|
335
343
|
| 83 | dBase III with memo file | Y | Y | Y | Y | Y | - | - | - | - | - | - | - | - | - | - | - | - |
|
|
336
344
|
| 87 | Visual Objects 1.x with memo file | Y | Y | Y | Y | Y | - | - | - | - | - | - | - | - | - | - | - | - |
|
|
337
345
|
| 8b | dBase IV with memo file | Y | Y | Y | Y | Y | - | - | - | - | - | - | - | - | N | - | - | - |
|
|
338
346
|
| 8e | dBase IV with SQL table | Y | Y | Y | Y | Y | - | - | - | - | - | - | - | - | N | - | - | - |
|
|
339
|
-
| f5 | FoxPro with memo file | Y | Y | Y | Y | Y | Y | Y | Y | N | Y |
|
|
340
|
-
| fb | FoxPro without memo file | Y | Y | Y | Y | - | Y | Y | Y | N | Y |
|
|
347
|
+
| f5 | FoxPro with memo file | Y | Y | Y | Y | Y | Y | Y | Y | N | Y | Y | Y | N | N | N | N | N |
|
|
348
|
+
| fb | FoxPro without memo file | Y | Y | Y | Y | - | Y | Y | Y | N | Y | Y | Y | N | N | N | N | N |
|
|
341
349
|
|
|
342
350
|
Data type descriptions
|
|
343
351
|
|
data/bin/dbf
CHANGED
|
@@ -1,60 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
Signal.trap('PIPE', 'SYSTEM_DEFAULT')
|
|
4
5
|
|
|
5
6
|
require 'dbf'
|
|
6
|
-
require 'dbf/version'
|
|
7
|
-
require 'optparse'
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
if params['v']
|
|
12
|
-
puts "dbf version: #{DBF::VERSION}"
|
|
13
|
-
|
|
14
|
-
elsif params['h']
|
|
15
|
-
puts "usage: #{File.basename(__FILE__)} [-h|-s|-a|-c|-r] filename"
|
|
16
|
-
puts ' -h = print this message'
|
|
17
|
-
puts ' -v = print the DBF gem version'
|
|
18
|
-
puts ' -s = print summary information'
|
|
19
|
-
puts ' -a = create an ActiveRecord::Schema'
|
|
20
|
-
puts ' -r = create a Sequel migration'
|
|
21
|
-
puts ' -c = export as CSV'
|
|
22
|
-
else
|
|
23
|
-
|
|
24
|
-
filename = ARGV.shift
|
|
25
|
-
abort 'You must supply a filename on the command line' unless filename
|
|
26
|
-
|
|
27
|
-
# create an ActiveRecord::Schema
|
|
28
|
-
if params['a']
|
|
29
|
-
table = DBF::Table.new filename
|
|
30
|
-
puts table.schema(:activerecord)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# create an Sequel::Migration
|
|
34
|
-
if params['r']
|
|
35
|
-
table = DBF::Table.new filename
|
|
36
|
-
puts table.schema(:sequel)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
if params['s']
|
|
40
|
-
table = DBF::Table.new filename
|
|
41
|
-
puts
|
|
42
|
-
puts "Database: #{filename}"
|
|
43
|
-
puts "Type: (#{table.version}) #{table.version_description}"
|
|
44
|
-
puts "Encoding: #{table.header_encoding}" if table.header_encoding
|
|
45
|
-
puts "Memo File: #{table.has_memo_file? ? 'true' : 'false'}"
|
|
46
|
-
puts "Records: #{table.record_count}"
|
|
47
|
-
|
|
48
|
-
puts "\nFields:"
|
|
49
|
-
puts 'Name Type Length Decimal'
|
|
50
|
-
puts '-' * 78
|
|
51
|
-
table.columns.each do |f|
|
|
52
|
-
puts format('%-16s %-10s %-10s %-10s', f.name, f.type, f.length, f.decimal)
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
if params['c']
|
|
57
|
-
table = DBF::Table.new filename
|
|
58
|
-
table.to_csv
|
|
59
|
-
end
|
|
60
|
-
end
|
|
8
|
+
exit DBF::CLI.run(ARGV)
|
data/lib/dbf/cli.rb
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'optparse'
|
|
4
|
+
|
|
5
|
+
module DBF
|
|
6
|
+
class CLI
|
|
7
|
+
USAGE = <<~HELP
|
|
8
|
+
usage: dbf [-h|-s|-a|-c|-r] filename
|
|
9
|
+
-h = print this message
|
|
10
|
+
-v = print the DBF gem version
|
|
11
|
+
-s = print summary information
|
|
12
|
+
-a = create an ActiveRecord::Schema
|
|
13
|
+
-r = create a Sequel migration
|
|
14
|
+
-c = export as CSV
|
|
15
|
+
HELP
|
|
16
|
+
|
|
17
|
+
def self.run(argv, stdout: $stdout, stderr: $stderr)
|
|
18
|
+
new(argv, stdout: stdout, stderr: stderr).run
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize(argv, stdout: $stdout, stderr: $stderr)
|
|
22
|
+
@argv = argv.dup
|
|
23
|
+
@stdout = stdout
|
|
24
|
+
@stderr = stderr
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def run
|
|
28
|
+
params = OptionParser.new.getopts(@argv, 'h', 's', 'a', 'c', 'r', 'v')
|
|
29
|
+
|
|
30
|
+
if params['v']
|
|
31
|
+
print_version
|
|
32
|
+
elsif params['h']
|
|
33
|
+
print_help
|
|
34
|
+
else
|
|
35
|
+
filename = @argv.shift
|
|
36
|
+
return missing_filename unless filename
|
|
37
|
+
|
|
38
|
+
action = %w[a r s c].find { |flag| params[flag] }
|
|
39
|
+
case action
|
|
40
|
+
when 'a' then print_ar_schema(filename)
|
|
41
|
+
when 'r' then print_sequel_schema(filename)
|
|
42
|
+
when 's' then print_summary(filename)
|
|
43
|
+
when 'c' then print_csv(filename)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
0
|
|
47
|
+
rescue DBF::FileNotFoundError => e
|
|
48
|
+
@stderr.puts "DBF::FileNotFoundError: #{e.message}"
|
|
49
|
+
1
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def print_version
|
|
55
|
+
@stdout.puts "dbf version: #{DBF::VERSION}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def print_help
|
|
59
|
+
@stdout.puts USAGE
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def missing_filename
|
|
63
|
+
@stderr.puts 'You must supply a filename on the command line'
|
|
64
|
+
1
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def print_ar_schema(filename)
|
|
68
|
+
@stdout.puts DBF::Table.new(filename).schema(:activerecord)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def print_sequel_schema(filename)
|
|
72
|
+
@stdout.puts DBF::Table.new(filename).schema(:sequel)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def print_summary(filename)
|
|
76
|
+
table = DBF::Table.new(filename)
|
|
77
|
+
@stdout.puts
|
|
78
|
+
@stdout.puts "Database: #{filename}"
|
|
79
|
+
@stdout.puts "Type: (#{table.version}) #{table.version_description}"
|
|
80
|
+
@stdout.puts "Encoding: #{table.header_encoding}" if table.header_encoding
|
|
81
|
+
@stdout.puts "Memo File: #{table.has_memo_file? ? 'true' : 'false'}"
|
|
82
|
+
@stdout.puts "Records: #{table.record_count}"
|
|
83
|
+
@stdout.puts "\nFields:"
|
|
84
|
+
@stdout.puts 'Name Type Length Decimal'
|
|
85
|
+
@stdout.puts '-' * 78
|
|
86
|
+
table.columns.each do |f|
|
|
87
|
+
@stdout.puts format('%-16s %-10s %-10s %-10s', f.name, f.type, f.length, f.decimal)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def print_csv(filename)
|
|
92
|
+
DBF::Table.new(filename).to_csv(@stdout)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
data/lib/dbf/column.rb
CHANGED
|
@@ -58,8 +58,8 @@ module DBF
|
|
|
58
58
|
# @param raw [String]
|
|
59
59
|
# @yield [raw] for memo column resolution
|
|
60
60
|
# @return decoded value
|
|
61
|
-
def decode(raw, &
|
|
62
|
-
type_cast_class.decode(raw, &
|
|
61
|
+
def decode(raw, &)
|
|
62
|
+
type_cast_class.decode(raw, &)
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
# Returns a Hash with :name, :type, :length, and :decimal keys
|
data/lib/dbf/column_type.rb
CHANGED
|
@@ -5,8 +5,7 @@ module DBF
|
|
|
5
5
|
class Base
|
|
6
6
|
attr_reader :decimal, :encoding
|
|
7
7
|
|
|
8
|
-
# @param
|
|
9
|
-
# @param encoding [String, Encoding]
|
|
8
|
+
# @param column [DBF::Column]
|
|
10
9
|
def initialize(column)
|
|
11
10
|
@decimal = column.decimal
|
|
12
11
|
@encoding = column.encoding
|
|
@@ -20,7 +19,7 @@ module DBF
|
|
|
20
19
|
false
|
|
21
20
|
end
|
|
22
21
|
|
|
23
|
-
def decode(raw, &
|
|
22
|
+
def decode(raw, &)
|
|
24
23
|
if skip_blank? && raw.count(' ') == raw.length
|
|
25
24
|
blank_value
|
|
26
25
|
else
|
|
@@ -71,8 +70,12 @@ module DBF
|
|
|
71
70
|
end
|
|
72
71
|
|
|
73
72
|
class Float < Base
|
|
73
|
+
def skip_blank? = true
|
|
74
|
+
|
|
74
75
|
# @param value [String]
|
|
75
76
|
def type_cast(value)
|
|
77
|
+
return nil if value.empty?
|
|
78
|
+
|
|
76
79
|
value.to_f
|
|
77
80
|
end
|
|
78
81
|
end
|
|
@@ -111,6 +114,8 @@ module DBF
|
|
|
111
114
|
# @param value [String]
|
|
112
115
|
def type_cast(value)
|
|
113
116
|
days, msecs = value.unpack('l2')
|
|
117
|
+
return nil if days.nil? || days.zero?
|
|
118
|
+
|
|
114
119
|
secs = (msecs / 1000).to_i
|
|
115
120
|
::DateTime.jd(days, (secs / 3600).to_i, (secs / 60).to_i % 60, secs % 60).to_time
|
|
116
121
|
rescue StandardError
|
|
@@ -119,8 +124,8 @@ module DBF
|
|
|
119
124
|
end
|
|
120
125
|
|
|
121
126
|
class Memo < Base
|
|
122
|
-
def decode(raw, &
|
|
123
|
-
memo_content =
|
|
127
|
+
def decode(raw, &)
|
|
128
|
+
memo_content = yield(raw)
|
|
124
129
|
memo_content ? type_cast(memo_content) : nil
|
|
125
130
|
end
|
|
126
131
|
|
|
@@ -128,7 +133,7 @@ module DBF
|
|
|
128
133
|
def type_cast(value)
|
|
129
134
|
return value unless encoding && value
|
|
130
135
|
|
|
131
|
-
|
|
136
|
+
Encoder.encode(value.dup, encoding)
|
|
132
137
|
end
|
|
133
138
|
end
|
|
134
139
|
|
|
@@ -158,8 +163,9 @@ module DBF
|
|
|
158
163
|
private
|
|
159
164
|
|
|
160
165
|
def encode(value)
|
|
161
|
-
value.force_encoding(encoding)
|
|
162
|
-
|
|
166
|
+
return value.force_encoding(encoding) unless @needs_encode
|
|
167
|
+
|
|
168
|
+
Encoder.encode(value, encoding, @target_encoding)
|
|
163
169
|
end
|
|
164
170
|
end
|
|
165
171
|
end
|
data/lib/dbf/encoder.rb
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DBF
|
|
4
|
+
# Transcodes strings from a source encoding to a target encoding.
|
|
5
|
+
#
|
|
6
|
+
# Most DBF code pages map to encodings built into Ruby and are converted
|
|
7
|
+
# with String#encode. A few (Mazovia cp620, Kamenický cp895) have no Ruby
|
|
8
|
+
# equivalent and are decoded through vendored byte-to-Unicode tables.
|
|
9
|
+
module Encoder
|
|
10
|
+
# Byte-to-Unicode tables generated from
|
|
11
|
+
# https://github.com/SheetJS/js-codepage/blob/master/codepages/620.TBL and
|
|
12
|
+
# https://github.com/SheetJS/js-codepage/blob/master/codepages/895.TBL
|
|
13
|
+
|
|
14
|
+
# Mazovia (Polish) MS-DOS
|
|
15
|
+
CP620_TO_UNICODE = [
|
|
16
|
+
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
|
|
17
|
+
0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
|
18
|
+
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
|
|
19
|
+
0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
|
|
20
|
+
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
|
|
21
|
+
0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
|
|
22
|
+
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
|
23
|
+
0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
|
|
24
|
+
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
|
25
|
+
0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
|
|
26
|
+
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
|
|
27
|
+
0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
|
|
28
|
+
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
|
29
|
+
0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
|
|
30
|
+
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
|
|
31
|
+
0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
|
|
32
|
+
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x0105, 0x00E7,
|
|
33
|
+
0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0107, 0x00C4, 0x0104,
|
|
34
|
+
0x0118, 0x0119, 0x0142, 0x00F4, 0x00F6, 0x0106, 0x00FB, 0x00F9,
|
|
35
|
+
0x015A, 0x00D6, 0x00DC, 0x00A2, 0x0141, 0x00A5, 0x015B, 0x0192,
|
|
36
|
+
0x0179, 0x017B, 0x00F3, 0x00D3, 0x0144, 0x0143, 0x017A, 0x017C,
|
|
37
|
+
0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
|
38
|
+
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
|
39
|
+
0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
|
40
|
+
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
|
|
41
|
+
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
|
42
|
+
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
|
|
43
|
+
0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
|
44
|
+
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
|
|
45
|
+
0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
|
|
46
|
+
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
|
|
47
|
+
0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
|
48
|
+
].freeze
|
|
49
|
+
|
|
50
|
+
# Kamenický (Czech) MS-DOS
|
|
51
|
+
CP895_TO_UNICODE = [
|
|
52
|
+
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
|
|
53
|
+
0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
|
54
|
+
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
|
|
55
|
+
0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
|
|
56
|
+
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
|
|
57
|
+
0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
|
|
58
|
+
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
|
59
|
+
0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
|
|
60
|
+
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
|
61
|
+
0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
|
|
62
|
+
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
|
|
63
|
+
0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
|
|
64
|
+
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
|
65
|
+
0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
|
|
66
|
+
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
|
|
67
|
+
0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
|
|
68
|
+
0x010C, 0x00FC, 0x00E9, 0x010F, 0x00E4, 0x010E, 0x0164, 0x010D,
|
|
69
|
+
0x011B, 0x011A, 0x0139, 0x00CD, 0x013E, 0x01EA, 0x00C4, 0x00C1,
|
|
70
|
+
0x00C9, 0x017E, 0x017D, 0x00F4, 0x00F6, 0x00D3, 0x016F, 0x00DA,
|
|
71
|
+
0x00FD, 0x00D6, 0x00DC, 0x0160, 0x013D, 0x00DD, 0x0158, 0x0165,
|
|
72
|
+
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0148, 0x0147, 0x016E, 0x00D4,
|
|
73
|
+
0x0161, 0x0159, 0x0155, 0x0154, 0x00BC, 0x00A7, 0x00AB, 0x00BB,
|
|
74
|
+
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
|
75
|
+
0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
|
76
|
+
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
|
|
77
|
+
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
|
78
|
+
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
|
|
79
|
+
0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
|
80
|
+
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
|
|
81
|
+
0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
|
|
82
|
+
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
|
|
83
|
+
0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
|
84
|
+
].freeze
|
|
85
|
+
|
|
86
|
+
CUSTOM_TABLES = {
|
|
87
|
+
'cp620' => CP620_TO_UNICODE,
|
|
88
|
+
'cp895' => CP895_TO_UNICODE
|
|
89
|
+
}.freeze
|
|
90
|
+
|
|
91
|
+
# Is the encoding a code page without a Ruby Encoding, decoded through
|
|
92
|
+
# a vendored table?
|
|
93
|
+
#
|
|
94
|
+
# @param encoding [String, Encoding, nil]
|
|
95
|
+
# @return [Boolean]
|
|
96
|
+
def self.custom?(encoding)
|
|
97
|
+
encoding.is_a?(::String) && CUSTOM_TABLES.key?(encoding.downcase)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Transcode a string from source_encoding to target_encoding
|
|
101
|
+
#
|
|
102
|
+
# @param string [String]
|
|
103
|
+
# @param source_encoding [String, Encoding]
|
|
104
|
+
# @param target_encoding [Encoding]
|
|
105
|
+
# @return [String]
|
|
106
|
+
def self.encode(string, source_encoding, target_encoding = Encoding.default_external)
|
|
107
|
+
table = source_encoding.is_a?(::String) && CUSTOM_TABLES[source_encoding.downcase]
|
|
108
|
+
if table
|
|
109
|
+
decoded = string.each_byte.map { |byte| table[byte] }.pack('U*')
|
|
110
|
+
target_encoding == Encoding::UTF_8 ? decoded : decoded.encode(target_encoding, undef: :replace, invalid: :replace)
|
|
111
|
+
else
|
|
112
|
+
string.force_encoding(source_encoding).encode(target_encoding, undef: :replace, invalid: :replace)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
data/lib/dbf/encodings.rb
CHANGED
|
@@ -5,6 +5,7 @@ module DBF
|
|
|
5
5
|
'01' => 'cp437', # U.S. MS-DOS
|
|
6
6
|
'02' => 'cp850', # International MS-DOS
|
|
7
7
|
'03' => 'cp1252', # Windows ANSI
|
|
8
|
+
'04' => 'macRoman', # Standard Macintosh
|
|
8
9
|
'08' => 'cp865', # Danish OEM
|
|
9
10
|
'09' => 'cp437', # Dutch OEM
|
|
10
11
|
'0a' => 'cp850', # Dutch OEM*
|
|
@@ -45,6 +46,8 @@ module DBF
|
|
|
45
46
|
'65' => 'cp866', # Russian MS-DOS
|
|
46
47
|
'66' => 'cp865', # Nordic MS-DOS
|
|
47
48
|
'67' => 'cp861', # Icelandic MS-DOS
|
|
49
|
+
'68' => 'cp895', # Kamenicky (Czech) MS-DOS
|
|
50
|
+
'69' => 'cp620', # Mazovia (Polish) MS-DOS
|
|
48
51
|
'6a' => 'cp737', # Greek MS-DOS (437G)
|
|
49
52
|
'6b' => 'cp857', # Turkish MS-DOS
|
|
50
53
|
'6c' => 'cp863', # French-Canadian MS-DOS
|
|
@@ -53,9 +56,14 @@ module DBF
|
|
|
53
56
|
'7a' => 'cp936', # PRC GBK
|
|
54
57
|
'7b' => 'cp932', # Japanese Shift-JIS
|
|
55
58
|
'7c' => 'cp874', # Thai Windows/MS-DOS
|
|
59
|
+
'7d' => 'cp1255', # Hebrew Windows
|
|
60
|
+
'7e' => 'cp1256', # Arabic Windows
|
|
56
61
|
'86' => 'cp737', # Greek OEM
|
|
57
62
|
'87' => 'cp852', # Slovenian OEM
|
|
58
63
|
'88' => 'cp857', # Turkish OEM
|
|
64
|
+
'96' => 'macCyrillic', # Russian Macintosh
|
|
65
|
+
'97' => 'macCentEuro', # Macintosh EE
|
|
66
|
+
'98' => 'macGreek', # Greek Macintosh
|
|
59
67
|
'c8' => 'cp1250', # Eastern European Windows
|
|
60
68
|
'c9' => 'cp1251', # Russian Windows
|
|
61
69
|
'ca' => 'cp1254', # Turkish Windows
|
data/lib/dbf/table.rb
CHANGED
|
@@ -140,9 +140,14 @@ module DBF
|
|
|
140
140
|
# Dumps all records to a CSV file. If no filename is given then CSV is
|
|
141
141
|
# output to STDOUT.
|
|
142
142
|
#
|
|
143
|
-
# @param [optional String] path
|
|
144
|
-
def to_csv(
|
|
145
|
-
|
|
143
|
+
# @param [optional String, IO] path_or_io String path, IO-like object, or nil for STDOUT
|
|
144
|
+
def to_csv(path_or_io = nil)
|
|
145
|
+
io = case path_or_io
|
|
146
|
+
when nil then $stdout
|
|
147
|
+
when String then File.open(path_or_io, 'w')
|
|
148
|
+
else path_or_io
|
|
149
|
+
end
|
|
150
|
+
csv = CSV.new(io, force_quotes: true)
|
|
146
151
|
csv << column_names
|
|
147
152
|
each { |record| csv << record.to_a }
|
|
148
153
|
end
|
|
@@ -159,7 +164,7 @@ module DBF
|
|
|
159
164
|
# @param [String] string
|
|
160
165
|
# @return [String]
|
|
161
166
|
def encode_string(string) # :nodoc:
|
|
162
|
-
|
|
167
|
+
Encoder.encode(string, @encoding)
|
|
163
168
|
end
|
|
164
169
|
|
|
165
170
|
# Encoding specified in the file header
|
data/lib/dbf/version.rb
CHANGED
data/lib/dbf.rb
CHANGED
|
@@ -14,6 +14,7 @@ require 'dbf/record_context'
|
|
|
14
14
|
require 'dbf/column_type'
|
|
15
15
|
require 'dbf/column'
|
|
16
16
|
require 'dbf/encodings'
|
|
17
|
+
require 'dbf/encoder'
|
|
17
18
|
require 'dbf/header'
|
|
18
19
|
require 'dbf/version_config'
|
|
19
20
|
require 'dbf/file_handler'
|
|
@@ -25,3 +26,4 @@ require 'dbf/memo/dbase3'
|
|
|
25
26
|
require 'dbf/memo/dbase4'
|
|
26
27
|
require 'dbf/memo/foxpro'
|
|
27
28
|
require 'dbf/database/foxpro'
|
|
29
|
+
require 'dbf/cli'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dbf
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.
|
|
4
|
+
version: 5.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Keith Morrison
|
|
@@ -37,10 +37,12 @@ files:
|
|
|
37
37
|
- bin/dbf
|
|
38
38
|
- dbf.gemspec
|
|
39
39
|
- lib/dbf.rb
|
|
40
|
+
- lib/dbf/cli.rb
|
|
40
41
|
- lib/dbf/column.rb
|
|
41
42
|
- lib/dbf/column_builder.rb
|
|
42
43
|
- lib/dbf/column_type.rb
|
|
43
44
|
- lib/dbf/database/foxpro.rb
|
|
45
|
+
- lib/dbf/encoder.rb
|
|
44
46
|
- lib/dbf/encodings.rb
|
|
45
47
|
- lib/dbf/file_handler.rb
|
|
46
48
|
- lib/dbf/find.rb
|
|
@@ -77,7 +79,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
77
79
|
- !ruby/object:Gem::Version
|
|
78
80
|
version: '0'
|
|
79
81
|
requirements: []
|
|
80
|
-
rubygems_version:
|
|
82
|
+
rubygems_version: 4.0.10
|
|
81
83
|
specification_version: 4
|
|
82
84
|
summary: Read xBase files
|
|
83
85
|
test_files: []
|