Class: DBF::Table

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/dbf/table.rb,
lib/dbf/table/encodings.rb

Overview

DBF::Table is the primary interface to a single DBF file and provides methods for enumerating and searching the records.

Constant Summary

DBF_HEADER_SIZE =
32
VERSIONS =
{
  "02" => "FoxBase",
  "03" => "dBase III without memo file",
  "04" => "dBase IV without memo file",
  "05" => "dBase V without memo file",
  "07" => "Visual Objects 1.x",
  "30" => "Visual FoxPro",
  "31" => "Visual FoxPro with AutoIncrement field",
  "43" => "dBASE IV SQL table files, no memo",
  "63" => "dBASE IV SQL system files, no memo",
  "7b" => "dBase IV with memo file",
  "83" => "dBase III with memo file",
  "87" => "Visual Objects 1.x with memo file",
  "8b" => "dBase IV with memo file",
  "8e" => "dBase IV with SQL table",
  "cb" => "dBASE IV SQL table files, with memo",
  "f5" => "FoxPro with memo file",
  "fb" => "FoxPro without memo file"
}
FOXPRO_VERSIONS =
{
  "30" => "Visual FoxPro",
  "31" => "Visual FoxPro with AutoIncrement field",
  "f5" => "FoxPro with memo file",
  "fb" => "FoxPro without memo file"
}
ENCODINGS =
{
  "01" => "cp437",       # U.S. MS–DOS
  "02" => "cp850",       # International MS–DOS
  "03" => "cp1252",      # Windows ANSI
  "08" => "cp865",       # Danish OEM
  "09" => "cp437",       # Dutch OEM
  "0a" => "cp850",       # Dutch OEM*
  "0b" => "cp437",       # Finnish OEM
  "0d" => "cp437",       # French OEM
  "0e" => "cp850",       # French OEM*
  "0f" => "cp437",       # German OEM
  "10" => "cp850",       # German OEM*
  "11" => "cp437",       # Italian OEM
  "12" => "cp850",       # Italian OEM*
  "13" => "cp932",       # Japanese Shift-JIS
  "14" => "cp850",       # Spanish OEM*
  "15" => "cp437",       # Swedish OEM
  "16" => "cp850",       # Swedish OEM*
  "17" => "cp865",       # Norwegian OEM
  "18" => "cp437",       # Spanish OEM
  "19" => "cp437",       # English OEM (Britain)
  "1a" => "cp850",       # English OEM (Britain)*
  "1b" => "cp437",       # English OEM (U.S.)
  "1c" => "cp863",       # French OEM (Canada)
  "1d" => "cp850",       # French OEM*
  "1f" => "cp852",       # Czech OEM
  "22" => "cp852",       # Hungarian OEM
  "23" => "cp852",       # Polish OEM
  "24" => "cp860",       # Portuguese OEM
  "25" => "cp850",       # Portuguese OEM*
  "26" => "cp866",       # Russian OEM
  "37" => "cp850",       # English OEM (U.S.)*
  "40" => "cp852",       # Romanian OEM
  "4d" => "cp936",       # Chinese GBK (PRC)
  "4e" => "cp949",       # Korean (ANSI/OEM)
  "4f" => "cp950",       # Chinese Big5 (Taiwan)
  "50" => "cp874",       # Thai (ANSI/OEM)
  "57" => "cp1252",      # ANSI
  "58" => "cp1252",      # Western European ANSI
  "59" => "cp1252",      # Spanish ANSI
  "64" => "cp852",       # Eastern European MS–DOS
  "65" => "cp866",       # Russian MS–DOS
  "66" => "cp865",       # Nordic MS–DOS
  "67" => "cp861",       # Icelandic MS–DOS
  "6a" => "cp737",       # Greek MS–DOS (437G)
  "6b" => "cp857",       # Turkish MS–DOS
  "6c" => "cp863",       # French–Canadian MS–DOS
  "78" => "cp950",       # Taiwan Big 5
  "79" => "cp949",       # Hangul (Wansung)
  "7a" => "cp936",       # PRC GBK
  "7b" => "cp932",       # Japanese Shift-JIS
  "7c" => "cp874",       # Thai Windows/MS–DOS
  "86" => "cp737",       # Greek OEM
  "87" => "cp852",       # Slovenian OEM
  "88" => "cp857",       # Turkish OEM
  "c8" => "cp1250",      # Eastern European Windows
  "c9" => "cp1251",      # Russian Windows
  "ca" => "cp1254",      # Turkish Windows
  "cb" => "cp1253",      # Greek Windows
  "cc" => "cp1257",      # Baltic Windows
}

Instance Attribute Summary (collapse)

Instance Method Summary (collapse)

Constructor Details

- (Table) initialize(data, memo = nil, encoding = nil)

Opens a DBF::Table Examples:

# working with a file stored on the filesystem
table = DBF::Table.new 'data.dbf'

# working with a misnamed memo file
table = DBF::Table.new 'data.dbf', 'memo.dbt'

# working with a dbf in memory
table = DBF::Table.new StringIO.new(dbf_data)

# working with a dbf and memo in memory
table = DBF::Table.new StringIO.new(dbf_data), StringIO.new(memo_data)

# working with a dbf overriding specified in the dbf encoding
table = DBF::Table.new 'data.dbf', nil, 'cp437'
table = DBF::Table.new 'data.dbf', 'memo.dbt', Encoding::US_ASCII

Parameters:

  • data (String, StringIO)

    Path to the dbf file or a StringIO object

  • memo (optional String, StringIO) (defaults to: nil)

    Path to the memo file or a StringIO object

  • encoding (optional String, Encoding) (defaults to: nil)

    Name of the encoding or an Encoding object



62
63
64
65
66
67
# File 'lib/dbf/table.rb', line 62

def initialize(data, memo = nil, encoding = nil)
  @data = open_data(data)
  @version, @record_count, @header_length, @record_length, @encoding_key, @encoding = get_header_info
  @encoding = encoding if encoding
  @memo = open_memo(data, memo)
end

Instance Attribute Details

- (Object) encoding

Internal dBase version number Total number of records Source encoding (for ex. :cp1251)



39
40
41
# File 'lib/dbf/table.rb', line 39

def encoding
  @encoding
end

- (Object) record_count (readonly)

Returns the value of attribute record_count



38
39
40
# File 'lib/dbf/table.rb', line 38

def record_count
  @record_count
end

- (Object) version (readonly)

Returns the value of attribute version



37
38
39
# File 'lib/dbf/table.rb', line 37

def version
  @version
end

Instance Method Details

- (TrueClass, FalseClass) close

Closes the table and memo file

Returns:

  • (TrueClass, FalseClass)


77
78
79
80
# File 'lib/dbf/table.rb', line 77

def close
  @data.close
  @memo && @memo.close
end

- (TrueClass, FalseClass) closed?

Returns:

  • (TrueClass, FalseClass)


83
84
85
86
87
88
89
# File 'lib/dbf/table.rb', line 83

def closed?
  if @memo
    @data.closed? && @memo.closed?
  else
    @data.closed?
  end
end

- (Object) columns

Retrieves column information from the database



205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/dbf/table.rb', line 205

def columns
  @columns ||= begin
    @data.seek(DBF_HEADER_SIZE)
    columns = []
    while !["\0", "\r"].include?(first_byte = @data.read(1))
      column_data = first_byte + @data.read(31)
      name, type, length, decimal = column_data.unpack('a10 x a x4 C2')
      if length > 0
        columns << column_class.new(name.strip, type, length, decimal, version, @encoding)
      end
    end
    columns
  end
end

- (Object) each {|nil, DBF::Record| ... }

Calls block once for each record in the table. The record may be nil if the record has been marked as deleted.

Yields:



100
101
102
# File 'lib/dbf/table.rb', line 100

def each
  @record_count.times {|i| yield record(i)}
end

- (Object) filename

String

Returns:

  • String



92
93
94
# File 'lib/dbf/table.rb', line 92

def filename
  File.basename @data.path
end

- (Object) find(command, options = {}) {|optional, DBF::Record, NilClass| ... }

Find records using a simple ActiveRecord-like syntax.

Examples:

table = DBF::Table.new 'mydata.dbf'

# Find record number 5
table.find(5)

# Find all records for Keith Morrison
table.find :all, :first_name => "Keith", :last_name => "Morrison"

# Find first record
table.find :first, :first_name => "Keith"

The command may be a record index, :all, or :first. options is optional and, if specified, should be a hash where the keys correspond to column names in the database. The values will be matched exactly with the value in the database. If you specify more than one key, all values must match in order for the record to be returned. The equivalent SQL would be “WHERE key1 = 'value1' AND key2 = 'value2'”.

Parameters:

  • command (Fixnum, Symbol)
  • options (optional, Hash) (defaults to: {})

    Hash of search parameters

Yields:



191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/dbf/table.rb', line 191

def find(command, options = {}, &block)
  case command
  when Fixnum
    record(command)
  when Array
    command.map {|i| record(i)}
  when :all
    find_all(options, &block)
  when :first
    find_first(options)
  end
end

- (Boolean) foxpro?

Returns:

  • (Boolean)


231
232
233
# File 'lib/dbf/table.rb', line 231

def foxpro?
  FOXPRO_VERSIONS.keys.include? @version
end

- (TrueClass, FalseClass) has_memo_file?

Returns:

  • (TrueClass, FalseClass)


70
71
72
# File 'lib/dbf/table.rb', line 70

def has_memo_file?
  !!@memo
end

- (DBF::Record, NilClass) record(index) Also known as: row

Retrieve a record by index number. The record will be nil if it has been deleted, but not yet pruned from the database.

Parameters:

  • index (Fixnum)

Returns:



110
111
112
113
114
115
# File 'lib/dbf/table.rb', line 110

def record(index)
  seek(index * @record_length)
  if !deleted_record?
    DBF::Record.new(@data.read(@record_length), columns, version, @memo)
  end
end

- (String) schema

Generate an ActiveRecord::Schema

xBase data types are converted to generic types as follows:

  • Number columns with no decimals are converted to :integer

  • Number columns with decimals are converted to :float

  • Date columns are converted to :datetime

  • Logical columns are converted to :boolean

  • Memo columns are converted to :text

  • Character columns are converted to :string and the :limit option is set to the length of the character column

Example:

create_table "mydata" do |t|
  t.column :name, :string, :limit => 30
  t.column :last_update, :datetime
  t.column :is_active, :boolean
  t.column :age, :integer
  t.column :notes, :text
end

Returns:

  • (String)


147
148
149
150
151
152
153
154
155
# File 'lib/dbf/table.rb', line 147

def schema
  s = "ActiveRecord::Schema.define do\n"
  s << "  create_table \"#{File.basename(@data.path, ".*")}\" do |t|\n"
  columns.each do |column|
    s << "    t.column #{column.schema_definition}"
  end
  s << "  end\nend"
  s
end

- (Boolean) supports_encoding?

Returns:

  • (Boolean)


220
221
222
# File 'lib/dbf/table.rb', line 220

def supports_encoding?
  String.new.respond_to?(:encoding)
end

- (Boolean) supports_iconv?

Returns:

  • (Boolean)


224
225
226
227
228
229
# File 'lib/dbf/table.rb', line 224

def supports_iconv?
  require 'iconv'
  true
rescue
  false
end

- (Object) to_csv(path = nil)

Dumps all records to a CSV file. If no filename is given then CSV is output to STDOUT.

Parameters:

  • path (optional String) (defaults to: nil)

    Defaults to STDOUT



161
162
163
164
165
# File 'lib/dbf/table.rb', line 161

def to_csv(path = nil)
  csv = csv_class.new((path ? File.open(path, 'w') : $stdout), :force_quotes => true)
  csv << columns.map {|c| c.name}
  each {|record| csv << record.to_a}
end

- (String) version_description

Human readable version description

Returns:

  • (String)


122
123
124
# File 'lib/dbf/table.rb', line 122

def version_description
  VERSIONS[version]
end