func (dt *DbfTable) FieldValueByName(row int, fieldName string) (value string, err error) {
	if fieldIndex, entryFound := dt.fieldMap[fieldName]; entryFound {
		return dt.FieldValue(row, fieldIndex), err
	}
	err = errors.New("Field name \"" + fieldName + "\" does not exist")
	return
}
FieldValueByName returns the value of a field given row number and name provided
package godbf
import (
	"errors"
	"strconv"
	"strings"
	"time"
	"github.com/axgle/mahonia"
)
const (
	null  = 0x00
	blank = 0x20
)
type DbfTable struct {
	fileSignature         uint8  
	updateYear            uint8  
	updateMonth           uint8
	updateDay             uint8
	numberOfRecords       uint32    
	numberOfBytesInHeader uint16    
	lengthOfEachRecord    uint16    
	reservedBytes         [20]byte  
	fieldDescriptor       [32]byte  
	fieldTerminator       int8      
	numberOfFields int  
	fields []FieldDescriptor
	fieldMap map[string]int
	dataEntryStarted bool
	createdFromScratch bool
	fileEncoding string
	decoder      mahonia.Decoder
	encoder      mahonia.Encoder
	dataStore []byte
}
func New(encoding string) (table *DbfTable) {
	dt := new(DbfTable)
	dt.fileEncoding = encoding
	dt.encoder = mahonia.NewEncoder(encoding)
	dt.decoder = mahonia.NewDecoder(encoding)
	dt.createdFromScratch = true
	dt.fileSignature = 0x03
	dt.updateYear = byte(time.Now().Year() - 1900)
	dt.updateMonth = byte(time.Now().Month())
	dt.updateDay = byte(time.Now().Day())
	dt.numberOfRecords = 0
	dt.numberOfBytesInHeader = 32
	dt.lengthOfEachRecord = 0
	dt.fieldMap = make(map[string]int)
	dt.numberOfFields = int((dt.numberOfBytesInHeader - 1 - 32) / 32)
	s := make([]byte, dt.numberOfBytesInHeader)
	dt.dataEntryStarted = false
	dt.dataStore = s
	dt.dataStore[0] = dt.fileSignature
	dt.dataStore[1] = dt.updateYear
	dt.dataStore[2] = dt.updateMonth
	dt.dataStore[3] = dt.updateDay
	dt.dataStore[28] = 0x00
	if code, ok := encodingTable[lookup[encoding]]; ok {
		dt.dataStore[29] = code
	} else {
		dt.dataStore[29] = 0x57  
	}
	return dt
}
func (dt *DbfTable) AddBooleanField(fieldName string) (err error) {
	return dt.addField(fieldName, Logical, Logical.fixedFieldLength(), Logical.decimalCountNotApplicable())
}
func (dt *DbfTable) AddDateField(fieldName string) (err error) {
	return dt.addField(fieldName, Date, Date.fixedFieldLength(), Date.decimalCountNotApplicable())
}
func (dt *DbfTable) AddTextField(fieldName string, length byte) (err error) {
	return dt.addField(fieldName, Character, length, Character.decimalCountNotApplicable())
}
func (dt *DbfTable) AddNumberField(fieldName string, length byte, decimalPlaces uint8) (err error) {
	return dt.addField(fieldName, Numeric, length, decimalPlaces)
}
func (dt *DbfTable) AddFloatField(fieldName string, length byte, decimalPlaces uint8) (err error) {
	return dt.addField(fieldName, Float, length, decimalPlaces)
}
func (dt *DbfTable) addField(fieldName string, fieldType DbaseDataType, length byte, decimalPlaces uint8) (err error) {
	if dt.dataEntryStarted {
		return errors.New("Once you start entering data to the dbase table or open an existing dbase file, altering dbase table schema is not allowed!")
	}
	normalizedFieldName := dt.normaliseFieldName(fieldName)
	if dt.HasField(normalizedFieldName) {
		return errors.New("Field name \"" + normalizedFieldName + "\" already exists!")
	}
	df := new(FieldDescriptor)
	df.name = normalizedFieldName
	df.fieldType = fieldType
	df.length = length
	df.decimalPlaces = decimalPlaces
	slice := dt.convertToByteSlice(df.name, 10)
	for i := 0; i < len(slice); i++ {
		df.fieldStore[i] = slice[i]
	}
	df.fieldStore[10] = 0x00
	df.fieldStore[11] = df.fieldType.byte()
	df.fieldStore[16] = df.length
	df.fieldStore[17] = df.decimalPlaces
	dt.fields = append(dt.fields, *df)
	if dt.createdFromScratch {
		dt.updateHeader()
	}
	return
}
func (dt *DbfTable) normaliseFieldName(name string) (s string) {
	e := mahonia.NewEncoder(dt.fileEncoding)
	b := []byte(e.ConvertString(name))
	if len(b) > 10 {
		b = b[0:10]
	}
	d := mahonia.NewDecoder(dt.fileEncoding)
	s = d.ConvertString(string(b))
	return
}
func (dt *DbfTable) convertToByteSlice(value string, numberOfBytes int) (s []byte) {
	e := mahonia.NewEncoder(dt.fileEncoding)
	b := []byte(e.ConvertString(value))
	if len(b) <= numberOfBytes {
		s = b
	} else {
		s = b[0:numberOfBytes]
	}
	return
}
func (dt *DbfTable) updateHeader() {
	slice := dt.dataStore[0:32]
	slice[0] = 0x03
	var lengthOfEachRecord uint16 = 0
	for i := range dt.Fields() {
		lengthOfEachRecord += uint16(dt.Fields()[i].length)
		slice = appendSlice(slice, dt.Fields()[i].fieldStore[:])
		dt.fieldMap[dt.Fields()[i].name] = i
	}
	slice = appendSlice(slice, []byte{0x0D})
	dt.dataStore = slice
	dt.numberOfBytesInHeader = uint16(len(slice))
	s := uint32ToBytes(uint32(dt.numberOfBytesInHeader))
	dt.dataStore[8] = s[0]
	dt.dataStore[9] = s[1]
	dt.lengthOfEachRecord = lengthOfEachRecord + 1  
	s = uint32ToBytes(uint32(dt.lengthOfEachRecord))
	dt.dataStore[10] = s[0]
	dt.dataStore[11] = s[1]
	return
}
func (dt *DbfTable) Fields() []FieldDescriptor {
	return dt.fields
}
func (dt *DbfTable) FieldNames() []string {
	names := make([]string, 0)
	for _, field := range dt.Fields() {
		names = append(names, field.name)
	}
	return names
}
func (dt *DbfTable) HasField(fieldName string) bool {
	for i := 0; i < len(dt.fields); i++ {
		if dt.fields[i].name == fieldName {
			return true
		}
	}
	return false
}
func (dt *DbfTable) DecimalPlacesInField(fieldName string) (uint8, error) {
	if !dt.HasField(fieldName) {
		return 0, errors.New("Field name \"" + fieldName + "\" does not exist. ")
	}
	for i := 0; i < len(dt.fields); i++ {
		if dt.fields[i].name == fieldName && dt.fields[i].usesDecimalPlaces() {
			return dt.fields[i].decimalPlaces, nil
		}
	}
	return 0, errors.New("Type of field \"" + fieldName + "\" is not Numeric or Float.")
}
func (dt *DbfTable) AddNewRecord() (newRecordNumber int) {
	if dt.dataEntryStarted == false {
		dt.dataEntryStarted = true
	}
	newRecord := make([]byte, dt.lengthOfEachRecord)
	dt.dataStore = appendSlice(dt.dataStore, newRecord)
	newRecordNumber = int(dt.numberOfRecords)
	dt.numberOfRecords++
	s := uint32ToBytes(dt.numberOfRecords)
	dt.dataStore[4] = s[0]
	dt.dataStore[5] = s[1]
	dt.dataStore[6] = s[2]
	dt.dataStore[7] = s[3]
	return newRecordNumber
}
func (dt *DbfTable) NumberOfRecords() int {
	return int(dt.numberOfRecords)
}
func (dt *DbfTable) SetFieldValueByName(row int, fieldName string, value string) (err error) {
	if fieldIndex, found := dt.fieldMap[fieldName]; found {
		return dt.SetFieldValue(row, fieldIndex, value)
	}
	return errors.New("Field name \"" + fieldName + "\" does not exist")
}
func (dt *DbfTable) SetFieldValue(row int, fieldIndex int, value string) (err error) {
	b := []byte(dt.encoder.ConvertString(value))
	fieldLength := int(dt.fields[fieldIndex].length)
	offset := int(dt.numberOfBytesInHeader)
	lengthOfRecord := int(dt.lengthOfEachRecord)
	offset = offset + (row * lengthOfRecord)
	recordOffset := 1
	for i := 0; i < len(dt.fields); i++ {
		if i == fieldIndex {
			break
		} else {
			recordOffset += int(dt.fields[i].length)
		}
	}
	dt.fillFieldWithBlanks(fieldLength, offset, recordOffset)
	switch dt.fields[fieldIndex].fieldType {
	case Character, Logical, Date:
		for i := 0; i < len(b) && i < fieldLength; i++ {
			dt.dataStore[offset+recordOffset+i] = b[i]
		}
	case Float, Numeric:
		for i := 0; i < fieldLength; i++ {
			if i < len(b) {
				dt.dataStore[offset+recordOffset+(fieldLength-i-1)] = b[(len(b)-1)-i]
			} else {
				break
			}
		}
	}
	return
}
func (dt *DbfTable) fillFieldWithBlanks(fieldLength int, offset int, recordOffset int) {
	for i := 0; i < fieldLength; i++ {
		dt.dataStore[offset+recordOffset+i] = blank
	}
}
func (dt *DbfTable) FieldValue(row int, fieldIndex int) (value string) {
	offset := int(dt.numberOfBytesInHeader)
	lengthOfRecord := int(dt.lengthOfEachRecord)
	offset = offset + (row * lengthOfRecord)
	recordOffset := 1
	for i := 0; i < len(dt.fields); i++ {
		if i == fieldIndex {
			break
		} else {
			recordOffset += int(dt.fields[i].length)
		}
	}
	temp := dt.dataStore[(offset + recordOffset):((offset + recordOffset) + int(dt.fields[fieldIndex].length))]
	enforceBlankPadding(temp)
	s := dt.decoder.ConvertString(string(temp))
	value = strings.TrimSpace(s)
	return
}
func enforceBlankPadding(temp []byte) {
	for i := 0; i < len(temp); i++ {
		if temp[i] == null {
			temp[i] = blank
		}
	}
}
func (dt *DbfTable) Float64FieldValueByName(row int, fieldName string) (value float64, err error) {
	valueAsString, err := dt.FieldValueByName(row, fieldName)
	return strconv.ParseFloat(valueAsString, 64)
}
func (dt *DbfTable) Int64FieldValueByName(row int, fieldName string) (value int64, err error) {
	valueAsString, err := dt.FieldValueByName(row, fieldName)
	return strconv.ParseInt(valueAsString, 0, 64)
}