├── DotNetDBF ├── sn.snk ├── Properties │ └── MoreAssemblyInfo.cs ├── original java src │ ├── DBFException.java │ ├── DBFBase.java │ ├── Utils.java │ ├── DBFHeader.java │ ├── DBFField.java │ ├── DBFReader.java │ └── DBFWriter.java ├── DBTHeader.cs ├── DBFBase.cs ├── DBFException.cs ├── DotNetDBF.csproj ├── DBFFieldType.cs ├── Utils.cs ├── MemoValue.cs ├── DBFHeader.cs ├── DBFField.cs ├── DBFReader.cs └── DBFWriter.cs ├── DotNetDBF.Enumerable ├── sn.snk ├── DotNetDBF.Enumerable.csproj ├── Obsolete.cs └── Enumerable.cs ├── .gitignore ├── DotNetDBF.Test ├── dbfs │ ├── cp1251.dbf │ ├── dbase_30.dbf │ ├── dbase_30.fpt │ ├── dbase_31.dbf │ ├── dbase_83.dbf │ ├── dbase_83.dbt │ ├── dbase_8b.dbf │ ├── dbase_8b.dbt │ ├── dbase_f5.dbf │ ├── dbase_f5.fpt │ ├── foxprodb │ │ ├── calls.CDX │ │ ├── calls.dbf │ │ ├── setup.CDX │ │ ├── types.CDX │ │ ├── contacts.CDX │ │ ├── contacts.FPT │ │ ├── contacts.dbf │ │ ├── FOXPRO-DB-TEST.DBC │ │ ├── FOXPRO-DB-TEST.DCT │ │ ├── FOXPRO-DB-TEST.DCX │ │ ├── types.dbf │ │ ├── setup.dbf │ │ └── calls.FPT │ ├── dbase_83_missing_memo.dbf │ ├── dbase_83_missing_memo_record_0.yml │ ├── cp1251_summary.txt │ ├── dbase_8b_summary.txt │ ├── dbase_83_schema.txt │ ├── dbase_83_record_0.yml │ ├── dbase_31_summary.txt │ ├── dbase_83_record_9.yml │ ├── dbase_83_summary.txt │ ├── Readme.md │ ├── dbase_03_summary.txt │ ├── dbase_f5_summary.txt │ ├── dbase_30_summary.txt │ └── dbase_03.dbf ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── Issue19Test.cs ├── DotNetDBF.Test.csproj └── Test.cs ├── Directory.Build.props ├── Directory.Build.targets ├── .github └── workflows │ ├── dotnet.yml │ └── dotnet48.yml ├── DotNetDBF.sln.DotSettings ├── README.md └── DotNetDBF.sln /DotNetDBF/sn.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF/sn.snk -------------------------------------------------------------------------------- /DotNetDBF.Enumerable/sn.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Enumerable/sn.snk -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | obj 3 | /packages 4 | /publish 5 | 6 | *.suo 7 | 8 | *.user 9 | .idea/* 10 | .vs/* 11 | -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/cp1251.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Test/dbfs/cp1251.dbf -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/dbase_30.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Test/dbfs/dbase_30.dbf -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/dbase_30.fpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Test/dbfs/dbase_30.fpt -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/dbase_31.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Test/dbfs/dbase_31.dbf -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/dbase_83.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Test/dbfs/dbase_83.dbf -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/dbase_83.dbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Test/dbfs/dbase_83.dbt -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/dbase_8b.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Test/dbfs/dbase_8b.dbf -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/dbase_8b.dbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Test/dbfs/dbase_8b.dbt -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/dbase_f5.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Test/dbfs/dbase_f5.dbf -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/dbase_f5.fpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Test/dbfs/dbase_f5.fpt -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/foxprodb/calls.CDX: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Test/dbfs/foxprodb/calls.CDX -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/foxprodb/calls.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Test/dbfs/foxprodb/calls.dbf -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/foxprodb/setup.CDX: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Test/dbfs/foxprodb/setup.CDX -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/foxprodb/types.CDX: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Test/dbfs/foxprodb/types.CDX -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/foxprodb/contacts.CDX: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Test/dbfs/foxprodb/contacts.CDX -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/foxprodb/contacts.FPT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Test/dbfs/foxprodb/contacts.FPT -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/foxprodb/contacts.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Test/dbfs/foxprodb/contacts.dbf -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/dbase_83_missing_memo.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Test/dbfs/dbase_83_missing_memo.dbf -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/foxprodb/FOXPRO-DB-TEST.DBC: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Test/dbfs/foxprodb/FOXPRO-DB-TEST.DBC -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/foxprodb/FOXPRO-DB-TEST.DCT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Test/dbfs/foxprodb/FOXPRO-DB-TEST.DCT -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/foxprodb/FOXPRO-DB-TEST.DCX: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekonbenefits/dotnetdbf/HEAD/DotNetDBF.Test/dbfs/foxprodb/FOXPRO-DB-TEST.DCX -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7.0.1 4 | preview$(GITHUB_RUN_NUMBER) 5 | 6 | 7 | -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/dbase_83_missing_memo_record_0.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - 87 3 | - 2 4 | - 0 5 | - 0 6 | - 87 7 | - '1' 8 | - Assorted Petits Fours 9 | - graphics/00000001/t_1.jpg 10 | - graphics/00000001/1.jpg 11 | - 0.0 12 | - 0.0 13 | - 14 | - 5.51 15 | - true 16 | - true 17 | -------------------------------------------------------------------------------- /DotNetDBF.Test/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DotNetDBF.Test 6 | { 7 | #if NET472 8 | public class Program 9 | { 10 | public static void Main() 11 | { 12 | 13 | } 14 | } 15 | #endif 16 | } 17 | -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/cp1251_summary.txt: -------------------------------------------------------------------------------- 1 | 2 | Database: cp1251.dbf 3 | Type: (30) Visual FoxPro 4 | Memo File: false 5 | Records: 4 6 | 7 | Fields: 8 | Name Type Length Decimal 9 | ------------------------------------------------------------------------------ 10 | RN N 4 0 11 | NAME C 100 0 12 | -------------------------------------------------------------------------------- /DotNetDBF/Properties/MoreAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("DotNetDBF.Enumerable,PublicKey=00240000048000009400000006020000002400005253413100040000010001008713EA5197F8878AF1E1CDEF220E2D0A898944AD1643B851775EB8624697A183FBCD2ED8C1A58CE185B657D6381419AFF8B0DE8F8934F2B7E5DC7C19C11DE8D146B113F6794BF604BD2A11334DCF1022A485DD7A6E6BED8873D26363E9692136598B7750AD633747922657FF215347614DE2FDFA7866843F2924C9E5DB2545E1")] -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/foxprodb/types.dbf: -------------------------------------------------------------------------------- 1 | 0h7CONTACT_TYICONTACT_T2C2 foxpro-db-test.dbc Buyer Seller  -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/foxprodb/setup.dbf: -------------------------------------------------------------------------------- 1 | 0h7KEY_NAMEC2VALUEI3 foxpro-db-test.dbc CALLS  CONTACTS  CONTACT_TYPES  -------------------------------------------------------------------------------- /DotNetDBF.Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // Setting ComVisible to false makes the types in this assembly not visible 6 | // to COM components. If you need to access a type in this assembly from 7 | // COM, set the ComVisible attribute to true on that type. 8 | [assembly: ComVisible(false)] 9 | 10 | // The following GUID is for the ID of the typelib if this project is exposed to COM 11 | [assembly: Guid("8d5436e3-f584-40a0-acdc-65346eceadb0")] 12 | -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/dbase_8b_summary.txt: -------------------------------------------------------------------------------- 1 | 2 | Database: dbase_8b.dbf 3 | Type: (8b) dBase IV with memo file 4 | Memo File: true 5 | Records: 10 6 | 7 | Fields: 8 | Name Type Length Decimal 9 | ------------------------------------------------------------------------------ 10 | CHARACTER C 100 0 11 | NUMERICAL N 20 2 12 | DATE D 8 0 13 | LOGICAL L 1 0 14 | FLOAT F 20 18 15 | MEMO M 10 0 16 | -------------------------------------------------------------------------------- /DotNetDBF/original java src/DBFException.java: -------------------------------------------------------------------------------- 1 | /* 2 | DBFException 3 | Represents exceptions happen in the JAvaDBF classes. 4 | 5 | This file is part of JavaDBF packege. 6 | 7 | author: anil@linuxense.com 8 | license: LGPL (http://www.gnu.org/copyleft/lesser.html) 9 | 10 | $Id: DBFException.java,v 1.2 2004/03/31 10:40:18 anil Exp $ 11 | */ 12 | package com.linuxense.javadbf; 13 | 14 | import java.io.IOException; 15 | 16 | public class DBFException extends IOException { 17 | 18 | public DBFException() { 19 | 20 | super(); 21 | } 22 | 23 | public DBFException( String msg) { 24 | 25 | super( msg); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/dbase_83_schema.txt: -------------------------------------------------------------------------------- 1 | ActiveRecord::Schema.define do 2 | create_table "dbase_83" do |t| 3 | t.column "id", :integer 4 | t.column "catcount", :integer 5 | t.column "agrpcount", :integer 6 | t.column "pgrpcount", :integer 7 | t.column "order", :integer 8 | t.column "code", :string, :limit => 50 9 | t.column "name", :string, :limit => 100 10 | t.column "thumbnail", :string, :limit => 254 11 | t.column "image", :string, :limit => 254 12 | t.column "price", :float 13 | t.column "cost", :float 14 | t.column "desc", :text 15 | t.column "weight", :float 16 | t.column "taxable", :boolean 17 | t.column "active", :boolean 18 | end 19 | end -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: Build .net core 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | name: Test ${{ matrix.os }} for dotnet ${{ matrix.dotnet }} 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | dotnet: [ '8.0.x' ] 12 | os: [ubuntu-latest, macOS-latest] 13 | 14 | steps: 15 | - uses: actions/checkout@v1 16 | - name: Setup .NET Core 17 | uses: actions/setup-dotnet@v1 18 | with: 19 | dotnet-version: ${{ matrix.dotnet }} 20 | - name: Build with dotnet core 21 | run: dotnet build --configuration Release 22 | - name: Tests 23 | run: dotnet test DotNetDBF.Test/DotNetDBF.Test.csproj --configuration Release --no-build --no-restore 24 | -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/dbase_83_record_0.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - 87 3 | - 2 4 | - 0 5 | - 0 6 | - 87 7 | - '1' 8 | - Assorted Petits Fours 9 | - graphics/00000001/t_1.jpg 10 | - graphics/00000001/1.jpg 11 | - 0.0 12 | - 0.0 13 | - "Our Original assortment...a little taste of heaven for everyone. Let us\r\nselect a special assortment of our chocolate and pastel favorites for you.\r\nEach petit four is its own special hand decorated creation. Multi-layers of\r\nmoist cake with combinations of specialty fillings create memorable cake\r\nconfections. Varietes include; Luscious Lemon, Strawberry Hearts, White\r\nChocolate, Mocha Bean, Roasted Almond, Triple Chocolate, Chocolate Hazelnut,\r\nGrand Orange, Plum Squares, Milk chocolate squares, and Raspberry Blanc." 14 | - 5.51 15 | - true 16 | - true 17 | -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/dbase_31_summary.txt: -------------------------------------------------------------------------------- 1 | 2 | Database: dbase_31.dbf 3 | Type: (31) Visual FoxPro with AutoIncrement field 4 | Memo File: false 5 | Records: 77 6 | 7 | Fields: 8 | Name Type Length Decimal 9 | ------------------------------------------------------------------------------ 10 | PRODUCTID I 4 0 11 | PRODUCTNAM C 40 0 12 | SUPPLIERID I 4 0 13 | CATEGORYID I 4 0 14 | QUANTITYPE C 20 0 15 | UNITPRICE Y 8 4 16 | UNITSINSTO I 4 0 17 | UNITSONORD I 4 0 18 | REORDERLEV I 4 0 19 | DISCONTINU L 1 0 20 | _NullFlags 0 1 0 21 | -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/dbase_83_record_9.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - 34 3 | - 1 4 | - 0 5 | - 0 6 | - 34 7 | - AB01 8 | - Apricot Brandy Fruitcake 9 | - graphics/00000001/t_AB01.jpg 10 | - graphics/00000001/AB01.jpg 11 | - 37.95 12 | - 37.95 13 | - "Once tasted you will understand why we won The\r\nBoston Herald's Fruitcake Taste-off. 14 | Judges liked its generous size,\r\nluscious appearance, moist texture and fruit 15 | to cake ratio ... commented one\r\njudge \"It's a lip Smacker!\" Our signature fruitcake 16 | is baked with carefully\r\nselected ingredients that will be savored until the last 17 | moist crumb is\r\ndevoured each golden slice is brimming with Australian glaced 18 | apricots,\r\ntoasted pecans, candied orange peel, and currants, folded gently into 19 | a\r\nbrandy butter batter and slowly baked to perfection and then generously\r\nimbibed 20 | with \"Holiday Spirits\". Presented in a gift tin. (3lbs. 4oz)" 21 | - 0.0 22 | - false 23 | - true 24 | -------------------------------------------------------------------------------- /DotNetDBF/DBTHeader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace DotNetDBF 7 | { 8 | public class DBTHeader 9 | { 10 | public const byte FieldTerminator = 0x1A; 11 | 12 | 13 | private int _nextBlock; /* 0-3*/ 14 | private byte _version = 0x03; 15 | 16 | 17 | internal int NextBlock 18 | { 19 | get => _nextBlock; 20 | set => _nextBlock = value; 21 | } 22 | 23 | internal byte Version 24 | { 25 | get => _version; 26 | set => _version = value; 27 | } 28 | 29 | internal void Write(BinaryWriter dataOutput) 30 | { 31 | dataOutput.Write(_nextBlock); 32 | dataOutput.Write(new byte[12]); 33 | dataOutput.Write(_version); 34 | dataOutput.Write(new byte[495]); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /.github/workflows/dotnet48.yml: -------------------------------------------------------------------------------- 1 | name: .net framework Windows 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | name: Test Windows .net Framework Only 8 | runs-on: windows-2022 9 | steps: 10 | - uses: actions/checkout@v4 11 | - uses: NuGet/setup-nuget@v2 12 | - uses: microsoft/setup-msbuild@v2 13 | with: 14 | dotnet-version: 4.7.2 15 | 16 | - name: Restore 17 | run: nuget restore DotNetDBF.sln 18 | - name: Build 19 | run: msbuild DotNetDBF.sln -t:rebuild -property:Configuration=Release 20 | - name: Test 21 | uses: josepho0918/vstest-action@0e887de8dcfab5ce3eecaf4ad6320bb9b3ecf633 22 | with: 23 | testAssembly: DotNetDBF.Test.dll 24 | searchFolder: .\DotNetDBF.Test\bin\Release\*\ 25 | runInParallel: true 26 | platform: x64 27 | - name: Publish 28 | run: 29 | dotnet nuget push '${{ github.workspace }}\publish\*.nupkg' --source https://nuget.pkg.github.com/ekonbenefits/index.json --api-key ${{ secrets.GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /DotNetDBF/original java src/DBFBase.java: -------------------------------------------------------------------------------- 1 | /* 2 | $Id: DBFBase.java,v 1.3 2004/03/31 15:59:40 anil Exp $ 3 | Serves as the base class of DBFReader adn DBFWriter. 4 | 5 | @author: anil@linuxense.com 6 | 7 | Support for choosing implemented character Sets as 8 | suggested by Nick Voznesensky 9 | */ 10 | /** 11 | Base class for DBFReader and DBFWriter. 12 | */ 13 | package com.linuxense.javadbf; 14 | 15 | public abstract class DBFBase { 16 | 17 | protected String characterSetName = "8859_1"; 18 | protected final int END_OF_DATA = 0x1A; 19 | 20 | /* 21 | If the library is used in a non-latin environment use this method to set 22 | corresponding character set. More information: 23 | http://www.iana.org/assignments/character-sets 24 | Also see the documentation of the class java.nio.charset.Charset 25 | */ 26 | public String getCharactersetName() { 27 | 28 | return this.characterSetName; 29 | } 30 | 31 | public void setCharactersetName( String characterSetName) { 32 | 33 | this.characterSetName = characterSetName; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/dbase_83_summary.txt: -------------------------------------------------------------------------------- 1 | 2 | Database: dbase_83.dbf 3 | Type: (83) dBase III with memo file 4 | Memo File: true 5 | Records: 67 6 | 7 | Fields: 8 | Name Type Length Decimal 9 | ------------------------------------------------------------------------------ 10 | ID N 19 0 11 | CATCOUNT N 19 0 12 | AGRPCOUNT N 19 0 13 | PGRPCOUNT N 19 0 14 | ORDER N 19 0 15 | CODE C 50 0 16 | NAME C 100 0 17 | THUMBNAIL C 254 0 18 | IMAGE C 254 0 19 | PRICE N 13 2 20 | COST N 13 2 21 | DESC M 10 0 22 | WEIGHT N 13 2 23 | TAXABLE L 1 0 24 | ACTIVE L 1 0 25 | -------------------------------------------------------------------------------- /DotNetDBF.Test/Issue19Test.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using NUnit.Framework; 8 | 9 | namespace DotNetDBF.Test 10 | { 11 | 12 | [TestFixture] 13 | public class Issue19Test 14 | { 15 | private Stream DbfsData(string filename) => typeof(Issue19Test).Assembly 16 | .GetManifestResourceStream($"{nameof(DotNetDBF)}.{nameof(Test)}.dbfs.{filename}"); 17 | 18 | [Test] 19 | public void ReadTest() 20 | { 21 | using (var dbfstream = DbfsData("dbase_8b.dbf")) 22 | using (var memoStream = DbfsData("dbase_8b.dbt")) 23 | using (DBFReader dbfr = new DBFReader(dbfstream) { DataMemo = memoStream}) 24 | { 25 | object[] record = dbfr.NextRecord(); 26 | //This line would hang issue #19 https://github.com/ekonbenefits/dotnetdbf/issues/19 27 | Assert.Throws(() => record[5].ToString()); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /DotNetDBF.sln.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | DBT 3 | False 4 | <data><IncludeFilters /><ExcludeFilters /></data> 5 | <data /> 6 | True 7 | True 8 | True 9 | True -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/Readme.md: -------------------------------------------------------------------------------- 1 | Most of the DBFs here are from the spec directory of the dbf ruby gem 2 | 3 | Copyright (c) 2006-2017 Keith Morrison 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /DotNetDBF/DBFBase.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Serves as the base class of DBFReader adn DBFWriter. 3 | 4 | This file is part of DotNetDBF packege. 5 | 6 | original author (javadbf): anil@linuxense.com 2004/03/31 7 | license: LGPL (http://www.gnu.org/copyleft/lesser.html) 8 | 9 | Support for choosing implemented character Sets as 10 | suggested by Nick Voznesensky 11 | 12 | ported to C# (DotNetDBF): Jay Tuley 6/28/2007 13 | 14 | */ 15 | /** 16 | Base class for DBFReader and DBFWriter. 17 | */ 18 | 19 | using System; 20 | using System.Text; 21 | 22 | namespace DotNetDBF 23 | { 24 | public abstract class DBFBase 25 | { 26 | 27 | public Encoding CharEncoding { get; set; } = Encoding.GetEncoding("utf-8"); 28 | 29 | public int BlockSize { get; set; } = 512; 30 | 31 | private string _nullSymbol; 32 | public string NullSymbol 33 | { 34 | get => _nullSymbol ?? DBFFieldType.Unknown; 35 | set 36 | { 37 | if (value != null && value.Length != 1) 38 | throw new ArgumentException(nameof(NullSymbol)); 39 | _nullSymbol = value; 40 | } 41 | } 42 | 43 | } 44 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | dotnetdbf 2 | ========= 3 | 4 | This is a basic file parser written in C# for reading and writing xBase DBF files. 5 | Pure .NET direct access to xBase DBF. No need OBBC/JDBC for readind and writeing xBase DBF. 6 | xBase DBF files are used by Clipper and FoxPro. 7 | 8 | Compilable on Linux (Mono). 9 | For .net 4.0 projects there is an enumeration framework in which makes it easy to use Linq to Objects. 10 | 11 | Code derived from javadbf. 12 | 13 | ## Get The Binaries 14 | Use [NuGet](http://nuget.org/packages/dotnetdbf/) from Visual Studio 15 | 16 | ## Quick start 17 | Writng ODF 18 | 19 |
20 | using (Stream fos = File.Open(dbffile, FileMode.OpenOrCreate, FileAccess.ReadWrite))
21 |       using (var writer = new DBFWriter())
22 |       {        
23 |           writer.CharEncoding = Encoding.GetEncoding(866);
24 |           writer.Signature = DBFSigniture.DBase3;
25 |           writer.LanguageDriver = 0x26; // Eq to CP866
26 |           var field1 = new DBFField("DOCDATE", NativeDbType.Date);
27 |           var field2 = new DBFField("DOCNUMBER", NativeDbType.Char, 50);                    
28 |           ...
29 |           var field9 = new DBFField("f9", NativeDbType.Char, 20);
30 |           writer.Fields = new[] { field1, field2, field3, field4, field5, field6, field7, field8, field9 };
31 |           foreach (var item in items)
32 |           {
33 |               writer.AddRecord(
34 |               ...
35 |               );
36 |           }
37 |           writer.Write(fos);
38 |       }
39 | 
40 | -------------------------------------------------------------------------------- /DotNetDBF/DBFException.cs: -------------------------------------------------------------------------------- 1 | /* 2 | DBFException 3 | Represents exceptions happen in the JAvaDBF classes. 4 | 5 | This file is part of DotNetDBF packege. 6 | 7 | original author (javadbf): anil@linuxense.com 2004/03/31 8 | license: LGPL (http://www.gnu.org/copyleft/lesser.html) 9 | 10 | ported to C# (DotNetDBF): Jay Tuley 6/28/2007 11 | 12 | */ 13 | 14 | using System; 15 | using System.IO; 16 | 17 | namespace DotNetDBF 18 | { 19 | public class DBTException : DBFException 20 | { 21 | 22 | public DBTException(string msg) : base(msg) 23 | { 24 | } 25 | 26 | public DBTException(string msg, Exception internalException) 27 | : base(msg, internalException) 28 | { 29 | } 30 | } 31 | 32 | public class DBFRecordException : DBFException 33 | { 34 | public int Record { get; } 35 | 36 | public DBFRecordException(string msg, int record) : base(msg) 37 | { 38 | Record = record; 39 | } 40 | 41 | public DBFRecordException(string msg, Exception internalException) 42 | : base(msg, internalException) 43 | { 44 | } 45 | } 46 | 47 | public class DBFException : IOException 48 | { 49 | public DBFException() : base() 50 | { 51 | } 52 | 53 | public DBFException(string msg) : base(msg) 54 | { 55 | } 56 | 57 | public DBFException(string msg, Exception internalException) 58 | : base(msg, internalException) 59 | { 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/foxprodb/calls.FPT: -------------------------------------------------------------------------------- 1 | @LNancy told me about their blends. Thinking about it. Should call back later.Usual monthly order.+Asked Nancy about their Hazelnut flavoring.'Placed a special order on the Hazelnut. Changed the usual monthly order.GSpoke to Janet about NWIND carrying a coffee collection designed by us.5Too high - should wait and see if Janet comes around.;She offered $100 less per order (12 packages / order) - OK. Set up marketing plans w/ Janet.Confirmation of shipment.Got Some really odd new blends.Even more new blends.Ordered a sample.Ordered 1000 lbs. - good stuff.$Shipment to Margaret was late, oops.)Margaret's shipment went to Steven, oops. -------------------------------------------------------------------------------- /DotNetDBF/DotNetDBF.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net472;netstandard2.0 4 | True 5 | sn.snk 6 | Ekon Benefits 7 | Copyright 2009-2017 8 | This is a basic file parser for reading and writing xBase DBF files particularlly Clipper. Code originally derived from javadbf. 9 | https://github.com/ekonbenefits/dotnetdbf 10 | clipper xbase dbf 11 | Anil Kumar, Jay Tuley 12 | True 13 | True 14 | https://github.com/ekonbenefits/dotnetdbf 15 | git 16 | True 17 | True 18 | LGPL-2.1-or-later 19 | snupkg 20 | true 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /DotNetDBF.Enumerable/DotNetDBF.Enumerable.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net472;netstandard2.0 4 | True 5 | sn.snk 6 | dotnetdbf.enumerable 7 | Jay Tuley 8 | Ekon Benefits 9 | For dotnetdbf projects using .net 4.0 projects this is an enumeration framework in which makes it easy to use Linq to Objects. 10 | lgpl 11 | https://github.com/ekonbenefits/dotnetdbf 12 | clipper xbase dbf linq 13 | LGPL-2.1-or-later 14 | https://github.com/ekonbenefits/dotnetdbf 15 | https://github.com/ekonbenefits/dotnetdbf 16 | gits 17 | True 18 | True 19 | snupkg 20 | true 21 | True 22 | True 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/dbase_03_summary.txt: -------------------------------------------------------------------------------- 1 | 2 | Database: dbase_03.dbf 3 | Type: (03) dBase III without memo file 4 | Memo File: false 5 | Records: 14 6 | 7 | Fields: 8 | Name Type Length Decimal 9 | ------------------------------------------------------------------------------ 10 | Point_ID C 12 0 11 | Type C 20 0 12 | Shape C 20 0 13 | Circular_D C 20 0 14 | Non_circul C 60 0 15 | Flow_prese C 20 0 16 | Condition C 20 0 17 | Comments C 60 0 18 | Date_Visit D 8 0 19 | Time C 10 0 20 | Max_PDOP N 5 1 21 | Max_HDOP N 5 1 22 | Corr_Type C 36 0 23 | Rcvr_Type C 36 0 24 | GPS_Date D 8 0 25 | GPS_Time C 10 0 26 | Update_Sta C 36 0 27 | Feat_Name C 20 0 28 | Datafile C 20 0 29 | Unfilt_Pos N 10 0 30 | Filt_Pos N 10 0 31 | Data_Dicti C 20 0 32 | GPS_Week N 6 0 33 | GPS_Second N 12 3 34 | GPS_Height N 16 3 35 | Vert_Prec N 16 1 36 | Horz_Prec N 16 1 37 | Std_Dev N 16 6 38 | Northing N 16 3 39 | Easting N 16 3 40 | Point_ID N 9 0 41 | -------------------------------------------------------------------------------- /DotNetDBF.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 15 3 | VisualStudioVersion = 15.0.26228.9 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetDBF", "DotNetDBF\DotNetDBF.csproj", "{C5E9AE18-1EA3-4C90-AFAB-5323093BE4AC}" 6 | EndProject 7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetDBF.Enumerable", "DotNetDBF.Enumerable\DotNetDBF.Enumerable.csproj", "{3BB97ECD-325D-4288-B355-57CFC1404019}" 8 | EndProject 9 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetDBF.Test", "DotNetDBF.Test\DotNetDBF.Test.csproj", "{8D5436E3-F584-40A0-ACDC-65346ECEADB0}" 10 | EndProject 11 | Global 12 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 13 | Debug|Any CPU = Debug|Any CPU 14 | Release|Any CPU = Release|Any CPU 15 | EndGlobalSection 16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 17 | {C5E9AE18-1EA3-4C90-AFAB-5323093BE4AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 18 | {C5E9AE18-1EA3-4C90-AFAB-5323093BE4AC}.Debug|Any CPU.Build.0 = Debug|Any CPU 19 | {C5E9AE18-1EA3-4C90-AFAB-5323093BE4AC}.Release|Any CPU.ActiveCfg = Release|Any CPU 20 | {C5E9AE18-1EA3-4C90-AFAB-5323093BE4AC}.Release|Any CPU.Build.0 = Release|Any CPU 21 | {3BB97ECD-325D-4288-B355-57CFC1404019}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {3BB97ECD-325D-4288-B355-57CFC1404019}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {3BB97ECD-325D-4288-B355-57CFC1404019}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {3BB97ECD-325D-4288-B355-57CFC1404019}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {8D5436E3-F584-40A0-ACDC-65346ECEADB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {8D5436E3-F584-40A0-ACDC-65346ECEADB0}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {8D5436E3-F584-40A0-ACDC-65346ECEADB0}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {8D5436E3-F584-40A0-ACDC-65346ECEADB0}.Release|Any CPU.Build.0 = Release|Any CPU 29 | EndGlobalSection 30 | GlobalSection(SolutionProperties) = preSolution 31 | HideSolutionNode = FALSE 32 | EndGlobalSection 33 | EndGlobal 34 | -------------------------------------------------------------------------------- /DotNetDBF.Test/DotNetDBF.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0;net472 4 | Exe 5 | Copyright © 2017 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /DotNetDBF/DBFFieldType.cs: -------------------------------------------------------------------------------- 1 | /* 2 | DBFFieldType 3 | Class for reading the records assuming that the given 4 | InputStream comtains DBF data. 5 | 6 | This file is part of DotNetDBF packege. 7 | 8 | author (DotNetDBF): Jay Tuley 6/28/2007 9 | 10 | License: LGPL (http://www.gnu.org/copyleft/lesser.html) 11 | 12 | */ 13 | 14 | using System.Data; 15 | 16 | 17 | namespace DotNetDBF 18 | { 19 | public enum NativeDbType : byte 20 | { 21 | Autoincrement = (byte) 0x2B, //+ in ASCII 22 | Timestamp = (byte) 0x40, //@ in ASCII 23 | Binary = (byte) 0x42, //B in ASCII 24 | Char = (byte) 0x43, //C in ASCII 25 | Date = (byte) 0x44, //D in ASCII 26 | Float = (byte) 0x46, //F in ASCII 27 | Ole = (byte) 0x47, //G in ASCII 28 | Long = (byte) 0x49, //I in ASCII 29 | Logical = (byte) 0x4C, //L in ASCII 30 | Memo = (byte) 0x4D, //M in ASCII 31 | Numeric = (byte) 0x4E, //N in ASCII 32 | Double = (byte) 0x4F, //O in ASCII 33 | } 34 | 35 | public static class DBFFieldType 36 | { 37 | public const byte EndOfData = 0x1A; //^Z End of File 38 | public const byte EndOfField = 0x0D; //End of Field 39 | public const byte False = 0x46; //F in Ascii 40 | public const byte Space = 0x20; //Space in ascii 41 | public const byte True = 0x54; //T in ascii 42 | public const byte UnknownByte = 0x3F; //Unknown Bool value 43 | public const string Unknown = "?"; //Unknown value 44 | 45 | public static DbType FromNative(NativeDbType @byte) 46 | { 47 | switch (@byte) 48 | { 49 | case NativeDbType.Char: 50 | return DbType.AnsiStringFixedLength; 51 | case NativeDbType.Logical: 52 | return DbType.Boolean; 53 | case NativeDbType.Numeric: 54 | return DbType.Decimal; 55 | case NativeDbType.Date: 56 | return DbType.Date; 57 | case NativeDbType.Float: 58 | return DbType.Decimal; 59 | case NativeDbType.Memo: 60 | return DbType.AnsiString; 61 | default: 62 | return DbType.Object; 63 | } 64 | } 65 | 66 | public static NativeDbType FromDbType(DbType dbType) 67 | { 68 | switch (dbType) 69 | { 70 | case DbType.AnsiStringFixedLength: 71 | return NativeDbType.Char; 72 | case DbType.Boolean: 73 | return NativeDbType.Logical; 74 | case DbType.Decimal: 75 | return NativeDbType.Numeric; 76 | case DbType.Date: 77 | return NativeDbType.Date; 78 | case DbType.AnsiString: 79 | return NativeDbType.Memo; 80 | default: 81 | throw new DBFException( 82 | $"Unsupported Type {dbType}"); 83 | } 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/dbase_f5_summary.txt: -------------------------------------------------------------------------------- 1 | 2 | Database: dbase_f5.dbf 3 | Type: (f5) FoxPro with memo file 4 | Memo File: true 5 | Records: 975 6 | 7 | Fields: 8 | Name Type Length Decimal 9 | ------------------------------------------------------------------------------ 10 | NF N 5 0 11 | SEXE C 1 0 12 | NOM C 20 0 13 | COG1 C 15 0 14 | COG2 C 15 0 15 | TELEFON C 9 0 16 | RENOM C 15 0 17 | NFP N 5 0 18 | NFM N 5 0 19 | ARXN C 10 0 20 | DATN D 8 0 21 | LLON C 15 0 22 | MUNN C 15 0 23 | COMN C 15 0 24 | PROV C 15 0 25 | PAIN C 15 0 26 | OFIC C 15 0 27 | ARXB C 10 0 28 | DATB D 8 0 29 | LLOB C 15 0 30 | MUNB C 15 0 31 | COMB C 15 0 32 | PAIB C 15 0 33 | DRIB C 30 0 34 | INAB C 30 0 35 | OFTB C 10 0 36 | OFNB C 20 0 37 | AXC1 C 10 0 38 | DTC1 D 8 0 39 | LLC1 C 15 0 40 | NFC1 N 5 0 41 | TCA1 C 10 0 42 | OTC1 C 10 0 43 | ONC1 C 20 0 44 | AXC2 C 10 0 45 | DTC2 D 8 0 46 | LLC2 C 15 0 47 | NFC2 N 5 0 48 | TCA2 C 10 0 49 | OTC2 C 10 0 50 | ONC2 C 20 0 51 | AXC3 C 10 0 52 | DTC3 D 8 0 53 | LLC3 C 15 0 54 | NFC3 N 5 0 55 | TCA3 C 10 0 56 | OTC3 C 10 0 57 | ONC3 C 20 0 58 | ARXD C 10 0 59 | DATD D 8 0 60 | LLOD C 15 0 61 | OFTD C 10 0 62 | OFND C 20 0 63 | OBS1 C 70 0 64 | OBS2 C 70 0 65 | OBS3 C 70 0 66 | OBS4 C 70 0 67 | OBSE M 10 0 68 | GHD C 15 0 69 | -------------------------------------------------------------------------------- /DotNetDBF/original java src/Utils.java: -------------------------------------------------------------------------------- 1 | /* 2 | Utils 3 | Class for contining utility functions. 4 | 5 | This file is part of JavaDBF packege. 6 | 7 | author: anil@linuxense.com 8 | license: LGPL (http://www.gnu.org/copyleft/lesser.html) 9 | 10 | $Id: Utils.java,v 1.7 2004/03/31 16:00:34 anil Exp $ 11 | */ 12 | package com.linuxense.javadbf; 13 | 14 | import java.io.*; 15 | import java.util.*; 16 | import java.text.*; 17 | 18 | /** 19 | Miscelaneous functions required by the JavaDBF package. 20 | */ 21 | public final class Utils { 22 | 23 | public static final int ALIGN_LEFT = 10; 24 | public static final int ALIGN_RIGHT = 12; 25 | 26 | private Utils(){} 27 | 28 | public static int readLittleEndianInt( DataInput in) 29 | throws IOException { 30 | 31 | int bigEndian = 0; 32 | for( int shiftBy=0; shiftBy<32; shiftBy+=8) { 33 | 34 | bigEndian |= (in.readUnsignedByte()&0xff) << shiftBy; 35 | } 36 | 37 | return bigEndian; 38 | } 39 | 40 | public static short readLittleEndianShort( DataInput in) 41 | throws IOException { 42 | 43 | int low = in.readUnsignedByte() & 0xff; 44 | int high = in.readUnsignedByte(); 45 | 46 | return (short )(high << 8 | low); 47 | } 48 | 49 | public static byte[] trimLeftSpaces( byte [] arr) { 50 | 51 | StringBuffer t_sb = new StringBuffer( arr.length); 52 | 53 | for( int i=0; i>8; 74 | 75 | return num2; 76 | } 77 | 78 | public static int littleEndian(int value) { 79 | 80 | int num1 = value; 81 | int mask = 0xff; 82 | int num2 = 0x00; 83 | 84 | num2 |= num1 & mask; 85 | 86 | for( int i=1; i<4; i++) { 87 | 88 | num2<<=8; 89 | mask <<= 8; 90 | num2 |= (num1 & mask)>>(8*i); 91 | } 92 | 93 | return num2; 94 | } 95 | 96 | public static byte[] textPadding( String text, String characterSetName, int length) throws java.io.UnsupportedEncodingException { 97 | 98 | return textPadding( text, characterSetName, length, Utils.ALIGN_LEFT); 99 | } 100 | 101 | public static byte[] textPadding( String text, String characterSetName, int length, int alignment) throws java.io.UnsupportedEncodingException { 102 | 103 | return textPadding( text, characterSetName, length, alignment, (byte)' '); 104 | } 105 | 106 | public static byte[] textPadding( String text, String characterSetName, int length, int alignment, 107 | byte paddingByte) throws java.io.UnsupportedEncodingException { 108 | 109 | if( text.length() >= length) { 110 | 111 | return text.substring( 0, length).getBytes( characterSetName); 112 | } 113 | 114 | byte byte_array[] = new byte[ length]; 115 | Arrays.fill( byte_array, paddingByte); 116 | 117 | switch( alignment) { 118 | 119 | case ALIGN_LEFT: 120 | System.arraycopy( text.getBytes( characterSetName), 0, byte_array, 0, text.length()); 121 | break; 122 | 123 | case ALIGN_RIGHT: 124 | int t_offset = length - text.length(); 125 | System.arraycopy( text.getBytes( characterSetName), 0, byte_array, t_offset, text.length()); 126 | break; 127 | } 128 | 129 | return byte_array; 130 | } 131 | 132 | public static byte[] doubleFormating( Double doubleNum, String characterSetName, int fieldLength, int sizeDecimalPart) throws java.io.UnsupportedEncodingException{ 133 | 134 | int sizeWholePart = fieldLength - (sizeDecimalPart>0?( sizeDecimalPart + 1):0); 135 | 136 | StringBuffer format = new StringBuffer( fieldLength); 137 | 138 | for( int i=0; i 0) { 144 | 145 | format.append( "."); 146 | 147 | for( int i=0; i 10 | /// Interface to get the contents of the DBF Wrapper 11 | /// 12 | /// 13 | [Obsolete("DotNetDBF.Enumerable.IDBFInterceptor is the new interface name",error:true)] 14 | public interface IDBFIntercepter:IDBFInterceptor 15 | { 16 | 17 | } 18 | 19 | 20 | [Obsolete("DotNetDBF.Enumerable.DBFInterceptor is the new class name")] 21 | public class DBFIntercepter : DBFEnumerable.DBFIntercepter 22 | { 23 | public DBFIntercepter(object[] wrappedObj, string[] fieldNames) : base(wrappedObj, fieldNames) 24 | { 25 | } 26 | } 27 | 28 | 29 | } 30 | 31 | 32 | namespace DotNetDBF.Enumerable 33 | { 34 | public static partial class DBFEnumerable 35 | { 36 | [Obsolete("DotNetDBF.Enumerable.IDBFIntercepter is the new interface name")] 37 | public interface IDBFIntercepter : DotNetDBF.Enumerable.IDBFIntercepter 38 | { 39 | } 40 | 41 | [Obsolete("DotNetDBF.Enumerable.DBFIntercepter is the new class name")] 42 | public class DBFIntercepter : DotNetDBF.Enumerable.Enuemrable.DBFIntercepter, IDBFIntercepter 43 | { 44 | public DBFIntercepter(object[] wrappedObj, string[] fieldNames) 45 | : base(wrappedObj, fieldNames) 46 | { 47 | } 48 | } 49 | } 50 | 51 | 52 | [Obsolete("DBFEnumerable is the new class name")] 53 | public static class Enuemrable 54 | { 55 | /// 56 | /// New Blank Row Dynamic object that matches writer; 57 | /// 58 | /// The writer. 59 | /// 60 | public static dynamic NewBlankRow(DBFWriter writer) 61 | { 62 | return writer.NewBlankRow(); 63 | } 64 | 65 | 66 | /// 67 | /// Writes the record. 68 | /// 69 | /// The writer. 70 | /// The value. 71 | public static void WriteRecord(DBFWriter writer, IDBFIntercepter value) 72 | { 73 | writer.WriteRecord(value); 74 | } 75 | 76 | /// 77 | /// Adds the record. 78 | /// 79 | /// The writer. 80 | /// The value. 81 | public static void AddRecord(DBFWriter writer, IDBFIntercepter value) 82 | { 83 | writer.AddRecord(writer, value); 84 | } 85 | 86 | /// 87 | /// Return all the records. T should be interface with getter properties that match types and names of the database. 88 | /// Optionally instead of T being and interface you can pass in an anonymous object with properties that match that 89 | /// database and then you'll get an IEnumerable of that anonymous type with the data filled in. 90 | /// 91 | /// 92 | /// The reader. 93 | /// The prototype. Anonymous class instance 94 | /// 95 | public static IEnumerable AllRecords(DBFReader reader, T prototype = null) where T : class 96 | { 97 | return reader.AllRecords(prototype); 98 | } 99 | 100 | /// 101 | /// Returns a list of dynamic objects whose properties and types match up with that database name. 102 | /// 103 | /// The reader. 104 | /// The where column name. 105 | /// What the were column should equal. 106 | /// 107 | public static IEnumerable DynamicAllRecords(DBFReader reader, string whereColumn = null, 108 | dynamic whereColumnEquals = null) 109 | { 110 | return reader.DynamicAllRecords(whereColumn, (object) whereColumnEquals); 111 | } 112 | 113 | 114 | [Obsolete("DotNetDBF.Enumerable.IDBFIntercepter is the new interface name",error:true)] 115 | public interface IDBFIntercepter : DotNetDBF.Enumerable.IDBFInterceptor 116 | { 117 | } 118 | 119 | [Obsolete("DotNetDBF.Enumerable.DBFIntercepter is the new class name")] 120 | public class DBFIntercepter : DotNetDBF.Enumerable.BaseDBFInterceptor, IDBFIntercepter 121 | { 122 | public DBFIntercepter(object[] wrappedObj, string[] fieldNames) 123 | : base(wrappedObj, fieldNames) 124 | { 125 | } 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /DotNetDBF/original java src/DBFHeader.java: -------------------------------------------------------------------------------- 1 | /* 2 | DBFHeader 3 | Class for reading the metadata assuming that the given 4 | InputStream carries DBF data. 5 | 6 | This file is part of JavaDBF packege. 7 | 8 | Author: anil@linuxense.com 9 | License: LGPL (http://www.gnu.org/copyleft/lesser.html) 10 | 11 | $Id$ 12 | */ 13 | 14 | package com.linuxense.javadbf; 15 | 16 | import java.io.*; 17 | import java.util.*; 18 | 19 | class DBFHeader { 20 | 21 | static final byte SIG_DBASE_III = (byte)0x03; 22 | /* DBF structure start here */ 23 | 24 | byte signature; /* 0 */ 25 | byte year; /* 1 */ 26 | byte month; /* 2 */ 27 | byte day; /* 3 */ 28 | int numberOfRecords; /* 4-7 */ 29 | short headerLength; /* 8-9 */ 30 | short recordLength; /* 10-11 */ 31 | short reserv1; /* 12-13 */ 32 | byte incompleteTransaction; /* 14 */ 33 | byte encryptionFlag; /* 15 */ 34 | int freeRecordThread; /* 16-19 */ 35 | int reserv2; /* 20-23 */ 36 | int reserv3; /* 24-27 */ 37 | byte mdxFlag; /* 28 */ 38 | byte languageDriver; /* 29 */ 39 | short reserv4; /* 30-31 */ 40 | DBFField []fieldArray; /* each 32 bytes */ 41 | byte terminator1; /* n+1 */ 42 | 43 | //byte[] databaseContainer; /* 263 bytes */ 44 | /* DBF structure ends here */ 45 | 46 | DBFHeader() { 47 | 48 | this.signature = SIG_DBASE_III; 49 | this.terminator1 = 0x0D; 50 | } 51 | 52 | void read( DataInput dataInput) throws IOException { 53 | 54 | signature = dataInput.readByte(); /* 0 */ 55 | year = dataInput.readByte(); /* 1 */ 56 | month = dataInput.readByte(); /* 2 */ 57 | day = dataInput.readByte(); /* 3 */ 58 | numberOfRecords = Utils.readLittleEndianInt( dataInput); /* 4-7 */ 59 | 60 | headerLength = Utils.readLittleEndianShort( dataInput); /* 8-9 */ 61 | recordLength = Utils.readLittleEndianShort( dataInput); /* 10-11 */ 62 | 63 | reserv1 = Utils.readLittleEndianShort( dataInput); /* 12-13 */ 64 | incompleteTransaction = dataInput.readByte(); /* 14 */ 65 | encryptionFlag = dataInput.readByte(); /* 15 */ 66 | freeRecordThread = Utils.readLittleEndianInt( dataInput); /* 16-19 */ 67 | reserv2 = dataInput.readInt(); /* 20-23 */ 68 | reserv3 = dataInput.readInt(); /* 24-27 */ 69 | mdxFlag = dataInput.readByte(); /* 28 */ 70 | languageDriver = dataInput.readByte(); /* 29 */ 71 | reserv4 = Utils.readLittleEndianShort( dataInput); /* 30-31 */ 72 | 73 | Vector v_fields = new Vector(); 74 | 75 | DBFField field = DBFField.createField( dataInput); /* 32 each */ 76 | while( field != null) { 77 | 78 | v_fields.addElement( field); 79 | field = DBFField.createField( dataInput); 80 | } 81 | 82 | fieldArray = new DBFField[ v_fields.size()]; 83 | 84 | for( int i=0; i 6/28/2007 12 | 13 | */ 14 | 15 | using System; 16 | using System.Collections.Generic; 17 | using System.Globalization; 18 | using System.Linq; 19 | using System.Text; 20 | using System.Threading; 21 | 22 | namespace DotNetDBF 23 | { 24 | public static class Utils 25 | { 26 | public const int ALIGN_LEFT = 10; 27 | public const int ALIGN_RIGHT = 12; 28 | 29 | public static byte[] FillArray(byte[] anArray, byte value) 30 | { 31 | for (var i = 0; i < anArray.Length; i++) 32 | { 33 | anArray[i] = value; 34 | } 35 | return anArray; 36 | } 37 | 38 | public static byte[] trimLeftSpaces(byte[] arr) 39 | { 40 | var tList = new List(arr.Length); 41 | 42 | for (var i = 0; i < arr.Length; i++) 43 | { 44 | if (arr[i] != ' ') 45 | { 46 | tList.Add(arr[i]); 47 | } 48 | } 49 | return tList.ToArray(); 50 | } 51 | 52 | public static byte[] textPadding(string text, 53 | Encoding charEncoding, 54 | int length) 55 | { 56 | return textPadding(text, charEncoding, length, ALIGN_LEFT); 57 | } 58 | 59 | public static byte[] textPadding(string text, 60 | Encoding charEncoding, 61 | int length, 62 | int alignment) 63 | { 64 | return 65 | textPadding(text, 66 | charEncoding, 67 | length, 68 | alignment, 69 | DBFFieldType.Space); 70 | } 71 | 72 | public static byte[] textPadding(string text, 73 | Encoding charEncoding, 74 | int length, 75 | int alignment, 76 | byte paddingByte) 77 | { 78 | var tEncoding = charEncoding; 79 | var inputBytes = tEncoding.GetBytes(text); 80 | if (inputBytes.Length >= length) 81 | { 82 | return inputBytes.Take(length).ToArray(); 83 | } 84 | 85 | var byte_array = FillArray(new byte[length], paddingByte); 86 | 87 | switch (alignment) 88 | { 89 | case ALIGN_LEFT: 90 | Array.Copy(inputBytes, 91 | 0, 92 | byte_array, 93 | 0, 94 | inputBytes.Length); 95 | break; 96 | 97 | case ALIGN_RIGHT: 98 | var t_offset = length - text.Length; 99 | Array.Copy(inputBytes, 100 | 0, 101 | byte_array, 102 | t_offset, 103 | inputBytes.Length); 104 | break; 105 | } 106 | 107 | return byte_array; 108 | } 109 | 110 | public static byte[] NumericFormating(IFormattable doubleNum, 111 | Encoding charEncoding, 112 | int fieldLength, 113 | int sizeDecimalPart) 114 | { 115 | var sizeWholePart = fieldLength 116 | - 117 | (sizeDecimalPart > 0 ? (sizeDecimalPart + 1) : 0); 118 | 119 | var format = new StringBuilder(fieldLength); 120 | 121 | for (var i = 0; i < sizeWholePart; i++) 122 | { 123 | format.Append(i + 1 == sizeWholePart ? "0" : "#"); 124 | } 125 | 126 | if (sizeDecimalPart > 0) 127 | { 128 | format.Append("."); 129 | 130 | for (var i = 0; i < sizeDecimalPart; i++) 131 | { 132 | format.Append("0"); 133 | } 134 | } 135 | 136 | 137 | return 138 | textPadding( 139 | doubleNum.ToString(format.ToString(), 140 | NumberFormatInfo.InvariantInfo), 141 | charEncoding, 142 | fieldLength, 143 | ALIGN_RIGHT); 144 | } 145 | 146 | public static bool contains(byte[] arr, byte value) 147 | { 148 | return 149 | Array.Exists(arr, 150 | delegate(byte anItem) { return anItem == value; }); 151 | } 152 | 153 | 154 | public static Type TypeForNativeDBType(NativeDbType aType) 155 | { 156 | switch (aType) 157 | { 158 | case NativeDbType.Char: 159 | return typeof(string); 160 | case NativeDbType.Date: 161 | return typeof(DateTime); 162 | case NativeDbType.Numeric: 163 | return typeof(decimal); 164 | case NativeDbType.Logical: 165 | return typeof(bool); 166 | case NativeDbType.Float: 167 | return typeof(decimal); 168 | case NativeDbType.Memo: 169 | return typeof(MemoValue); 170 | default: 171 | return typeof(Object); 172 | } 173 | } 174 | } 175 | } -------------------------------------------------------------------------------- /DotNetDBF/MemoValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace DotNetDBF 7 | { 8 | public class MemoValue 9 | { 10 | public const string MemoTerminator = "\x1A"; 11 | private bool _loaded; 12 | private bool _new; 13 | 14 | 15 | public MemoValue(string aValue) 16 | { 17 | _lockName = $"DotNetDBF.Memo.new.{Guid.NewGuid()}"; 18 | Value = aValue; 19 | } 20 | 21 | 22 | internal MemoValue(long block, DBFBase aBase, 23 | string fileLoc, DBFReader.LazyStream fileStream) 24 | { 25 | _block = block; 26 | _base = aBase; 27 | _fileLoc = fileLoc; 28 | _fileStream = fileStream; 29 | if (string.IsNullOrEmpty(fileLoc)) 30 | { 31 | _lockName = fileStream(); 32 | } 33 | else 34 | { 35 | _lockName = $"DotNetDBF.Memo.read.{_fileLoc}"; 36 | } 37 | } 38 | 39 | private readonly DBFBase _base; 40 | private readonly object _lockName; 41 | private long _block; 42 | private readonly string _fileLoc; 43 | private string _value; 44 | private readonly DBFReader.LazyStream _fileStream; 45 | 46 | internal long Block => _block; 47 | 48 | internal void Write(DBFWriter aBase) 49 | { 50 | lock (_lockName) 51 | { 52 | if (!_new) 53 | return; 54 | 55 | var raf = aBase.DataMemo; 56 | 57 | /* before proceeding check whether the passed in File object 58 | is an empty/non-existent file or not. 59 | */ 60 | if (raf == null) 61 | { 62 | throw new InvalidDataException("Null Memo Field Stream from Writer"); 63 | } 64 | 65 | var tWriter = new BinaryWriter(raf, aBase.CharEncoding); //Don't close the stream could be used else where; 66 | 67 | if (raf.Length == 0) 68 | { 69 | var tHeader = new DBTHeader(); 70 | tHeader.Write(tWriter); 71 | } 72 | 73 | var tValue = _value; 74 | if ((tValue.Length + sizeof(int)) % aBase.BlockSize != 0) 75 | { 76 | tValue = tValue + MemoTerminator; 77 | } 78 | 79 | var tPosition = raf.Seek(0, SeekOrigin.End); //Got To End Of File 80 | var tBlockDiff = tPosition % aBase.BlockSize; 81 | if (tBlockDiff != 0) 82 | { 83 | tPosition = raf.Seek(aBase.BlockSize - tBlockDiff, SeekOrigin.Current); 84 | } 85 | _block = tPosition / aBase.BlockSize; 86 | var tData = aBase.CharEncoding.GetBytes(tValue); 87 | var tDataLength = tData.Length; 88 | var tNewDiff = (tDataLength % aBase.BlockSize); 89 | tWriter.Write(tData); 90 | if (tNewDiff != 0) 91 | tWriter.Seek(aBase.BlockSize - (tDataLength % aBase.BlockSize), SeekOrigin.Current); 92 | 93 | } 94 | } 95 | 96 | 97 | public string Value 98 | { 99 | get 100 | { 101 | lock (_lockName) 102 | { 103 | if (_new || _loaded) return _value; 104 | var fileStream = _fileStream(); 105 | 106 | var reader = new BinaryReader(fileStream); 107 | 108 | { 109 | reader.BaseStream.Seek(_block * _base.BlockSize, SeekOrigin.Begin); 110 | var builder = new StringBuilder(); 111 | int termIndex; 112 | var softReturn = _base.CharEncoding.GetString(new byte[] {0x8d, 0x0a}); 113 | 114 | do 115 | { 116 | var data = reader.ReadBytes(_base.BlockSize); 117 | if ((data.Length == 0)) 118 | { 119 | throw new DBTException("Missing Data for block or no 1a memo terminator"); 120 | } 121 | var stringVal = _base.CharEncoding.GetString(data); 122 | termIndex = stringVal.IndexOf(MemoTerminator, StringComparison.Ordinal); 123 | if (termIndex != -1) 124 | stringVal = stringVal.Substring(0, termIndex); 125 | builder.Append(stringVal); 126 | } while (termIndex == -1); 127 | _value = builder.ToString().Replace(softReturn, string.Empty); 128 | } 129 | _loaded = true; 130 | 131 | return _value; 132 | } 133 | } 134 | set 135 | { 136 | lock (_lockName) 137 | { 138 | _new = true; 139 | _value = value; 140 | } 141 | } 142 | } 143 | 144 | public override int GetHashCode() 145 | { 146 | return _lockName.GetHashCode(); 147 | } 148 | 149 | public override string ToString() 150 | { 151 | return Value; 152 | } 153 | 154 | public override bool Equals(object obj) 155 | { 156 | if (obj is MemoValue m) 157 | { 158 | return ReferenceEquals(this, obj) || Value.Equals(m.Value); 159 | } 160 | 161 | return false; 162 | } 163 | } 164 | } -------------------------------------------------------------------------------- /DotNetDBF/DBFHeader.cs: -------------------------------------------------------------------------------- 1 | /* 2 | DBFHeader 3 | Class for reading the metadata assuming that the given 4 | InputStream carries DBF data. 5 | 6 | This file is part of DotNetDBF packege. 7 | 8 | original author (javadbf): anil@linuxense.com 2004/03/31 9 | 10 | License: LGPL (http://www.gnu.org/copyleft/lesser.html) 11 | 12 | ported to C# (DotNetDBF): Jay Tuley 6/28/2007 13 | 14 | 15 | */ 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.IO; 20 | 21 | namespace DotNetDBF 22 | { 23 | [Obsolete("Use DBFSignature instead", error:true)] 24 | public static class DBFSigniture 25 | { 26 | public const byte NotSet = 0, 27 | WithMemo = 0x80, 28 | DBase3 = 0x03, 29 | DBase3WithMemo = DBase3 | WithMemo; 30 | } 31 | 32 | public static class DBFSignature 33 | { 34 | public const byte NotSet = 0, 35 | WithMemo = 0x80, 36 | DBase3 = 0x03, 37 | DBase3WithMemo = DBase3 | WithMemo; 38 | } 39 | 40 | [Flags] 41 | public enum MemoFlags : byte 42 | { 43 | } 44 | 45 | 46 | public class DBFHeader 47 | { 48 | public const byte HeaderRecordTerminator = 0x0D; 49 | 50 | internal byte Signature { get; set; } /* 0 */ 51 | internal byte Year { set; get; } /* 1 */ 52 | internal byte Month { set; get; } /* 2 */ 53 | internal byte Day { set; get; } /* 3 */ 54 | internal int NumberOfRecords { set; get; } /* 4-7 */ 55 | internal short HeaderLength { set; get; } /* 8-9 */ 56 | internal short RecordLength { set; get; } /* 10-11 */ 57 | private short _reserv1; /* 12-13 */ 58 | private byte _incompleteTransaction; /* 14 */ 59 | private byte _encryptionFlag; /* 15 */ 60 | private int _freeRecordThread; /* 16-19 */ 61 | private int _reserv2; /* 20-23 */ 62 | private int _reserv3; /* 24-27 */ 63 | private byte _mdxFlag; /* 28 */ 64 | internal byte LanguageDriver { get; set; } /* 29 */ 65 | private short _reserv4; /* 30-31 */ 66 | internal DBFField[] FieldArray { set; get; } /* each 32 bytes */ 67 | 68 | 69 | public DBFHeader() 70 | { 71 | Signature = DBFSignature.DBase3; 72 | } 73 | 74 | 75 | 76 | internal short Size => (short) (sizeof(byte) + 77 | sizeof(byte) + sizeof(byte) + sizeof(byte) + 78 | sizeof(int) + 79 | sizeof(short) + 80 | sizeof(short) + 81 | sizeof(short) + 82 | sizeof(byte) + 83 | sizeof(byte) + 84 | sizeof(int) + 85 | sizeof(int) + 86 | sizeof(int) + 87 | sizeof(byte) + 88 | sizeof(byte) + 89 | sizeof(short) + 90 | (DBFField.SIZE * FieldArray.Length) + 91 | sizeof(byte)); 92 | 93 | internal short RecordSize 94 | { 95 | get 96 | { 97 | var tRecordLength = 0; 98 | for (var i = 0; i < FieldArray.Length; i++) 99 | { 100 | tRecordLength += FieldArray[i].FieldLength; 101 | } 102 | 103 | return (short) (tRecordLength + 1); 104 | } 105 | } 106 | 107 | internal void Read(BinaryReader dataInput) 108 | { 109 | Signature = dataInput.ReadByte(); /* 0 */ 110 | Year = dataInput.ReadByte(); /* 1 */ 111 | Month = dataInput.ReadByte(); /* 2 */ 112 | Day = dataInput.ReadByte(); /* 3 */ 113 | NumberOfRecords = dataInput.ReadInt32(); /* 4-7 */ 114 | 115 | HeaderLength = dataInput.ReadInt16(); /* 8-9 */ 116 | RecordLength = dataInput.ReadInt16(); /* 10-11 */ 117 | 118 | _reserv1 = dataInput.ReadInt16(); /* 12-13 */ 119 | _incompleteTransaction = dataInput.ReadByte(); /* 14 */ 120 | _encryptionFlag = dataInput.ReadByte(); /* 15 */ 121 | _freeRecordThread = dataInput.ReadInt32(); /* 16-19 */ 122 | _reserv2 = dataInput.ReadInt32(); /* 20-23 */ 123 | _reserv3 = dataInput.ReadInt32(); /* 24-27 */ 124 | _mdxFlag = dataInput.ReadByte(); /* 28 */ 125 | LanguageDriver = dataInput.ReadByte(); /* 29 */ 126 | _reserv4 = dataInput.ReadInt16(); /* 30-31 */ 127 | 128 | 129 | var v_fields = new List(); 130 | 131 | var field = DBFField.CreateField(dataInput); /* 32 each */ 132 | while (field != null) 133 | { 134 | v_fields.Add(field); 135 | field = DBFField.CreateField(dataInput); 136 | } 137 | 138 | FieldArray = v_fields.ToArray(); 139 | //System.out.println( "Number of fields: " + _fieldArray.length); 140 | } 141 | 142 | internal void Write(BinaryWriter dataOutput) 143 | { 144 | dataOutput.Write(Signature); /* 0 */ 145 | var tNow = DateTime.Now; 146 | Year = (byte) (tNow.Year - 1900); 147 | Month = (byte) (tNow.Month); 148 | Day = (byte) (tNow.Day); 149 | 150 | dataOutput.Write(Year); /* 1 */ 151 | dataOutput.Write(Month); /* 2 */ 152 | dataOutput.Write(Day); /* 3 */ 153 | 154 | //System.out.println( "Number of records in O/S: " + numberOfRecords); 155 | dataOutput.Write(NumberOfRecords); /* 4-7 */ 156 | 157 | HeaderLength = Size; 158 | dataOutput.Write(HeaderLength); /* 8-9 */ 159 | 160 | RecordLength = RecordSize; 161 | dataOutput.Write(RecordLength); /* 10-11 */ 162 | 163 | dataOutput.Write(_reserv1); /* 12-13 */ 164 | dataOutput.Write(_incompleteTransaction); /* 14 */ 165 | dataOutput.Write(_encryptionFlag); /* 15 */ 166 | dataOutput.Write(_freeRecordThread); /* 16-19 */ 167 | dataOutput.Write(_reserv2); /* 20-23 */ 168 | dataOutput.Write(_reserv3); /* 24-27 */ 169 | 170 | dataOutput.Write(_mdxFlag); /* 28 */ 171 | dataOutput.Write(LanguageDriver); /* 29 */ 172 | dataOutput.Write(_reserv4); /* 30-31 */ 173 | 174 | foreach (var field in FieldArray) 175 | { 176 | field.Write(dataOutput); 177 | } 178 | 179 | dataOutput.Write(HeaderRecordTerminator); /* n+1 */ 180 | } 181 | } 182 | } -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/dbase_30_summary.txt: -------------------------------------------------------------------------------- 1 | 2 | Database: dbase_30.dbf 3 | Type: (30) Visual FoxPro 4 | Memo File: true 5 | Records: 34 6 | 7 | Fields: 8 | Name Type Length Decimal 9 | ------------------------------------------------------------------------------ 10 | ACCESSNO C 15 0 11 | ACQVALUE N 12 2 12 | APPNOTES M 4 0 13 | APPRAISOR C 75 0 14 | CABINET C 25 0 15 | CAPTION C 30 0 16 | CAT C 1 0 17 | CATBY C 25 0 18 | CATDATE D 8 0 19 | CATTYPE C 15 0 20 | CLASSES M 4 0 21 | COLLECTION C 75 0 22 | CONDDATE D 8 0 23 | CONDEXAM C 25 0 24 | CONDITION C 35 0 25 | CONDNOTES M 4 0 26 | CONTAINER C 40 0 27 | COPYRIGHT M 4 0 28 | CREATOR C 80 0 29 | CREDIT M 4 0 30 | CURVALMAX N 12 2 31 | CURVALUE N 12 2 32 | DATASET C 15 0 33 | DATE C 50 0 34 | DESCRIP M 4 0 35 | DIMNOTES M 4 0 36 | DISPVALUE C 10 0 37 | DRAWER C 20 0 38 | EARLYDATE N 4 0 39 | EVENT C 80 0 40 | EXHIBITID C 36 0 41 | EXHIBITNO N 7 0 42 | EXHLABEL1 M 4 0 43 | EXHLABEL2 M 4 0 44 | EXHLABEL3 M 4 0 45 | EXHLABEL4 M 4 0 46 | EXHSTART D 8 0 47 | FILMSIZE C 35 0 48 | FLAGDATE T 8 0 49 | FLAGNOTES M 4 0 50 | FLAGREASON C 20 0 51 | FRAME C 75 0 52 | FRAMENO C 25 0 53 | GPARENT C 45 0 54 | HOMELOC C 60 0 55 | IMAGEFILE C 60 0 56 | IMAGENO N 3 0 57 | INSCOMP C 30 0 58 | INSDATE D 8 0 59 | INSPHONE C 25 0 60 | INSPREMIUM C 20 0 61 | INSREP C 30 0 62 | INSVALUE N 10 2 63 | INVNBY C 25 0 64 | INVNDATE D 8 0 65 | LATEDATE N 4 0 66 | LEGAL M 4 0 67 | LOANCOND M 4 0 68 | LOANDATE D 8 0 69 | LOANDUE D 8 0 70 | LOANID C 36 0 71 | LOANINNO C 15 0 72 | MAINTCYCLE C 10 0 73 | MAINTDATE D 8 0 74 | MAINTNOTE M 4 0 75 | MEDIUM C 75 0 76 | NEGLOC C 60 0 77 | NEGNO C 25 0 78 | NOTES M 4 0 79 | OBJECTID C 25 0 80 | OBJNAME C 40 0 81 | OLDNO C 25 0 82 | ORIGCOPY C 15 0 83 | OTHERNO C 25 0 84 | OUTDATE D 8 0 85 | PARENT C 40 0 86 | PEOPLE M 4 0 87 | PLACE C 100 0 88 | POLICYNO C 20 0 89 | PRINTSIZE C 35 0 90 | PROCESS C 75 0 91 | PROVENANCE M 4 0 92 | PUBNOTES M 4 0 93 | RECAS C 20 0 94 | RECDATE C 10 0 95 | RECFROM C 120 0 96 | RELATION C 36 0 97 | RELNOTES M 4 0 98 | ROOM C 25 0 99 | SGFLAG C 1 0 100 | SHELF C 20 0 101 | SITE C 40 0 102 | SITENO C 12 0 103 | SLIDENO C 25 0 104 | STATUS C 20 0 105 | STATUSBY C 25 0 106 | STATUSDATE D 8 0 107 | STERMS M 4 0 108 | STUDIO C 60 0 109 | SUBJECTS M 4 0 110 | TCABINET C 25 0 111 | TCONTAINER C 40 0 112 | TDRAWER C 20 0 113 | TEMPAUTHOR C 25 0 114 | TEMPBY C 25 0 115 | TEMPDATE D 8 0 116 | TEMPLOC C 60 0 117 | TEMPNOTES M 4 0 118 | TEMPREASON C 50 0 119 | TEMPUNTIL C 10 0 120 | TITLE M 4 0 121 | TITLESORT C 100 0 122 | TROOM C 25 0 123 | TSHELF C 20 0 124 | TWALL C 20 0 125 | UDF1 C 75 0 126 | UDF10 C 75 0 127 | UDF11 C 20 0 128 | UDF12 C 20 0 129 | UDF13 N 12 0 130 | UDF14 N 12 2 131 | UDF15 N 12 2 132 | UDF16 N 12 3 133 | UDF17 N 12 3 134 | UDF18 D 8 0 135 | UDF19 D 8 0 136 | UDF20 D 8 0 137 | UDF21 M 4 0 138 | UDF22 M 4 0 139 | UDF2 C 75 0 140 | UDF3 C 75 0 141 | UDF4 C 75 0 142 | UDF5 C 75 0 143 | UDF6 C 75 0 144 | UDF7 C 75 0 145 | UDF8 C 75 0 146 | UDF9 C 75 0 147 | UPDATED T 8 0 148 | UPDATEDBY C 25 0 149 | VALUEDATE D 8 0 150 | WALL C 20 0 151 | WEBINCLUDE L 1 0 152 | ZSORTER C 69 0 153 | ZSORTERX C 44 0 154 | PPID C 36 0 155 | -------------------------------------------------------------------------------- /DotNetDBF/original java src/DBFField.java: -------------------------------------------------------------------------------- 1 | /* 2 | DBFField 3 | Class represents a "field" (or column) definition of a DBF data structure. 4 | 5 | This file is part of JavaDBF packege. 6 | 7 | author: anil@linuxense.com 8 | license: LGPL (http://www.gnu.org/copyleft/lesser.html) 9 | 10 | $Id: DBFField.java,v 1.7 2004/03/31 10:50:11 anil Exp $ 11 | */ 12 | 13 | package com.linuxense.javadbf; 14 | import java.io.*; 15 | 16 | /** 17 | DBFField represents a field specification in an dbf file. 18 | 19 | DBFField objects are either created and added to a DBFWriter object or obtained 20 | from DBFReader object through getField( int) query. 21 | 22 | */ 23 | public class DBFField { 24 | 25 | public static final byte FIELD_TYPE_C = (byte)'C'; 26 | public static final byte FIELD_TYPE_L = (byte)'L'; 27 | public static final byte FIELD_TYPE_N = (byte)'N'; 28 | public static final byte FIELD_TYPE_F = (byte)'F'; 29 | public static final byte FIELD_TYPE_D = (byte)'D'; 30 | public static final byte FIELD_TYPE_M = (byte)'M'; 31 | 32 | /* Field struct variables start here */ 33 | byte[] fieldName = new byte[ 11]; /* 0-10*/ 34 | byte dataType; /* 11 */ 35 | int reserv1; /* 12-15 */ 36 | int fieldLength; /* 16 */ 37 | byte decimalCount; /* 17 */ 38 | short reserv2; /* 18-19 */ 39 | byte workAreaId; /* 20 */ 40 | short reserv3; /* 21-22 */ 41 | byte setFieldsFlag; /* 23 */ 42 | byte[] reserv4 = new byte[ 7]; /* 24-30 */ 43 | byte indexFieldFlag; /* 31 */ 44 | /* Field struct variables end here */ 45 | 46 | /* other class variables */ 47 | int nameNullIndex = 0; 48 | 49 | /** 50 | Creates a DBFField object from the data read from the given DataInputStream. 51 | 52 | The data in the DataInputStream object is supposed to be organised correctly 53 | and the stream "pointer" is supposed to be positioned properly. 54 | 55 | @param in DataInputStream 56 | @return Returns the created DBFField object. 57 | @throws IOException If any stream reading problems occures. 58 | */ 59 | protected static DBFField createField( DataInput in) 60 | throws IOException { 61 | 62 | DBFField field = new DBFField(); 63 | 64 | byte t_byte = in.readByte(); /* 0 */ 65 | if( t_byte == (byte)0x0d) { 66 | 67 | //System.out.println( "End of header found"); 68 | return null; 69 | } 70 | 71 | in.readFully( field.fieldName, 1, 10); /* 1-10 */ 72 | field.fieldName[0] = t_byte; 73 | 74 | for( int i=0; i 10) { 206 | 207 | throw new IllegalArgumentException( "Field name should be of length 0-10"); 208 | } 209 | 210 | this.fieldName = value.getBytes(); 211 | this.nameNullIndex = this.fieldName.length; 212 | } 213 | 214 | /** 215 | Sets the data type of the field. 216 | 217 | @param type of the field. One of the following:
218 | C, L, N, F, D, M 219 | */ 220 | public void setDataType( byte value) { 221 | 222 | switch( value) { 223 | 224 | case 'D': 225 | this.fieldLength = 8; /* fall through */ 226 | case 'C': 227 | case 'L': 228 | case 'N': 229 | case 'F': 230 | case 'M': 231 | 232 | this.dataType = value; 233 | break; 234 | 235 | default: 236 | throw new IllegalArgumentException( "Unknown data type"); 237 | } 238 | } 239 | 240 | /** 241 | Length of the field. 242 | This method should be called before calling setDecimalCount(). 243 | 244 | @param Length of the field as int. 245 | */ 246 | public void setFieldLength( int value) { 247 | 248 | if( value <= 0) { 249 | 250 | throw new IllegalArgumentException( "Field length should be a positive number"); 251 | } 252 | 253 | if( this.dataType == FIELD_TYPE_D) { 254 | 255 | throw new UnsupportedOperationException( "Cannot do this on a Date field"); 256 | } 257 | 258 | fieldLength = value; 259 | } 260 | 261 | /** 262 | Sets the decimal place size of the field. 263 | Before calling this method the size of the field 264 | should be set by calling setFieldLength(). 265 | 266 | @param Size of the decimal field. 267 | */ 268 | public void setDecimalCount( int value) { 269 | 270 | if( value < 0) { 271 | 272 | throw new IllegalArgumentException( "Decimal length should be a positive number"); 273 | } 274 | 275 | if( value > fieldLength) { 276 | 277 | throw new IllegalArgumentException( "Decimal length should be less than field length"); 278 | } 279 | 280 | decimalCount = (byte)value; 281 | } 282 | 283 | } 284 | -------------------------------------------------------------------------------- /DotNetDBF/original java src/DBFReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | DBFReader 3 | Class for reading the records assuming that the given 4 | InputStream comtains DBF data. 5 | 6 | This file is part of JavaDBF packege. 7 | 8 | Author: anil@linuxense.com 9 | License: LGPL (http://www.gnu.org/copyleft/lesser.html) 10 | 11 | $Id: DBFReader.java,v 1.8 2004/03/31 10:54:03 anil Exp $ 12 | */ 13 | 14 | package com.linuxense.javadbf; 15 | 16 | import java.io.*; 17 | import java.util.*; 18 | 19 | /** 20 | DBFReader class can creates objects to represent DBF data. 21 | 22 | This Class is used to read data from a DBF file. Meta data and 23 | records can be queried against this document. 24 | 25 |

26 | DBFReader cannot write anythng to a DBF file. For creating DBF files 27 | use DBFWriter. 28 | 29 |

30 | Fetching rocord is possible only in the forward direction and 31 | cannot re-wound. In such situation, a suggested approach is to reconstruct the object. 32 | 33 |

34 | The nextRecord() method returns an array of Objects and the types of these 35 | Object are as follows: 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
xBase TypeJava Type
CString
NInteger
FDouble
LBoolean
Djava.util.Date
58 | 59 | */ 60 | public class DBFReader extends DBFBase { 61 | 62 | DataInputStream dataInputStream; 63 | DBFHeader header; 64 | 65 | /* Class specific variables */ 66 | boolean isClosed = true; 67 | 68 | /** 69 | Initializes a DBFReader object. 70 | 71 | When this constructor returns the object 72 | will have completed reading the hader (meta date) and 73 | header information can be quried there on. And it will 74 | be ready to return the first row. 75 | 76 | @param InputStream where the data is read from. 77 | */ 78 | public DBFReader( InputStream in) throws DBFException { 79 | 80 | try { 81 | 82 | this.dataInputStream = new DataInputStream( in); 83 | this.isClosed = false; 84 | this.header = new DBFHeader(); 85 | this.header.read( this.dataInputStream); 86 | 87 | /* it might be required to leap to the start of records at times */ 88 | int t_dataStartIndex = this.header.headerLength - ( 32 + (32*this.header.fieldArray.length)) - 1; 89 | if( t_dataStartIndex > 0) { 90 | 91 | dataInputStream.skip( t_dataStartIndex); 92 | } 93 | } 94 | catch( IOException e) { 95 | 96 | throw new DBFException( e.getMessage()); 97 | } 98 | } 99 | 100 | 101 | public String toString() { 102 | 103 | StringBuffer sb = new StringBuffer( this.header.year + "/" + this.header.month + "/" + this.header.day + "\n" 104 | + "Total records: " + this.header.numberOfRecords + 105 | "\nHEader length: " + this.header.headerLength + 106 | ""); 107 | 108 | for( int i=0; i 0 && !Utils.contains( t_float, (byte)'?')) { 242 | 243 | recordObjects[i] = new Float( new String( t_float)); 244 | } 245 | else { 246 | 247 | recordObjects[i] = null; 248 | } 249 | } 250 | catch( NumberFormatException e) { 251 | 252 | throw new DBFException( "Failed to parse Float: " + e.getMessage()); 253 | } 254 | 255 | break; 256 | 257 | case 'N': 258 | 259 | try { 260 | 261 | byte t_numeric[] = new byte[ this.header.fieldArray[i].getFieldLength()]; 262 | dataInputStream.read( t_numeric); 263 | t_numeric = Utils.trimLeftSpaces( t_numeric); 264 | 265 | if( t_numeric.length > 0 && !Utils.contains( t_numeric, (byte)'?')) { 266 | 267 | recordObjects[i] = new Double( new String( t_numeric)); 268 | } 269 | else { 270 | 271 | recordObjects[i] = null; 272 | } 273 | } 274 | catch( NumberFormatException e) { 275 | 276 | throw new DBFException( "Failed to parse Number: " + e.getMessage()); 277 | } 278 | 279 | break; 280 | 281 | case 'L': 282 | 283 | byte t_logical = dataInputStream.readByte(); 284 | if( t_logical == 'Y' || t_logical == 't' || t_logical == 'T' || t_logical == 't') { 285 | 286 | recordObjects[i] = Boolean.TRUE; 287 | } 288 | else { 289 | 290 | recordObjects[i] = Boolean.FALSE; 291 | } 292 | break; 293 | 294 | case 'M': 295 | // TODO Later 296 | recordObjects[i] = new String( "null"); 297 | break; 298 | 299 | default: 300 | recordObjects[i] = new String( "null"); 301 | } 302 | } 303 | } 304 | catch( EOFException e) { 305 | 306 | return null; 307 | } 308 | catch( IOException e) { 309 | 310 | throw new DBFException( e.getMessage()); 311 | } 312 | 313 | return recordObjects; 314 | } 315 | } 316 | -------------------------------------------------------------------------------- /DotNetDBF.Test/dbfs/dbase_03.dbf: -------------------------------------------------------------------------------- 1 |  NPoint_IDC TypeCShapeCCircular_DCNon_circulC<Flow_preseCConditionCCommentsC<Date_VisitDTimeC 2 | Max_PDOPNMax_HDOPNCorr_TypeC$Rcvr_TypeC$GPS_DateDGPS_TimeC 3 | Update_StaC$Feat_NameCDatafileCUnfilt_PosN 4 | Filt_PosN 5 | Data_DictiCGPS_WeekNGPS_SecondN GPS_HeightNVert_PrecNHorz_PrecNStd_DevNNorthingNEastingNPoint_IDN 0507121 CMP circular 12 no Good 2005071210:56:30am 5.2 2.0Postprocessed Code GeoXT 2005071210:56:52amNew Driveway 050712TR2819.cor 2 2MS4 1331 226625.000 1131.323 3.1 1.3 0.897088 557904.898 2212577.192 401 0507122 CMP circular 12 no Good 2005071210:57:34am 4.9 2.0Postprocessed Code GeoXT 2005071210:57:37amNew Driveway 050712TR2819.cor 1 1MS4 1331 226670.000 1125.142 2.8 1.3 557997.831 2212576.868 402 0507123 CMP circular 12 no Good 2005071210:59:03am 5.4 4.4Postprocessed Code GeoXT 2005071210:59:12amNew Driveway 050712TR2819.cor 1 1MS4 1331 226765.000 1127.570 2.2 3.5 558184.757 2212571.349 403 0507125 CMP circular 12 no Good 2005071211:02:43am 3.4 1.5Postprocessed Code GeoXT 2005071211:03:12amNew Driveway 050712TR2819.cor 1 1MS4 1331 227005.000 1125.364 3.2 1.6 558703.723 2212562.547 405 05071210 CMP circular 15 no Good 2005071211:15:20am 3.7 2.2Postprocessed Code GeoXT 2005071211:14:52amNew Driveway 050712TR2819.cor 1 1MS4 1331 227705.000 1118.605 1.8 2.1 558945.763 2212739.979 410 05071216 CMP circular 12 no Good 2005071212:13:23pm 4.4 1.8Postprocessed Code GeoXT 2005071212:13:57pmNew Driveway 050712TR2819.cor 1 1MS4 1331 231250.000 1117.390 3.1 1.2 559024.234 2212856.927 416 05071217 CMP circular 12 no Good 2005071212:16:46pm 4.4 1.8Postprocessed Code GeoXT 2005071212:17:12pmNew Driveway 050712TR2819.cor 1 1MS4 1331 231445.000 1125.714 3.2 1.3 559342.534 2213340.161 417 05071219 CMP circular 12 no Plugged 2005071212:22:55pm 4.4 1.8Postprocessed Code GeoXT 2005071212:22:22pmNew Driveway 050712TR2819.cor 1 1MS4 1331 231755.000 1110.786 2.5 1.1 559578.776 2213560.247 419 05071224 CMP circular 12 no Good 2005071212:37:17pm 4.1 1.7Postprocessed Code GeoXT 2005071212:38:32pmNew Driveway 050712TR2819.cor 1 1MS4 1331 232725.000 1077.924 2.8 1.4 560582.575 2213759.022 424 05071225 CMP circular 12 no Good 2005071212:39:48pm 4.0 1.7Postprocessed Code GeoXT 2005071212:39:52pmNew Driveway 050712TR2819.cor 1 1MS4 1331 232805.000 1082.990 2.0 1.0 560678.501 2213716.657 425 05071229 CMP circular 12 no Good 2005071212:49:05pm 3.7 1.7Postprocessed Code GeoXT 2005071212:49:07pmNew Driveway 050712TR2819.cor 1 1MS4 1331 233360.000 1096.860 2.4 1.2 560126.094 2213720.301 429 05071231 CMP circular 12 no Plugged 2005071212:53:58pm 3.0 1.6Postprocessed Code GeoXT 2005071212:54:02pmNew Driveway 050712TR2819.cor 1 1MS4 1331 233655.000 1105.113 1.8 1.1 559952.331 2213689.001 431 05071232 CMP circular 12 no Plugged 2005071212:55:47pm 3.5 1.7Postprocessed Code GeoXT 2005071212:55:47pmNew Driveway 050712TR2819.cor 2 2MS4 1331 233760.000 1101.939 2.1 1.1 1.223112 559870.352 2213661.918 432 05071236 CMP circular 12 no Plugged 2005071201:08:40pm 3.3 1.6Postprocessed Code GeoXT 2005071201:08:42pmNew Driveway 050712TR2819.cor 1 1MS4 1331 234535.000 1125.517 1.8 1.2 559195.031 2213046.199 436 -------------------------------------------------------------------------------- /DotNetDBF/original java src/DBFWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | DBFWriter 3 | Class for defining a DBF structure and addin data to that structure and 4 | finally writing it to an OutputStream. 5 | 6 | This file is part of JavaDBF packege. 7 | 8 | author: anil@linuxense.com 9 | license: LGPL (http://www.gnu.org/copyleft/lesser.html) 10 | 11 | $Id: DBFWriter.java,v 1.9 2004/03/31 10:57:16 anil Exp $ 12 | */ 13 | package com.linuxense.javadbf; 14 | import java.io.*; 15 | import java.util.*; 16 | 17 | /** 18 | An object of this class can create a DBF file. 19 | 20 | Create an object,
21 | then define fields by creating DBFField objects and
22 | add them to the DBFWriter object
23 | add records using the addRecord() method and then
24 | call write() method. 25 | */ 26 | public class DBFWriter extends DBFBase { 27 | 28 | /* other class variables */ 29 | DBFHeader header; 30 | Vector v_records = new Vector(); 31 | int recordCount = 0; 32 | RandomAccessFile raf = null; /* Open and append records to an existing DBF */ 33 | boolean appendMode = false; 34 | 35 | /** 36 | Creates an empty Object. 37 | */ 38 | public DBFWriter() { 39 | 40 | this.header = new DBFHeader(); 41 | } 42 | 43 | /** 44 | Creates a DBFWriter which can append to records to an existing DBF file. 45 | @param dbfFile. The file passed in shouls be a valid DBF file. 46 | @exception Throws DBFException if the passed in file does exist but not a valid DBF file, or if an IO error occurs. 47 | */ 48 | public DBFWriter( File dbfFile) 49 | throws DBFException { 50 | 51 | try { 52 | 53 | this.raf = new RandomAccessFile( dbfFile, "rw"); 54 | 55 | /* before proceeding check whether the passed in File object 56 | is an empty/non-existent file or not. 57 | */ 58 | if( !dbfFile.exists() || dbfFile.length() == 0) { 59 | 60 | this.header = new DBFHeader(); 61 | return; 62 | } 63 | 64 | header = new DBFHeader(); 65 | this.header.read( raf); 66 | 67 | /* position file pointer at the end of the raf */ 68 | this.raf.seek( this.raf.length()-1 /* to ignore the END_OF_DATA byte at EoF */); 69 | } 70 | catch( FileNotFoundException e) { 71 | 72 | throw new DBFException( "Specified file is not found. " + e.getMessage()); 73 | } 74 | catch( IOException e) { 75 | 76 | throw new DBFException( e.getMessage() + " while reading header"); 77 | } 78 | 79 | this.recordCount = this.header.numberOfRecords; 80 | } 81 | 82 | /** 83 | Sets fields. 84 | */ 85 | public void setFields( DBFField[] fields) 86 | throws DBFException { 87 | 88 | if( this.header.fieldArray != null) { 89 | 90 | throw new DBFException( "Fields has already been set"); 91 | } 92 | 93 | if( fields == null || fields.length == 0) { 94 | 95 | throw new DBFException( "Should have at least one field"); 96 | } 97 | 98 | for( int i=0; i 12 | /// Interface to get the contents of the DBF Wrapper 13 | /// 14 | public interface IDBFInterceptor 15 | { 16 | ///

17 | /// Does field exist in row 18 | /// 19 | /// 20 | bool Exists(string fieldName); 21 | 22 | /// 23 | /// Gets the data row. 24 | /// 25 | /// 26 | object[] GetDataRow(); 27 | } 28 | 29 | #pragma warning disable 618 30 | public class DBFInterceptor : DBFIntercepter 31 | #pragma warning restore 618 32 | { 33 | public DBFInterceptor(object[] wrappedObj, string[] fieldNames) : base(wrappedObj, fieldNames) 34 | { 35 | } 36 | } 37 | 38 | 39 | /// 40 | /// DBF Dynamic Wrapper 41 | /// 42 | public abstract class BaseDBFInterceptor : Dynamitey.DynamicObjects.BaseObject, IDBFInterceptor 43 | { 44 | private readonly string[] _fieldNames; 45 | private readonly object[] _wrappedArray; 46 | 47 | protected BaseDBFInterceptor(object[] wrappedObj, string[] fieldNames) 48 | { 49 | _wrappedArray = wrappedObj; 50 | _fieldNames = fieldNames; 51 | } 52 | 53 | public override IEnumerable GetDynamicMemberNames() 54 | { 55 | return _fieldNames; 56 | } 57 | 58 | public bool Exists(string fieldName) 59 | { 60 | return _fieldNames.Contains(fieldName); 61 | } 62 | 63 | public override bool TryGetMember(GetMemberBinder binder, out object result) 64 | { 65 | result = null; 66 | var tLookup = binder.Name; 67 | var tIndex = Array.FindIndex(_fieldNames, 68 | it => it.Equals(tLookup, StringComparison.InvariantCultureIgnoreCase)); 69 | 70 | if (tIndex < 0) 71 | return false; 72 | 73 | 74 | result = _wrappedArray[tIndex]; 75 | 76 | 77 | if (TryTypeForName(tLookup, out var outType)) 78 | { 79 | result = Dynamic.CoerceConvert(result, outType); 80 | } 81 | 82 | return true; 83 | } 84 | 85 | public override bool TrySetMember(SetMemberBinder binder, object value) 86 | { 87 | var tLookup = binder.Name; 88 | var tIndex = Array.FindIndex(_fieldNames, 89 | it => it.Equals(tLookup, StringComparison.InvariantCultureIgnoreCase)); 90 | 91 | if (tIndex < 0) 92 | return false; 93 | 94 | if (TryTypeForName(tLookup, out var outType)) 95 | { 96 | value = Dynamic.CoerceConvert(value, outType); 97 | } 98 | 99 | _wrappedArray[tIndex] = value; 100 | 101 | return true; 102 | } 103 | 104 | public object[] GetDataRow() 105 | { 106 | return _wrappedArray; 107 | } 108 | } 109 | 110 | /// 111 | /// Enumerable API 112 | /// 113 | public static partial class DBFEnumerable 114 | { 115 | /// 116 | /// New Blank Row Dynamic object that matches writer; 117 | /// 118 | /// The writer. 119 | /// 120 | public static dynamic NewBlankRow(this DBFWriter writer) 121 | { 122 | var fields = writer.Fields.Select(it => it.Name).ToArray(); 123 | var obj = new object[fields.Length]; 124 | return new Enumerable.DBFInterceptor(obj, fields); 125 | } 126 | 127 | public static void CopyRecordTo(this IDBFInterceptor original, IDBFInterceptor dest) 128 | { 129 | foreach (var fieldName in Dynamitey.Dynamic.GetMemberNames(dest, true)) 130 | { 131 | try 132 | { 133 | var val = Dynamic.InvokeGet(original, fieldName); 134 | Dynamic.InvokeSet(dest, fieldName, val); 135 | } 136 | catch 137 | { 138 | // ignored 139 | } 140 | } 141 | } 142 | 143 | 144 | /// 145 | /// Writes the record. 146 | /// 147 | /// The writer. 148 | /// The value. 149 | public static void WriteRecord(this DBFWriter writer, Enumerable.IDBFInterceptor value) 150 | { 151 | writer.WriteRecord(value.GetDataRow()); 152 | } 153 | 154 | /// 155 | /// Adds the record. 156 | /// 157 | /// The writer. 158 | /// The value. 159 | public static void AddRecord(this DBFWriter writer, Enumerable.IDBFInterceptor value) 160 | { 161 | writer.AddRecord(value.GetDataRow()); 162 | } 163 | 164 | /// 165 | /// Return all the records. T should be interface with getter properties that match types and names of the database. 166 | /// Optionally instead of T being and interface you can pass in an anonymous object with properties that match that 167 | /// database and then you'll get an IEnumerable of that anonymous type with the data filled in. 168 | /// 169 | /// 170 | /// The reader. 171 | /// The prototype. Anonymous class instance 172 | /// 173 | public static IEnumerable AllRecords(this DBFReader reader, T prototype = null) where T : class 174 | { 175 | var tType = typeof(T); 176 | 177 | var tProperties = tType.GetProperties() 178 | .Where( 179 | it => 180 | Array.FindIndex(reader.Fields, 181 | f => f.Name.Equals(it.Name, StringComparison.InvariantCultureIgnoreCase)) >= 0) 182 | .ToList(); 183 | var tProps = tProperties 184 | .Select( 185 | it => 186 | Array.FindIndex(reader.Fields, 187 | jt => jt.Name.Equals(it.Name, StringComparison.InvariantCultureIgnoreCase))) 188 | .Where(it => it >= 0) 189 | .ToArray(); 190 | 191 | var tOrderedProps = tProps.OrderBy(it => it).ToArray(); 192 | var tReturn = new List(); 193 | 194 | 195 | if (tType.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Any()) 196 | { 197 | var tAnon = reader.NextRecord(tProps, tOrderedProps); 198 | while (tAnon != null) 199 | { 200 | tReturn.Add((T) Activator.CreateInstance(tType, tAnon)); 201 | tAnon = reader.NextRecord(tProps, tOrderedProps); 202 | } 203 | 204 | 205 | return tReturn; 206 | } 207 | 208 | var t = reader.NextRecord(tProps, tOrderedProps); 209 | 210 | while (t != null) 211 | { 212 | var interceptor = new Enumerable.DBFInterceptor(t, tProperties.Select(it => it.Name).ToArray()); 213 | 214 | tReturn.Add(interceptor.ActLike(typeof(Enumerable.IDBFInterceptor))); 215 | t = reader.NextRecord(tProps, tOrderedProps); 216 | } 217 | 218 | 219 | return tReturn; 220 | } 221 | 222 | /// 223 | /// Returns a list of dynamic objects whose properties and types match up with that database name. 224 | /// 225 | /// The reader. 226 | /// The where column name. 227 | /// What the were column should equal. 228 | /// 229 | public static IEnumerable DynamicAllRecords(this DBFReader reader, string whereColumn = null, 230 | dynamic whereColumnEquals = null) 231 | { 232 | var props = reader.GetSelectFields().Select(it => it.Name).ToArray(); 233 | 234 | int? whereColumnIndex = null; 235 | if (!String.IsNullOrEmpty(whereColumn)) 236 | { 237 | whereColumnIndex = Array.FindIndex(props, 238 | it => it.Equals(whereColumn, StringComparison.InvariantCultureIgnoreCase)); 239 | } 240 | 241 | 242 | var tReturn = new List(); 243 | var t = reader.NextRecord(); 244 | 245 | while (t != null) 246 | { 247 | if (whereColumnIndex is int i) 248 | { 249 | dynamic tO = t[i]; 250 | if (!tO.Equals(whereColumnEquals)) 251 | { 252 | t = reader.NextRecord(); 253 | continue; 254 | } 255 | } 256 | 257 | 258 | var interceptor = new Enumerable.DBFInterceptor(t, props); 259 | 260 | 261 | tReturn.Add(interceptor); 262 | t = reader.NextRecord(); 263 | } 264 | 265 | 266 | return tReturn; 267 | } 268 | } 269 | } -------------------------------------------------------------------------------- /DotNetDBF/DBFField.cs: -------------------------------------------------------------------------------- 1 | /* 2 | DBFField 3 | Class represents a "field" (or column) definition of a DBF data structure. 4 | 5 | This file is part of DotNetDBF packege. 6 | 7 | original author (javadbf): anil@linuxense.com 2004/03/31 8 | 9 | license: LGPL (http://www.gnu.org/copyleft/lesser.html) 10 | 11 | ported to C# (DotNetDBF): Jay Tuley 6/28/2007 12 | 13 | 14 | */ 15 | 16 | using System; 17 | using System.Diagnostics; 18 | using System.IO; 19 | using System.Text; 20 | 21 | namespace DotNetDBF 22 | { 23 | [DebuggerDisplay("Field:{Name}, Length:{FieldLength}")] 24 | public class DBFField 25 | { 26 | public const int SIZE = 32; 27 | public byte dataType; /* 11 */ 28 | public byte decimalCount; /* 17 */ 29 | public int fieldLength; /* 16 */ 30 | public byte[] fieldName = new byte[11]; /* 0-10*/ 31 | public byte indexFieldFlag; /* 31 */ 32 | 33 | /* other class variables */ 34 | public int nameNullIndex = 0; 35 | public int reserv1; /* 12-15 */ 36 | public short reserv2; /* 18-19 */ 37 | public short reserv3; /* 21-22 */ 38 | public byte[] reserv4 = new byte[7]; /* 24-30 */ 39 | public byte setFieldsFlag; /* 23 */ 40 | public byte workAreaId; /* 20 */ 41 | 42 | public DBFField() 43 | { 44 | } 45 | 46 | public DBFField(string fieldName, NativeDbType type) 47 | { 48 | Name = fieldName; 49 | DataType = type; 50 | } 51 | 52 | public DBFField(string fieldName, 53 | NativeDbType type, 54 | int fieldLength) 55 | { 56 | Name = fieldName; 57 | DataType = type; 58 | FieldLength = fieldLength; 59 | } 60 | 61 | public DBFField(string fieldName, 62 | NativeDbType type, 63 | int fieldLength, 64 | int decimalCount) 65 | { 66 | Name = fieldName; 67 | DataType = type; 68 | FieldLength = fieldLength; 69 | DecimalCount = decimalCount; 70 | } 71 | 72 | public int Size => SIZE; 73 | 74 | /** 75 | Returns the name of the field. 76 | 77 | @return Name of the field as String. 78 | */ 79 | 80 | public string Name 81 | { 82 | get => Encoding.ASCII.GetString(fieldName, 0, nameNullIndex); 83 | set 84 | { 85 | if (value == null) 86 | { 87 | throw new ArgumentException("Field name cannot be null"); 88 | } 89 | 90 | if (value.Length == 0 91 | || value.Length > 10) 92 | { 93 | throw new ArgumentException( 94 | "Field name should be of length 0-10"); 95 | } 96 | 97 | fieldName = Encoding.ASCII.GetBytes(value); 98 | nameNullIndex = fieldName.Length; 99 | } 100 | } 101 | 102 | /** 103 | Returns the data type of the field. 104 | 105 | @return Data type as byte. 106 | */ 107 | 108 | public Type Type => Utils.TypeForNativeDBType(DataType); 109 | 110 | 111 | public NativeDbType DataType 112 | { 113 | get => (NativeDbType)dataType; 114 | set 115 | { 116 | switch (value) 117 | { 118 | case NativeDbType.Date: 119 | fieldLength = 8; /* fall through */ 120 | goto default; 121 | case NativeDbType.Memo: 122 | fieldLength = 10; 123 | goto default; 124 | case NativeDbType.Logical: 125 | fieldLength = 1; 126 | goto default; 127 | default: 128 | dataType = (byte)value; 129 | break; 130 | } 131 | } 132 | } 133 | 134 | /** 135 | Returns field length. 136 | 137 | @return field length as int. 138 | */ 139 | 140 | public int FieldLength 141 | { 142 | get 143 | { 144 | if (DataType == NativeDbType.Char) 145 | { 146 | return fieldLength + (decimalCount * 256); 147 | } 148 | 149 | return fieldLength; 150 | } 151 | /** 152 | Length of the field. 153 | This method should be called before calling setDecimalCount(). 154 | 155 | @param Length of the field as int. 156 | */ 157 | set 158 | { 159 | if (value <= 0) 160 | { 161 | throw new ArgumentException( 162 | "Field length should be a positive number"); 163 | } 164 | 165 | switch (DataType) 166 | { 167 | case NativeDbType.Date: 168 | case NativeDbType.Memo: 169 | case NativeDbType.Logical: 170 | throw new NotSupportedException( 171 | "Cannot set length on this type of field"); 172 | case NativeDbType.Char when value > 255: 173 | fieldLength = value % 256; 174 | decimalCount = (byte) (value / 256); 175 | return; 176 | default: 177 | fieldLength = value; 178 | break; 179 | } 180 | } 181 | } 182 | 183 | /** 184 | Returns the decimal part. This is applicable 185 | only if the field type if of numeric in nature. 186 | 187 | If the field is specified to hold integral values 188 | the value returned by this method will be zero. 189 | 190 | @return decimal field size as int. 191 | */ 192 | 193 | public int DecimalCount 194 | { 195 | get => decimalCount; 196 | /** 197 | Sets the decimal place size of the field. 198 | Before calling this method the size of the field 199 | should be set by calling setFieldLength(). 200 | 201 | @param Size of the decimal field. 202 | */ 203 | set 204 | { 205 | if (value < 0) 206 | { 207 | throw new ArgumentException( 208 | "Decimal length should be a positive number"); 209 | } 210 | 211 | if (value > fieldLength) 212 | { 213 | throw new ArgumentException( 214 | "Decimal length should be less than field length"); 215 | } 216 | 217 | decimalCount = (byte) value; 218 | } 219 | } 220 | 221 | public bool Read(BinaryReader reader) 222 | { 223 | var t_byte = reader.ReadByte(); /* 0 */ 224 | if (t_byte == DBFFieldType.EndOfField) 225 | { 226 | //System.out.println( "End of header found"); 227 | return false; 228 | } 229 | 230 | reader.Read(fieldName, 1, 10); /* 1-10 */ 231 | fieldName[0] = t_byte; 232 | 233 | for (var i = 0; i < fieldName.Length; i++) 234 | { 235 | if (fieldName[i] 236 | == 0) 237 | { 238 | nameNullIndex = i; 239 | break; 240 | } 241 | } 242 | 243 | dataType = reader.ReadByte(); /* 11 */ 244 | reserv1 = reader.ReadInt32(); /* 12-15 */ 245 | fieldLength = reader.ReadByte(); /* 16 */ 246 | decimalCount = reader.ReadByte(); /* 17 */ 247 | reserv2 = reader.ReadInt16(); /* 18-19 */ 248 | workAreaId = reader.ReadByte(); /* 20 */ 249 | reserv3 = reader.ReadInt16(); /* 21-22 */ 250 | setFieldsFlag = reader.ReadByte(); /* 23 */ 251 | reader.Read(reserv4, 0, 7); /* 24-30 */ 252 | indexFieldFlag = reader.ReadByte(); /* 31 */ 253 | return true; 254 | } 255 | 256 | /** 257 | Writes the content of DBFField object into the stream as per 258 | DBF format specifications. 259 | 260 | @param os OutputStream 261 | @throws IOException if any stream related issues occur. 262 | */ 263 | 264 | public void Write(BinaryWriter writer) 265 | { 266 | // Field Name 267 | writer.Write(fieldName); /* 0-10 */ 268 | writer.Write(new byte[11 - fieldName.Length], 269 | 0, 270 | 11 - fieldName.Length); 271 | 272 | // data type 273 | writer.Write(dataType); /* 11 */ 274 | writer.Write(reserv1); /* 12-15 */ 275 | writer.Write((byte) fieldLength); /* 16 */ 276 | writer.Write(decimalCount); /* 17 */ 277 | writer.Write(reserv2); /* 18-19 */ 278 | writer.Write(workAreaId); /* 20 */ 279 | writer.Write(reserv3); /* 21-22 */ 280 | writer.Write(setFieldsFlag); /* 23 */ 281 | writer.Write(reserv4); /* 24-30*/ 282 | writer.Write(indexFieldFlag); /* 31 */ 283 | } 284 | 285 | /** 286 | Creates a DBFField object from the data read from the given DataInputStream. 287 | 288 | The data in the DataInputStream object is supposed to be organised correctly 289 | and the stream "pointer" is supposed to be positioned properly. 290 | 291 | @param in DataInputStream 292 | @return Returns the created DBFField object. 293 | @throws IOException If any stream reading problems occurs. 294 | */ 295 | 296 | internal static DBFField CreateField(BinaryReader reader) 297 | { 298 | var field = new DBFField(); 299 | if (field.Read(reader)) 300 | { 301 | return field; 302 | } 303 | else 304 | { 305 | return null; 306 | } 307 | } 308 | } 309 | } -------------------------------------------------------------------------------- /DotNetDBF/DBFReader.cs: -------------------------------------------------------------------------------- 1 | /* 2 | DBFReader 3 | Class for reading the records assuming that the given 4 | InputStream comtains DBF data. 5 | 6 | This file is part of DotNetDBF packege. 7 | 8 | original author (javadbf): anil@linuxense.com 2004/03/31 9 | 10 | License: LGPL (http://www.gnu.org/copyleft/lesser.html) 11 | 12 | ported to C# (DotNetDBF): Jay Tuley 6/28/2007 13 | */ 14 | 15 | using System; 16 | using System.Collections.Generic; 17 | using System.Globalization; 18 | using System.IO; 19 | using System.Text; 20 | using System.Linq; 21 | 22 | namespace DotNetDBF 23 | { 24 | public class DBFReader : DBFBase, IDisposable 25 | { 26 | private BinaryReader _dataInputStream; 27 | private DBFHeader _header; 28 | private Stream _dataMemo; 29 | 30 | private string _dataMemoLoc; 31 | 32 | private int[] _selectFields = new int[] {}; 33 | private int[] _orderedSelectFields = new int[] {}; 34 | /* Class specific variables */ 35 | private bool _isClosed = true; 36 | 37 | 38 | /** 39 | Initializes a DBFReader object. 40 | 41 | When this constructor returns the object 42 | will have completed reading the header (meta date) and 43 | header information can be queried there on. And it will 44 | be ready to return the first row. 45 | 46 | @param InputStream where the data is read from. 47 | */ 48 | 49 | 50 | public void SetSelectFields(params string[] aParams) 51 | { 52 | _selectFields = 53 | aParams.Select( 54 | it => 55 | Array.FindIndex(_header.FieldArray, 56 | jt => jt.Name.Equals(it, StringComparison.OrdinalIgnoreCase))).ToArray(); 57 | _orderedSelectFields = _selectFields.OrderBy(it => it).ToArray(); 58 | } 59 | 60 | public DBFField[] GetSelectFields() 61 | { 62 | return _selectFields.Any() 63 | ? _selectFields.Select(it => _header.FieldArray[it]).ToArray() 64 | : _header.FieldArray; 65 | } 66 | 67 | 68 | public DBFReader(string anIn) 69 | { 70 | try 71 | { 72 | _dataInputStream = new BinaryReader( 73 | File.Open(anIn, 74 | FileMode.Open, 75 | FileAccess.Read, 76 | FileShare.Read) 77 | ); 78 | 79 | var dbtPath = Path.ChangeExtension(anIn, "dbt"); 80 | if (File.Exists(dbtPath)) 81 | { 82 | _dataMemoLoc = dbtPath; 83 | } 84 | 85 | _isClosed = false; 86 | _header = new DBFHeader(); 87 | _header.Read(_dataInputStream); 88 | 89 | /* it might be required to leap to the start of records at times */ 90 | var t_dataStartIndex = _header.HeaderLength 91 | - (32 + (32 * _header.FieldArray.Length)) 92 | - 1; 93 | if (t_dataStartIndex > 0) 94 | { 95 | _dataInputStream.ReadBytes((t_dataStartIndex)); 96 | } 97 | } 98 | catch (IOException ex) 99 | { 100 | throw new DBFException("Failed To Read DBF", ex); 101 | } 102 | } 103 | 104 | public DBFReader(Stream anIn) 105 | { 106 | try 107 | { 108 | _dataInputStream = new BinaryReader(anIn); 109 | _isClosed = false; 110 | _header = new DBFHeader(); 111 | _header.Read(_dataInputStream); 112 | 113 | /* it might be required to leap to the start of records at times */ 114 | var t_dataStartIndex = _header.HeaderLength 115 | - (32 + (32 * _header.FieldArray.Length)) 116 | - 1; 117 | if (t_dataStartIndex > 0) 118 | { 119 | _dataInputStream.ReadBytes((t_dataStartIndex)); 120 | } 121 | } 122 | catch (IOException e) 123 | { 124 | throw new DBFException("Failed To Read DBF", e); 125 | } 126 | } 127 | 128 | /** 129 | Returns the number of records in the DBF. 130 | */ 131 | 132 | public int RecordCount => _header.NumberOfRecords; 133 | 134 | /** 135 | Returns the asked Field. In case of an invalid index, 136 | it returns a ArrayIndexOutOfBoundsException. 137 | 138 | @param index. Index of the field. Index of the first field is zero. 139 | */ 140 | 141 | public DBFField[] Fields => _header.FieldArray; 142 | 143 | #region IDisposable Members 144 | 145 | /// Performs application-defined tasks associated with freeing, releasing, 146 | /// or resetting unmanaged resources. 147 | /// 2 148 | public void Dispose() 149 | { 150 | Close(); 151 | } 152 | 153 | #endregion 154 | 155 | 156 | public string DataMemoLoc 157 | { 158 | get => _dataMemoLoc; 159 | set => _dataMemoLoc = value; 160 | } 161 | 162 | 163 | 164 | public delegate Stream LazyStream(); 165 | 166 | private Stream _loadedStream; 167 | 168 | private LazyStream GetLazyStreamFromLocation() 169 | { 170 | 171 | if (_dataMemo == null && !string.IsNullOrEmpty(_dataMemoLoc)) 172 | { 173 | return () => _loadedStream ?? 174 | (_loadedStream = File.Open(_dataMemoLoc, FileMode.Open, FileAccess.Read, 175 | FileShare.Read)); 176 | } 177 | if (_dataMemo != null) 178 | { 179 | return () => _dataMemo; 180 | } 181 | return null; 182 | } 183 | 184 | public Stream DataMemo 185 | { 186 | get => _dataMemo; 187 | set => _dataMemo = value; 188 | } 189 | 190 | public override string ToString() 191 | { 192 | var sb = 193 | new StringBuilder(_header.Year + "/" + _header.Month + "/" 194 | + _header.Day + "\n" 195 | + "Total records: " + _header.NumberOfRecords + 196 | "\nHeader length: " + _header.HeaderLength + 197 | ""); 198 | 199 | for (var i = 0; i < _header.FieldArray.Length; i++) 200 | { 201 | sb.Append(_header.FieldArray[i].Name); 202 | sb.Append("\n"); 203 | } 204 | 205 | return sb.ToString(); 206 | } 207 | 208 | public void Close() 209 | { 210 | 211 | 212 | _loadedStream?.Close(); 213 | _dataMemo?.Close(); 214 | _dataInputStream.Close(); 215 | 216 | _dataMemo?.Dispose(); 217 | 218 | 219 | 220 | _isClosed = true; 221 | } 222 | 223 | /** 224 | Reads the returns the next row in the DBF stream. 225 | @returns The next row as an Object array. Types of the elements 226 | these arrays follow the convention mentioned in the class description. 227 | */ 228 | 229 | public object[] NextRecord() 230 | { 231 | return NextRecord(_selectFields, _orderedSelectFields); 232 | } 233 | 234 | 235 | internal object[] NextRecord(IEnumerable selectIndexes, IList sortedIndexes) 236 | { 237 | if (_isClosed) 238 | { 239 | throw new DBFException("Source is not open"); 240 | } 241 | var tOrderdSelectIndexes = sortedIndexes; 242 | 243 | var recordObjects = new object[_header.FieldArray.Length]; 244 | 245 | try 246 | { 247 | var isDeleted = false; 248 | do 249 | { 250 | if (isDeleted) 251 | { 252 | _dataInputStream.ReadBytes(_header.RecordLength - 1); 253 | } 254 | 255 | int t_byte = _dataInputStream.ReadByte(); 256 | if (t_byte == DBFFieldType.EndOfData) 257 | { 258 | return null; 259 | } 260 | 261 | isDeleted = (t_byte == '*'); 262 | } while (isDeleted); 263 | 264 | var j = 0; 265 | var k = -1; 266 | for (var i = 0; i < _header.FieldArray.Length; i++) 267 | { 268 | if (tOrderdSelectIndexes.Count == j && j != 0 269 | || 270 | (tOrderdSelectIndexes.Count > j && tOrderdSelectIndexes[j] > i && tOrderdSelectIndexes[j] != k)) 271 | { 272 | _dataInputStream.BaseStream.Seek(_header.FieldArray[i].FieldLength, SeekOrigin.Current); 273 | continue; 274 | } 275 | if (tOrderdSelectIndexes.Count > j) 276 | k = tOrderdSelectIndexes[j]; 277 | j++; 278 | 279 | 280 | switch (_header.FieldArray[i].DataType) 281 | { 282 | case NativeDbType.Char: 283 | 284 | var b_array = new byte[ 285 | _header.FieldArray[i].FieldLength 286 | ]; 287 | _dataInputStream.Read(b_array, 0, b_array.Length); 288 | 289 | recordObjects[i] = CharEncoding.GetString(b_array).TrimEnd(); 290 | break; 291 | 292 | case NativeDbType.Date: 293 | 294 | var t_byte_year = new byte[4]; 295 | _dataInputStream.Read(t_byte_year, 296 | 0, 297 | t_byte_year.Length); 298 | 299 | var t_byte_month = new byte[2]; 300 | _dataInputStream.Read(t_byte_month, 301 | 0, 302 | t_byte_month.Length); 303 | 304 | var t_byte_day = new byte[2]; 305 | _dataInputStream.Read(t_byte_day, 306 | 0, 307 | t_byte_day.Length); 308 | 309 | try 310 | { 311 | var tYear = CharEncoding.GetString(t_byte_year); 312 | var tMonth = CharEncoding.GetString(t_byte_month); 313 | var tDay = CharEncoding.GetString(t_byte_day); 314 | 315 | if (int.TryParse(tYear, out var tIntYear) && 316 | int.TryParse(tMonth, out var tIntMonth) && 317 | int.TryParse(tDay, out var tIntDay)) 318 | { 319 | recordObjects[i] = new DateTime( 320 | tIntYear, 321 | tIntMonth, 322 | tIntDay); 323 | } 324 | else 325 | { 326 | recordObjects[i] = null; 327 | } 328 | } 329 | catch (ArgumentOutOfRangeException) 330 | { 331 | /* this field may be empty or may have improper value set */ 332 | recordObjects[i] = null; 333 | } 334 | 335 | break; 336 | 337 | case NativeDbType.Float: 338 | 339 | try 340 | { 341 | var t_float = new byte[ 342 | _header.FieldArray[i].FieldLength 343 | ]; 344 | _dataInputStream.Read(t_float, 0, t_float.Length); 345 | var tParsed = CharEncoding.GetString(t_float); 346 | var tLast = tParsed.Substring(tParsed.Length - 1); 347 | if (tParsed.Length > 0 348 | && tLast != " " 349 | && tLast != NullSymbol) 350 | { 351 | // 352 | // A Float in FoxPro has 20 significant digits, since it is 353 | // stored as a string with possible E-postfix notation. 354 | // An IEEE 754 float or double can not handle this number of digits 355 | // correctly. Therefor the only correct implementation is to use a decimal. 356 | // 357 | recordObjects[i] = decimal.Parse(tParsed, 358 | NumberStyles.Float | NumberStyles.AllowLeadingWhite, 359 | NumberFormatInfo.InvariantInfo); 360 | } 361 | else 362 | { 363 | recordObjects[i] = null; 364 | } 365 | } 366 | catch (FormatException e) 367 | { 368 | throw new DBFException("Failed to parse Float", 369 | e); 370 | } 371 | 372 | break; 373 | 374 | case NativeDbType.Numeric: 375 | 376 | try 377 | { 378 | var t_numeric = new byte[ 379 | _header.FieldArray[i].FieldLength 380 | ]; 381 | _dataInputStream.Read(t_numeric, 382 | 0, 383 | t_numeric.Length); 384 | var tParsed = 385 | CharEncoding.GetString(t_numeric); 386 | var tLast = tParsed.Substring(tParsed.Length - 1); 387 | if (tParsed.Length > 0 388 | && tLast != " " 389 | && tLast != NullSymbol) 390 | { 391 | recordObjects[i] = decimal.Parse(tParsed, 392 | NumberStyles.Float | NumberStyles.AllowLeadingWhite, 393 | NumberFormatInfo.InvariantInfo); 394 | } 395 | else 396 | { 397 | recordObjects[i] = null; 398 | } 399 | } 400 | catch (FormatException e) 401 | { 402 | throw new DBFException( 403 | "Failed to parse Number", e); 404 | } 405 | 406 | break; 407 | 408 | case NativeDbType.Logical: 409 | 410 | var t_logical = _dataInputStream.ReadByte(); 411 | //todo find out whats really valid 412 | if (t_logical == 'Y' || t_logical == 't' 413 | || t_logical == 'T' 414 | || t_logical == 't') 415 | { 416 | recordObjects[i] = true; 417 | } 418 | else if (t_logical == DBFFieldType.UnknownByte) 419 | { 420 | recordObjects[i] = DBNull.Value; 421 | } 422 | else 423 | { 424 | recordObjects[i] = false; 425 | } 426 | break; 427 | 428 | case NativeDbType.Memo: 429 | if ( 430 | 431 | string.IsNullOrEmpty(_dataMemoLoc) && 432 | 433 | _dataMemo is null) 434 | { 435 | throw new Exception("Memo Location Not Set"); 436 | } 437 | 438 | 439 | var rawMemoPointer = _dataInputStream.ReadBytes(_header.FieldArray[i].FieldLength); 440 | var memoPointer = CharEncoding.GetString(rawMemoPointer); 441 | if (string.IsNullOrEmpty(memoPointer)) 442 | { 443 | recordObjects[i] = DBNull.Value; 444 | break; 445 | } 446 | 447 | if (!long.TryParse(memoPointer, out var tBlock)) 448 | { 449 | //Because Memo files can vary and are often the least important data, 450 | //we will return null when it doesn't match our format. 451 | recordObjects[i] = DBNull.Value; 452 | break; 453 | } 454 | 455 | 456 | recordObjects[i] = new MemoValue(tBlock, this, 457 | _dataMemoLoc, 458 | GetLazyStreamFromLocation()); 459 | break; 460 | default: 461 | { 462 | byte[] data = _dataInputStream.ReadBytes(_header.FieldArray[i].FieldLength); 463 | 464 | recordObjects[i] = data != null ? (object)data : DBNull.Value; 465 | 466 | break; 467 | } 468 | } 469 | } 470 | } 471 | catch (EndOfStreamException) 472 | { 473 | return null; 474 | } 475 | catch (IOException e) 476 | { 477 | throw new DBFException("Problem Reading File", e); 478 | } 479 | 480 | return selectIndexes.Any() ? selectIndexes.Select(it => recordObjects[it]).ToArray() : recordObjects; 481 | } 482 | } 483 | } 484 | -------------------------------------------------------------------------------- /DotNetDBF/DBFWriter.cs: -------------------------------------------------------------------------------- 1 | /* 2 | DBFWriter 3 | Class for defining a DBF structure and addin data to that structure and 4 | finally writing it to an OutputStream. 5 | 6 | This file is part of DotNetDBF packege. 7 | 8 | original author (javadbf): anil@linuxense.com 2004/03/31 9 | 10 | license: LGPL (http://www.gnu.org/copyleft/lesser.html) 11 | 12 | ported to C# (DotNetDBF): Jay Tuley 6/28/2007 13 | */ 14 | 15 | using System; 16 | using System.Collections; 17 | using System.Collections.Generic; 18 | using System.IO; 19 | 20 | namespace DotNetDBF 21 | { 22 | public class DBFWriter : DBFBase, IDisposable 23 | { 24 | private DBFHeader header; 25 | private Stream raf; 26 | private int recordCount; 27 | private List v_records = new List(); 28 | private Stream _dataMemo; 29 | 30 | private string _dataMemoLoc; 31 | 32 | /// Creates an empty Object. 33 | public DBFWriter() 34 | { 35 | header = new DBFHeader(); 36 | } 37 | 38 | 39 | /// Creates a DBFWriter which can append to records to an existing DBF file. 40 | /// @param dbfFile. The file passed in should be a valid DBF file. 41 | /// @exception Throws DBFException if the passed in file does exist but not a valid DBF file, or if an IO error occurs. 42 | public DBFWriter(string dbfFile) 43 | { 44 | try 45 | { 46 | raf = 47 | File.Open(dbfFile, 48 | FileMode.OpenOrCreate, 49 | FileAccess.ReadWrite); 50 | 51 | DataMemoLoc = Path.ChangeExtension(dbfFile, "dbt"); 52 | 53 | /* before proceeding check whether the passed in File object 54 | is an empty/non-existent file or not. 55 | */ 56 | if (raf.Length == 0) 57 | { 58 | header = new DBFHeader(); 59 | return; 60 | } 61 | 62 | header = new DBFHeader(); 63 | header.Read(new BinaryReader(raf)); 64 | 65 | /* position file pointer at the end of the raf */ 66 | raf.Seek(-1, SeekOrigin.End); 67 | /* check whether the last byte is 0x1A (end of file marker for dbf files) - in this case move 1 byte back to ignore it when writing new records */ 68 | var lastByte = raf.ReadByte(); /* Advances to end of stream */ 69 | if (lastByte == DBFFieldType.EndOfData) 70 | { 71 | raf.Seek(-1, SeekOrigin.End); 72 | } 73 | } 74 | catch (FileNotFoundException e) 75 | { 76 | throw new DBFException("Specified file is not found. ", e); 77 | } 78 | catch (IOException e) 79 | { 80 | throw new DBFException(" while reading header", e); 81 | } 82 | recordCount = header.NumberOfRecords; 83 | } 84 | 85 | public DBFWriter(Stream dbfFile) 86 | { 87 | raf = dbfFile; 88 | 89 | /* before proceeding check whether the passed in File object 90 | is an empty/non-existent file or not. 91 | */ 92 | if (raf.Length == 0) 93 | { 94 | header = new DBFHeader(); 95 | return; 96 | } 97 | 98 | header = new DBFHeader(); 99 | header.Read(new BinaryReader(raf)); 100 | 101 | /* position file pointer at the end of the raf */ 102 | raf.Seek(-1, SeekOrigin.End); 103 | /* check whether the last byte is 0x1A (end of file marker for dbf files) - in this case move 1 byte back to ignore it when writing new records */ 104 | var lastByte = raf.ReadByte(); /* Advances to end of stream */ 105 | if (lastByte == DBFFieldType.EndOfData) 106 | { 107 | raf.Seek(-1, SeekOrigin.End); 108 | } 109 | 110 | recordCount = header.NumberOfRecords; 111 | } 112 | 113 | public byte Signature 114 | { 115 | get => header.Signature; 116 | set => header.Signature = value; 117 | } 118 | 119 | 120 | 121 | public string DataMemoLoc 122 | { 123 | get => _dataMemoLoc; 124 | set 125 | { 126 | _dataMemoLoc = value; 127 | 128 | _dataMemo?.Close(); 129 | _dataMemo = File.Open(_dataMemoLoc, 130 | FileMode.OpenOrCreate, 131 | FileAccess.ReadWrite); 132 | } 133 | } 134 | 135 | public Stream DataMemo 136 | { 137 | get => _dataMemo; 138 | set => _dataMemo = value; 139 | } 140 | 141 | public byte LanguageDriver 142 | { 143 | set 144 | { 145 | if (header.LanguageDriver != 0x00) 146 | { 147 | throw new DBFException("LanguageDriver has already been set"); 148 | } 149 | 150 | header.LanguageDriver = value; 151 | } 152 | } 153 | 154 | 155 | public DBFField[] Fields 156 | { 157 | get => header.FieldArray; 158 | 159 | 160 | set 161 | { 162 | if (header.FieldArray != null) 163 | { 164 | throw new DBFException("Fields has already been set"); 165 | } 166 | 167 | if (value == null 168 | || value.Length == 0) 169 | { 170 | throw new DBFException("Should have at least one field"); 171 | } 172 | 173 | for (var i = 0; i < value.Length; i++) 174 | { 175 | if (value[i] == null) 176 | { 177 | throw new DBFException("Field " + (i + 1) + " is null"); 178 | } 179 | } 180 | 181 | header.FieldArray = value; 182 | 183 | try 184 | { 185 | if (raf != null 186 | && raf.Length == 0) 187 | { 188 | /* 189 | this is a new/non-existent file. So write header before proceeding 190 | */ 191 | header.Write(new BinaryWriter(raf)); 192 | } 193 | } 194 | catch (IOException e) 195 | { 196 | throw new DBFException("Error accessing file", e); 197 | } 198 | } 199 | } 200 | 201 | #region IDisposable Members 202 | 203 | /// Performs application-defined tasks associated with freeing, releasing, 204 | /// or resetting unmanaged resources. 205 | /// 2 206 | public void Dispose() 207 | { 208 | Close(); 209 | } 210 | 211 | #endregion 212 | 213 | /** 214 | Add a record. 215 | */ 216 | 217 | public void WriteRecord(params object[] values) 218 | { 219 | if (raf == null) 220 | { 221 | throw new DBFException( 222 | "Not initialized with file for WriteRecord use, use AddRecord instead"); 223 | } 224 | AddRecord(values, true); 225 | } 226 | 227 | public void AddRecord(params object[] values) 228 | { 229 | if (raf != null) 230 | { 231 | throw new DBFException( 232 | "Appending to a file, requires using WriteRecord instead"); 233 | } 234 | AddRecord(values, false); 235 | } 236 | 237 | private void AddRecord(object[] values, bool writeImmediately) 238 | { 239 | if (header.FieldArray == null) 240 | { 241 | throw new DBFException( 242 | "Fields should be set before adding records"); 243 | } 244 | 245 | if (values == null) 246 | { 247 | throw new DBFException("Null cannot be added as row"); 248 | } 249 | 250 | if (values.Length 251 | != header.FieldArray.Length) 252 | { 253 | throw new DBFException( 254 | "Invalid record. Invalid number of fields in row"); 255 | } 256 | 257 | for (var i = 0; i < header.FieldArray.Length; i++) 258 | { 259 | var fld = header.FieldArray[i]; 260 | var val = values[i]; 261 | 262 | if (val is null || val is DBNull) 263 | { 264 | continue; 265 | } 266 | 267 | void ThrowErrorIfNot() 268 | { 269 | if (!(val is T)) 270 | { 271 | throw new DBFRecordException($"Invalid value '{val}' for field {i}({fld.Name}{fld.DataType})", 0); 272 | } 273 | } 274 | 275 | switch (fld.DataType) 276 | { 277 | case NativeDbType.Char: 278 | //ignore all objects have ToString() 279 | break; 280 | 281 | case NativeDbType.Logical: 282 | ThrowErrorIfNot(); 283 | break; 284 | 285 | case NativeDbType.Numeric: 286 | ThrowErrorIfNot(); 287 | break; 288 | 289 | case NativeDbType.Date: 290 | ThrowErrorIfNot(); 291 | break; 292 | 293 | case NativeDbType.Float: 294 | ThrowErrorIfNot(); 295 | break; 296 | case NativeDbType.Memo: 297 | ThrowErrorIfNot(); 298 | break; 299 | default: 300 | throw new ArgumentOutOfRangeException(); 301 | } 302 | } 303 | 304 | if (!writeImmediately) 305 | { 306 | v_records.Add(values); 307 | } 308 | else 309 | { 310 | try 311 | { 312 | WriteRecord(new BinaryWriter(raf), values); 313 | recordCount++; 314 | } 315 | catch (IOException e) 316 | { 317 | throw new DBFException( 318 | "Error occured while writing record. ", e); 319 | } 320 | } 321 | } 322 | 323 | ///Writes the set data to the OutputStream. 324 | public void Write(Stream tOut) 325 | { 326 | try 327 | { 328 | var outStream = new BinaryWriter(tOut); 329 | 330 | header.NumberOfRecords = v_records.Count; 331 | header.Write(outStream); 332 | 333 | /* Now write all the records */ 334 | var t_recCount = v_records.Count; 335 | for (var i = 0; i < t_recCount; i++) 336 | { 337 | /* iterate through records */ 338 | 339 | var t_values = (object[]) v_records[i]; 340 | 341 | WriteRecord(outStream, t_values); 342 | } 343 | 344 | outStream.Write(DBFFieldType.EndOfData); 345 | outStream.Flush(); 346 | } 347 | catch (IOException e) 348 | { 349 | throw new DBFException("Error Writing", e); 350 | } 351 | } 352 | 353 | public void Close() 354 | { 355 | /* everything is written already. just update the header for record count and the END_OF_DATA mark */ 356 | header.NumberOfRecords = recordCount; 357 | if (raf != null) 358 | { 359 | raf.Seek(0, SeekOrigin.Begin); 360 | header.Write(new BinaryWriter(raf)); 361 | raf.Seek(0, SeekOrigin.End); 362 | raf.WriteByte(DBFFieldType.EndOfData); 363 | 364 | raf.Close(); 365 | _dataMemo?.Close(); 366 | 367 | } else if (!string.IsNullOrEmpty(DataMemoLoc)) 368 | { 369 | DataMemo.Close(); 370 | } 371 | 372 | } 373 | 374 | private void WriteRecord(BinaryWriter dataOutput, object[] objectArray) 375 | { 376 | dataOutput.Write((byte) ' '); 377 | for (var j = 0; j < header.FieldArray.Length; j++) 378 | { 379 | /* iterate through fields */ 380 | 381 | switch (header.FieldArray[j].DataType) 382 | { 383 | case NativeDbType.Char: 384 | if (objectArray[j] != null && objectArray[j] != DBNull.Value) 385 | { 386 | var str_value = objectArray[j].ToString(); 387 | dataOutput.Write( 388 | Utils.textPadding(str_value, 389 | CharEncoding, 390 | header.FieldArray[j]. 391 | FieldLength 392 | ) 393 | ); 394 | } 395 | else 396 | { 397 | dataOutput.Write( 398 | Utils.textPadding("", 399 | CharEncoding, 400 | header.FieldArray[j]. 401 | FieldLength 402 | ) 403 | ); 404 | } 405 | 406 | break; 407 | 408 | case NativeDbType.Date: 409 | if (objectArray[j] != null && objectArray[j] != DBNull.Value) 410 | { 411 | var tDate = (DateTime) objectArray[j]; 412 | 413 | dataOutput.Write( 414 | CharEncoding.GetBytes(tDate.ToString("yyyyMMdd"))); 415 | } 416 | else 417 | { 418 | dataOutput.Write( 419 | Utils.FillArray(new byte[8], DBFFieldType.Space)); 420 | } 421 | 422 | break; 423 | 424 | case NativeDbType.Float: 425 | 426 | if (objectArray[j] != null && objectArray[j] != DBNull.Value) 427 | { 428 | var tDouble = Convert.ToDecimal(objectArray[j]); 429 | dataOutput.Write( 430 | Utils.NumericFormating( 431 | tDouble, 432 | CharEncoding, 433 | header.FieldArray[j].FieldLength, 434 | header.FieldArray[j].DecimalCount 435 | ) 436 | ); 437 | } 438 | else 439 | { 440 | dataOutput.Write( 441 | Utils.textPadding( 442 | NullSymbol, 443 | CharEncoding, 444 | header.FieldArray[j].FieldLength, 445 | Utils.ALIGN_RIGHT 446 | ) 447 | ); 448 | } 449 | 450 | break; 451 | 452 | case NativeDbType.Numeric: 453 | 454 | if (objectArray[j] != null && objectArray[j] != DBNull.Value) 455 | { 456 | var tDecimal = Convert.ToDecimal(objectArray[j]); 457 | dataOutput.Write( 458 | Utils.NumericFormating( 459 | tDecimal, 460 | CharEncoding, 461 | header.FieldArray[j].FieldLength, 462 | header.FieldArray[j].DecimalCount 463 | ) 464 | ); 465 | } 466 | else 467 | { 468 | dataOutput.Write( 469 | Utils.textPadding( 470 | NullSymbol, 471 | CharEncoding, 472 | header.FieldArray[j].FieldLength, 473 | Utils.ALIGN_RIGHT 474 | ) 475 | ); 476 | } 477 | 478 | break; 479 | case NativeDbType.Logical: 480 | 481 | if (objectArray[j] != null && objectArray[j] != DBNull.Value) 482 | { 483 | if ((bool) objectArray[j]) 484 | { 485 | dataOutput.Write(DBFFieldType.True); 486 | } 487 | else 488 | { 489 | dataOutput.Write(DBFFieldType.False); 490 | } 491 | } 492 | else 493 | { 494 | dataOutput.Write(DBFFieldType.UnknownByte); 495 | } 496 | 497 | break; 498 | 499 | case NativeDbType.Memo: 500 | if (objectArray[j] != null && objectArray[j] != DBNull.Value) 501 | { 502 | var tMemoValue = ((MemoValue) objectArray[j]); 503 | 504 | tMemoValue.Write(this); 505 | 506 | dataOutput.Write(Utils.NumericFormating(tMemoValue.Block, CharEncoding, 10, 0)); 507 | } 508 | else 509 | { 510 | dataOutput.Write( 511 | Utils.textPadding("", 512 | CharEncoding, 513 | 10 514 | ) 515 | ); 516 | } 517 | 518 | 519 | break; 520 | 521 | default: 522 | throw new DBFException("Unknown field type " 523 | + header.FieldArray[j].DataType); 524 | } 525 | } /* iterating through the fields */ 526 | } 527 | } 528 | } 529 | -------------------------------------------------------------------------------- /DotNetDBF.Test/Test.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Runtime.CompilerServices; 5 | using DotNetDBF; 6 | using DotNetDBF.Enumerable; 7 | using NUnit.Framework; 8 | 9 | namespace DotNetDBFTest 10 | { 11 | public interface ITestInterface 12 | { 13 | string F1 { get; set; } 14 | 15 | string F2 { get; set; } 16 | 17 | string F3 { get; set; } 18 | } 19 | 20 | [TestFixture] 21 | public class DotNetDBFTest : AssertionHelper 22 | { 23 | private string TestPath([CallerMemberName] string name = null) 24 | => Path.Combine(Path.GetTempPath(), name + "_121212.dbf"); 25 | 26 | private string TestRAFPath([CallerMemberName] string name = null) 27 | => Path.Combine(Path.GetTempPath(), name + "_raf-1212.dbf"); 28 | 29 | 30 | private string TestClipLongPath([CallerMemberName] string name = null) 31 | => Path.Combine(Path.GetTempPath(), name + "_cliplong.dbf"); 32 | 33 | private string TestMemoPath([CallerMemberName] string name = null) 34 | => Path.Combine(Path.GetTempPath(), name + "_clipmemo.dbf"); 35 | 36 | private string TestSelectPath([CallerMemberName] string name = null) 37 | => Path.Combine(Path.GetTempPath(), name + "_select.dbf"); 38 | 39 | private string GetCharacters(int aLength) 40 | { 41 | var chars = new[] {"a", "b", "c", "d", "e", "f", "g", " "}; 42 | var returnval = string.Join(string.Empty, 43 | Enumerable.Range(0, aLength).Select(it => chars[it % chars.Length]).ToArray()); 44 | Assert.That(returnval.Length, EqualTo(aLength), "GetCharacters() did not return correct length string"); 45 | return returnval; 46 | } 47 | 48 | 49 | private static void println(String s) 50 | { 51 | Console.WriteLine(s); 52 | } 53 | 54 | 55 | [Test] 56 | public void checkDataType_N() 57 | { 58 | Decimal writtenValue; 59 | using ( 60 | Stream fos = 61 | File.Open(TestPath(), 62 | FileMode.OpenOrCreate, 63 | FileAccess.ReadWrite)) 64 | using (var writer = new DBFWriter()) 65 | { 66 | var field = new DBFField("F1", NativeDbType.Numeric, 15, 0); 67 | writer.Fields = new[] {field}; 68 | 69 | writtenValue = 123456789012345L; 70 | writer.AddRecord(writtenValue); 71 | writer.Write(fos); 72 | } 73 | using ( 74 | Stream fis = 75 | File.Open(TestPath(), 76 | FileMode.Open, 77 | FileAccess.Read)) 78 | using (var reader = new DBFReader(fis)) 79 | { 80 | var readValues = reader.NextRecord(); 81 | 82 | Assert.That(readValues[0], EqualTo(writtenValue), "Written Value Equals Read"); 83 | } 84 | } 85 | 86 | 87 | [Test] 88 | public void checkLongCharLengthWithClipper() 89 | { 90 | var fieldLength = 750; 91 | string writtenValue; 92 | using ( 93 | Stream fos = 94 | File.Open(TestClipLongPath(), 95 | FileMode.OpenOrCreate, 96 | FileAccess.ReadWrite)) 97 | { 98 | var writer = new DBFWriter(); 99 | var field = new DBFField("F1", NativeDbType.Char, fieldLength); 100 | writer.Fields = new[] {field}; 101 | 102 | writtenValue = GetCharacters(fieldLength); 103 | writer.AddRecord(writtenValue); 104 | writer.Write(fos); 105 | } 106 | using ( 107 | Stream fis = 108 | File.Open(TestClipLongPath(), 109 | FileMode.OpenOrCreate, 110 | FileAccess.ReadWrite)) 111 | { 112 | var reader = new DBFReader(fis); 113 | Assert.That(reader.Fields.First().FieldLength, EqualTo(fieldLength)); 114 | var readValues = reader.NextRecord(); 115 | 116 | Assert.That(readValues[0], EqualTo(writtenValue), "Written Value not equaling Read"); 117 | } 118 | } 119 | 120 | 121 | [Test] 122 | public void checkDataType_M() 123 | { 124 | var fieldLength = 2400; 125 | MemoValue writtenValue; 126 | using (Stream fos = File.Open(TestMemoPath(), FileMode.OpenOrCreate, FileAccess.ReadWrite)) 127 | using (var writer = new DBFWriter 128 | { 129 | DataMemoLoc = Path.ChangeExtension(TestMemoPath(), "DBT") 130 | }) 131 | { 132 | var field = new DBFField("F1", NativeDbType.Memo); 133 | writer.Fields = new[] {field}; 134 | 135 | writtenValue = new MemoValue(GetCharacters(fieldLength)); 136 | writer.AddRecord(writtenValue); 137 | writer.Write(fos); 138 | } 139 | using (Stream fis = File.Open(TestMemoPath(), FileMode.OpenOrCreate, FileAccess.ReadWrite)) 140 | using (var reader = new DBFReader(fis) 141 | { 142 | DataMemoLoc = Path.ChangeExtension(TestMemoPath(), "DBT") 143 | }) 144 | { 145 | var readValues = reader.NextRecord(); 146 | 147 | Assert.That(readValues[0], EqualTo(writtenValue), "Written Value not equaling Read"); 148 | } 149 | } 150 | 151 | [Test] 152 | public void checkSelect() 153 | { 154 | var fieldLength = 2400; 155 | string writtenValue; 156 | using ( 157 | Stream fos = 158 | File.Open(TestSelectPath(), 159 | FileMode.OpenOrCreate, 160 | FileAccess.ReadWrite)) 161 | { 162 | var writer = new DBFWriter 163 | { 164 | DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") 165 | }; 166 | var field = new DBFField("F1", NativeDbType.Memo); 167 | var field2 = new DBFField("F2", NativeDbType.Numeric, 10); 168 | var field3 = new DBFField("F3", NativeDbType.Char, 10); 169 | writer.Fields = new[] {field, field2, field3}; 170 | 171 | writtenValue = "alpha"; 172 | writer.AddRecord(new MemoValue(GetCharacters(fieldLength)), 10, writtenValue); 173 | writer.Write(fos); 174 | } 175 | using ( 176 | Stream fis = 177 | File.Open(TestSelectPath(), 178 | FileMode.OpenOrCreate, 179 | FileAccess.ReadWrite)) 180 | { 181 | var reader = new DBFReader(fis) 182 | { 183 | DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") 184 | }; 185 | reader.SetSelectFields("F3"); 186 | var readValues = reader.NextRecord(); 187 | 188 | Assert.That(readValues[0], StartsWith(writtenValue), "Written Value not equaling Read"); 189 | } 190 | } 191 | 192 | [Test] 193 | public void checkSelectDynamic() 194 | { 195 | var fieldLength = 2400; 196 | string writtenValue; 197 | string writtenMemo; 198 | using ( 199 | Stream fos = 200 | File.Open(TestSelectPath(), 201 | FileMode.OpenOrCreate, 202 | FileAccess.ReadWrite)) 203 | using (var writer = new DBFWriter 204 | { 205 | DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") 206 | }) 207 | { 208 | var field = new DBFField("F1", NativeDbType.Memo); 209 | var field2 = new DBFField("F2", NativeDbType.Numeric, 10); 210 | var field3 = new DBFField("F3", NativeDbType.Char, 10); 211 | writer.Fields = new[] {field, field2, field3}; 212 | 213 | writtenValue = "alpha"; 214 | writtenMemo = GetCharacters(fieldLength); 215 | writer.AddRecord(new MemoValue(writtenMemo), 10, writtenValue); 216 | writer.Write(fos); 217 | } 218 | using ( 219 | Stream fis = 220 | File.Open(TestSelectPath(), 221 | FileMode.OpenOrCreate, 222 | FileAccess.ReadWrite)) 223 | using (var reader = new DBFReader(fis) 224 | { 225 | DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") 226 | }) 227 | { 228 | reader.SetSelectFields("F1", "F3"); 229 | var readValues = reader.DynamicAllRecords().First(); 230 | 231 | Assert.That(readValues.F1.ToString(), EqualTo(writtenMemo), "Written Value not equaling Read"); 232 | Assert.That(readValues.F3, EqualTo(writtenValue), "Written Value not equaling Read"); 233 | } 234 | } 235 | 236 | [Test] 237 | public void checkAnnoymous() 238 | { 239 | var fieldLength = 2400; 240 | string writtenValue; 241 | using ( 242 | Stream fos = 243 | File.Open(TestSelectPath(), 244 | FileMode.OpenOrCreate, 245 | FileAccess.ReadWrite)) 246 | { 247 | var writer = new DBFWriter 248 | { 249 | DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") 250 | }; 251 | var field = new DBFField("F1", NativeDbType.Memo); 252 | var field2 = new DBFField("F2", NativeDbType.Numeric, 10); 253 | var field3 = new DBFField("F3", NativeDbType.Char, 10); 254 | writer.Fields = new[] {field, field2, field3}; 255 | 256 | writtenValue = "alpha"; 257 | writer.AddRecord(new MemoValue(GetCharacters(fieldLength)), 10, writtenValue); 258 | writer.Write(fos); 259 | } 260 | using ( 261 | Stream fis = 262 | File.Open(TestSelectPath(), 263 | FileMode.OpenOrCreate, 264 | FileAccess.ReadWrite)) 265 | { 266 | var reader = new DBFReader(fis) 267 | { 268 | DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") 269 | }; 270 | 271 | var readValues = reader.AllRecords(new {F2 = default(decimal), F3 = default(string)}); 272 | 273 | Assert.That(readValues.First().F3, StartsWith(writtenValue), "Written Value not equaling Read"); 274 | } 275 | } 276 | 277 | 278 | [Test] 279 | public void checkProxy() 280 | { 281 | var fieldLength = 2400; 282 | string writtenValue; 283 | using ( 284 | Stream fos = 285 | File.Open(TestSelectPath(), 286 | FileMode.OpenOrCreate, 287 | FileAccess.ReadWrite)) 288 | { 289 | using (var writer = new DBFWriter { 290 | DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") 291 | }) 292 | { 293 | 294 | ; 295 | var field = new DBFField("F1", NativeDbType.Memo); 296 | var field2 = new DBFField("F2", NativeDbType.Numeric, 10); 297 | var field3 = new DBFField("F3", NativeDbType.Char, 10); 298 | writer.Fields = new[] {field, field2, field3}; 299 | 300 | writtenValue = "alpha"; 301 | writer.AddRecord(new MemoValue(GetCharacters(fieldLength)), 10, writtenValue); 302 | writer.Write(fos); 303 | } 304 | } 305 | using ( 306 | Stream fis = 307 | File.Open(TestSelectPath(), 308 | FileMode.OpenOrCreate, 309 | FileAccess.ReadWrite)) 310 | { 311 | using (var reader = new DBFReader(fis) 312 | { 313 | DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") 314 | }) 315 | { 316 | 317 | var readValues = reader.AllRecords(); 318 | 319 | Assert.That(readValues.First().F3, StartsWith(writtenValue), "Written Value not equaling Read"); 320 | } 321 | } 322 | } 323 | 324 | [Test] 325 | public void checkDynamicProxy() 326 | { 327 | var fieldLength = 2400; 328 | string writtenValue; 329 | using ( 330 | Stream fos = 331 | File.Open(TestSelectPath(), 332 | FileMode.OpenOrCreate, 333 | FileAccess.ReadWrite)) 334 | using (var writer = new DBFWriter 335 | { 336 | DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") 337 | }) 338 | { 339 | var field = new DBFField("F1", NativeDbType.Memo); 340 | var field2 = new DBFField("F2", NativeDbType.Numeric, 10); 341 | var field3 = new DBFField("F3", NativeDbType.Char, 10); 342 | writer.Fields = new[] {field, field2, field3}; 343 | 344 | writtenValue = "alpha"; 345 | writer.AddRecord(new MemoValue(GetCharacters(fieldLength)), 10, writtenValue); 346 | writer.Write(fos); 347 | } 348 | using ( 349 | Stream fis = 350 | File.Open(TestSelectPath(), 351 | FileMode.OpenOrCreate, 352 | FileAccess.ReadWrite)) 353 | using (var reader = new DBFReader(fis) 354 | { 355 | DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") 356 | }) 357 | { 358 | var readValues = reader.DynamicAllRecords(); 359 | 360 | Assert.That(readValues.First().F3, StartsWith(writtenValue), "Written Value not equaling Read"); 361 | } 362 | } 363 | 364 | 365 | [Test] 366 | public void checkDynamicProxyWhere() 367 | { 368 | var fieldLength = 2400; 369 | string writtenValue; 370 | using ( 371 | Stream fos = 372 | File.Open(TestSelectPath(), 373 | FileMode.OpenOrCreate, 374 | FileAccess.ReadWrite)) 375 | using (var writer = new DBFWriter 376 | { 377 | DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") 378 | }) 379 | { 380 | var field = new DBFField("F1", NativeDbType.Memo); 381 | var field2 = new DBFField("F2", NativeDbType.Numeric, 10); 382 | var field3 = new DBFField("F3", NativeDbType.Char, 10); 383 | writer.Fields = new[] {field, field2, field3}; 384 | 385 | writtenValue = "alpha"; 386 | writer.AddRecord(new MemoValue(GetCharacters(fieldLength)), 10, writtenValue); 387 | 388 | writer.AddRecord(new MemoValue(GetCharacters(fieldLength)), 12, "beta"); 389 | 390 | writer.Write(fos); 391 | } 392 | 393 | using (var reader = new DBFReader(TestSelectPath()) 394 | { 395 | DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") 396 | }) 397 | { 398 | var readValues = reader.DynamicAllRecords(); 399 | 400 | Assert.That(Equals(readValues.Count(), 2), "All Records not matching"); 401 | } 402 | 403 | using (var reader = new DBFReader(TestSelectPath()) 404 | { 405 | DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") 406 | }) 407 | { 408 | var readValues = reader.DynamicAllRecords(whereColumn: "F2", whereColumnEquals: 10); 409 | 410 | Assert.That(Equals(readValues.Count(), 1), "All Records not matching"); 411 | } 412 | 413 | using (var reader = new DBFReader(TestSelectPath()) 414 | { 415 | DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") 416 | }) 417 | { 418 | var readValues = reader.DynamicAllRecords(whereColumn: "F2", whereColumnEquals: 12); 419 | 420 | Assert.That(Equals(readValues.Count(), 1), "All Records not matching"); 421 | } 422 | using (var reader = new DBFReader(TestSelectPath()) 423 | { 424 | DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") 425 | }) 426 | { 427 | var readValues = reader.DynamicAllRecords(whereColumn: "F2", whereColumnEquals: 13); 428 | 429 | Assert.That(Equals(readValues.Count(), 0), "All Records not matching"); 430 | } 431 | } 432 | 433 | [Test] 434 | public void checkRAFwriting() 435 | { 436 | println("Writing in RAF mode ... "); 437 | 438 | if (File.Exists(TestRAFPath())) 439 | { 440 | File.Delete(TestRAFPath()); 441 | } 442 | using (var writer = new DBFWriter(TestRAFPath())) 443 | { 444 | var fields = new DBFField[2]; 445 | 446 | fields[0] = new DBFField("F1", NativeDbType.Char, 10); 447 | 448 | fields[1] = new DBFField("F2", NativeDbType.Numeric, 2); 449 | 450 | writer.Fields = fields; 451 | 452 | 453 | writer.WriteRecord("Red", 10); 454 | writer.WriteRecord("Blue", 20); 455 | } 456 | 457 | println("done."); 458 | 459 | println("Appending to this file"); 460 | 461 | using (var writer = new DBFWriter(TestRAFPath())) 462 | { 463 | writer.WriteRecord("Green", 33); 464 | 465 | writer.WriteRecord("Yellow", 44); 466 | } 467 | println("done."); 468 | } 469 | 470 | [Test] 471 | public void ShowPaths() 472 | { 473 | println(TestPath()); 474 | 475 | println(TestRAFPath()); 476 | 477 | println(TestClipLongPath()); 478 | } 479 | 480 | [Test] 481 | public void Test() 482 | { 483 | string test_dir = @"f:\st\dev\testdata"; 484 | if (!System.IO.Directory.Exists(test_dir)) 485 | Assert.Ignore(); 486 | using ( 487 | Stream fis = 488 | File.Open($@"{test_dir}\p.dbf" , 489 | FileMode.OpenOrCreate, 490 | FileAccess.ReadWrite)) 491 | using (var reader = new DBFReader(fis) 492 | { 493 | DataMemoLoc = Path.ChangeExtension($@"{test_dir}\p.dbf", "DBT") 494 | }) 495 | { 496 | var readValues = reader.NextRecord(); 497 | 498 | Console.WriteLine(readValues); 499 | } 500 | } 501 | 502 | 503 | [Test] 504 | public void test1() 505 | { 506 | Assert.DoesNotThrow(() => { new DBFWriter(); }, "Can't Create empty DBFWriter Object"); 507 | } 508 | 509 | [Test] 510 | public void test2() 511 | { 512 | Assert.DoesNotThrow(() => { new DBFField(); }, "Can't Create empty DBFWriter Object"); 513 | } 514 | 515 | 516 | [Test] 517 | public void test3() 518 | { 519 | WriteSample(); 520 | ReadSample(); 521 | } 522 | 523 | public void WriteSample([CallerMemberName] string name = null) 524 | { 525 | var field = new DBFField {Name = "F1", DataType = NativeDbType.Numeric}; 526 | var writer = new DBFWriter {Fields = new[] {field}}; 527 | writer.AddRecord(3); 528 | using ( 529 | Stream fos = 530 | File.Open(TestPath(name), 531 | FileMode.OpenOrCreate, 532 | FileAccess.ReadWrite)) 533 | { 534 | writer.Write(fos); 535 | } 536 | } 537 | 538 | 539 | public void ReadSample([CallerMemberName] string name = null) 540 | { 541 | using (var reader = new DBFReader(TestPath(name))) 542 | { 543 | Assert.That(reader.RecordCount, EqualTo(1)); 544 | } 545 | } 546 | } 547 | } 548 | --------------------------------------------------------------------------------