├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── CODE_OF_CONDUCT.md ├── DbfDataReader.sln ├── LICENSE ├── README.md ├── benchmarks.md ├── src └── DbfDataReader │ ├── BinaryReaderExtensions.cs │ ├── DbfColumn.cs │ ├── DbfColumnType.cs │ ├── DbfDataReader.cs │ ├── DbfDataReader.csproj │ ├── DbfDataReaderOptions.cs │ ├── DbfFileFormatException.cs │ ├── DbfHeader.cs │ ├── DbfMemo.cs │ ├── DbfMemoDbase3.cs │ ├── DbfMemoDbase4.cs │ ├── DbfMemoFoxPro.cs │ ├── DbfRecord.cs │ ├── DbfTable.cs │ ├── DbfValue.cs │ ├── DbfValueBoolean.cs │ ├── DbfValueCurrency.cs │ ├── DbfValueDate.cs │ ├── DbfValueDateTime.cs │ ├── DbfValueDecimal.cs │ ├── DbfValueDouble.cs │ ├── DbfValueFloat.cs │ ├── DbfValueInt.cs │ ├── DbfValueInt64.cs │ ├── DbfValueLong.cs │ ├── DbfValueMemo.cs │ ├── DbfValueNull.cs │ ├── DbfValueString.cs │ ├── DbfValueWideString.cs │ ├── Disposable.cs │ ├── EncodingProvider.cs │ ├── IDbfValue.cs │ └── Properties │ └── AssemblyInfo.cs └── test ├── DbfDataReader.Benchmarks ├── DbfDataReader.Benchmarks.csproj ├── DbfDataReaderBenchmarks.cs ├── DbfDataReaderExtensions.cs └── Program.cs ├── DbfDataReader.Tests ├── Dbase03NullCharTests.cs ├── Dbase03Tests.cs ├── Dbase30Tests.cs ├── Dbase31Tests.cs ├── Dbase8BTests.cs ├── DbaseF5Tests.cs ├── DbaseTests.cs ├── DbfDataReader.Tests.csproj ├── DbfDataReaderTests.cs ├── DbfMemo1251Tests.cs ├── DbfTableTests.cs ├── EsriShapefileTests.cs ├── FixtureHelpers.cs ├── FoxproCurrencyTests.cs ├── LargeCharacterColumnTests.cs ├── MS_KHDMTests.cs └── UseCultureAttribute.cs └── fixtures ├── FolderRoot.csv ├── FolderRoot.dbf ├── FolderRoot_summary.txt ├── INNAKLKVT20180904.dbf ├── INNAKLKVT20180904.dbt ├── MS__KHDM.DBF ├── MS__KHDM.csv ├── MS__KHDM_summary.txt ├── client.csv ├── client.dbf ├── client.dbt ├── client_summary.txt ├── cp1251.dbf ├── cp1251_summary.txt ├── dbase_03.csv ├── dbase_03.dbf ├── dbase_03_nullchar.csv ├── dbase_03_nullchar.dbf ├── dbase_03_nullchar_summary.txt ├── dbase_03_summary.txt ├── dbase_30.csv ├── dbase_30.dbf ├── dbase_30.fpt ├── dbase_30_summary.txt ├── dbase_31.csv ├── dbase_31.dbf ├── dbase_31_summary.txt ├── dbase_83.dbf ├── dbase_83.dbt ├── dbase_83_missing_memo.dbf ├── dbase_83_missing_memo_record_0.yml ├── dbase_83_record_0.yml ├── dbase_83_record_9.yml ├── dbase_83_schema.txt ├── dbase_83_summary.txt ├── dbase_8b.dbf ├── dbase_8b.dbt ├── dbase_8b_summary.txt ├── dbase_f5.csv ├── dbase_f5.dbf ├── dbase_f5.fpt ├── dbase_f5_summary.txt ├── foxpro_currency_01.csv ├── foxpro_currency_01.dbf ├── foxpro_currency_01_summary.txt ├── foxprodb ├── FOXPRO-DB-TEST.DBC ├── FOXPRO-DB-TEST.DCT ├── FOXPRO-DB-TEST.DCX ├── calls.CDX ├── calls.FPT ├── calls.dbf ├── contacts.CDX ├── contacts.FPT ├── contacts.dbf ├── setup.CDX ├── setup.dbf ├── types.CDX └── types.dbf ├── tl_2019_01_place.csv ├── tl_2019_01_place.dbf └── tl_2019_01_place_summary.txt /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: yellowfeather 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: nuget 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | ignore: 9 | - dependency-name: CsvHelper 10 | versions: 11 | - 21.3.0 12 | - 22.1.0 13 | - 22.1.1 14 | - 22.1.2 15 | - 25.0.0 16 | - 26.0.0 17 | - 26.0.1 18 | - 26.1.0 19 | - 27.0.0 20 | - 27.0.1 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | build: 13 | 14 | env: 15 | BUILD_CONFIG: 'Release' 16 | SOLUTION: 'DbfDataReader.sln' 17 | 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | 23 | - name: Setup .NET 24 | uses: actions/setup-dotnet@v1 25 | with: 26 | dotnet-version: 6.0.x 27 | 28 | - name: Build 29 | run: dotnet build $SOLUTION --configuration $BUILD_CONFIG 30 | 31 | - name: Run tests 32 | run: dotnet test /p:Configuration=$BUILD_CONFIG --no-restore --no-build --verbosity normal 33 | 34 | - name: Pack 35 | run: dotnet pack --output ./artifacts --configuration $BUILD_CONFIG --no-build 36 | 37 | - name: Push to MyGet 38 | if: github.ref == 'refs/heads/main' 39 | env: 40 | NUGET_URL: ${{ secrets.MYGET_URL }} 41 | NUGET_API_KEY: ${{ secrets.MYGET_API_KEY }} 42 | run: dotnet nuget push ./artifacts/**.nupkg --source $NUGET_URL --api-key $NUGET_API_KEY 43 | 44 | - name: Artifacts 45 | uses: actions/upload-artifact@v2 46 | with: 47 | name: artifacts 48 | path: artifacts/**/* 49 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*.*.*' 7 | 8 | jobs: 9 | build: 10 | 11 | env: 12 | BUILD_CONFIG: 'Release' 13 | SOLUTION: 'DbfDataReader.sln' 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | with: 20 | fetch-depth: 0 21 | 22 | - name: Setup .NET 23 | uses: actions/setup-dotnet@v1 24 | with: 25 | dotnet-version: 6.0.x 26 | 27 | - name: Build 28 | run: dotnet build $SOLUTION --configuration $BUILD_CONFIG 29 | 30 | - name: Run tests 31 | run: dotnet test /p:Configuration=$BUILD_CONFIG --no-restore --no-build --verbosity normal 32 | 33 | - name: Pack 34 | run: dotnet pack ./src/DbfDataReader --output ./artifacts --configuration $BUILD_CONFIG --no-build 35 | 36 | - name: Push to MyGet 37 | env: 38 | NUGET_URL: ${{ secrets.MYGET_URL }} 39 | NUGET_API_KEY: ${{ secrets.MYGET_API_KEY }} 40 | run: dotnet nuget push ./artifacts/**.nupkg --source $NUGET_URL --api-key $NUGET_API_KEY 41 | 42 | - name: Push to NuGet 43 | env: 44 | NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} 45 | run: dotnet nuget push ./artifacts/**.nupkg --source nuget.org --api-key $NUGET_API_KEY 46 | 47 | - name: Artifacts 48 | uses: actions/upload-artifact@v2 49 | with: 50 | name: artifacts 51 | path: artifacts/**/* -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #ignore outputs of project 2 | build*/ 3 | #src/SharpArch/CommonAssemblyInfo.cs 4 | Solutions/Ppt.Web/App_Data/error-*.xml 5 | #ignore thumbnails created by windows 6 | Thumbs.db 7 | #Ignore files build by Visual Studio 8 | *.obj 9 | *.exe 10 | *.pdb 11 | *.user 12 | *.aps 13 | *.pch 14 | *.vspscc 15 | *_i.c 16 | *_p.c 17 | *.ncb 18 | *.suo 19 | *.tlb 20 | *.tlh 21 | *.bak 22 | *.cache 23 | *.ilk 24 | *.log 25 | [Bb]in 26 | [Dd]ebug*/ 27 | *.lib 28 | *.sbr 29 | obj/ 30 | [Rr]elease*/ 31 | _ReSharper*/ 32 | *.dotCover 33 | [Tt]est[Rr]esult* 34 | #Ignore MonoDevelop files 35 | *.pidb 36 | *.userprefs 37 | *.DS_Store 38 | .svn 39 | *_mm_cache.bin 40 | $SSH_ENV 41 | Solutions/packages 42 | .vs 43 | project.lock.json 44 | project.fragment.lock.json 45 | artifacts/ 46 | **/Properties/launchSettings.json 47 | /test/DbfDataReader.Benchmarks/BenchmarkDotNet.Artifacts/results 48 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | // Use IntelliSense to find out which attributes exist for C# debugging 6 | // Use hover for the description of the existing attributes 7 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/test/DbfDataReader.Tests/bin/Debug/netcoreapp3.1/DbfDataReader.Tests.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}/test/DbfDataReader.Tests", 16 | // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "command": "dotnet", 6 | "args": [], 7 | "tasks": [ 8 | { 9 | "label": "build", 10 | "type": "shell", 11 | "command": "dotnet", 12 | "args": [ 13 | "build", 14 | "src/DbfDataReader", 15 | "test/DbfDataReader.Tests" 16 | ], 17 | "problemMatcher": "$msCompile", 18 | "group": "build" 19 | }, 20 | { 21 | "label": "test", 22 | "type": "shell", 23 | "command": "dotnet", 24 | "args": [ 25 | "test", 26 | "test/DbfDataReader.Tests" 27 | ], 28 | "problemMatcher": [], 29 | "group": "test" 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at enquiries@yellowfeather.co.uk. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /DbfDataReader.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31005.135 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{02A96866-FEA4-483E-A6FD-CB8660F51FD3}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8C65E3B7-6E4F-470A-9422-7B273FE6EBF6}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{6E1D36E2-2D05-4ACF-80B7-1AFB75A20839}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "fixtures", "fixtures", "{A58BB1E8-935E-4A73-B630-969EE5B4AF2F}" 13 | ProjectSection(SolutionItems) = preProject 14 | test\fixtures\cp1251.dbf = test\fixtures\cp1251.dbf 15 | test\fixtures\client.dbf = test\fixtures\client.dbf 16 | test\fixtures\client.dbt = test\fixtures\client.dbt 17 | test\fixtures\client.mdx = test\fixtures\client.mdx 18 | test\fixtures\cp1251_summary.txt = test\fixtures\cp1251_summary.txt 19 | test\fixtures\dbase_03.csv = test\fixtures\dbase_03.csv 20 | test\fixtures\dbase_03.dbf = test\fixtures\dbase_03.dbf 21 | test\fixtures\dbase_03_nullchar.csv = test\fixtures\dbase_03_nullchar.csv 22 | test\fixtures\dbase_03_nullchar.dbf = test\fixtures\dbase_03_nullchar.dbf 23 | test\fixtures\dbase_03_nullchar_summary.txt = test\fixtures\dbase_03_nullchar_summary.txt 24 | test\fixtures\dbase_03_summary.txt = test\fixtures\dbase_03_summary.txt 25 | test\fixtures\dbase_30.csv = test\fixtures\dbase_30.csv 26 | test\fixtures\dbase_30.dbf = test\fixtures\dbase_30.dbf 27 | test\fixtures\dbase_30.fpt = test\fixtures\dbase_30.fpt 28 | test\fixtures\dbase_30_summary.txt = test\fixtures\dbase_30_summary.txt 29 | test\fixtures\dbase_31.csv = test\fixtures\dbase_31.csv 30 | test\fixtures\dbase_31.dbf = test\fixtures\dbase_31.dbf 31 | test\fixtures\dbase_31_summary.txt = test\fixtures\dbase_31_summary.txt 32 | test\fixtures\dbase_83.dbf = test\fixtures\dbase_83.dbf 33 | test\fixtures\dbase_83.dbt = test\fixtures\dbase_83.dbt 34 | test\fixtures\dbase_83_missing_memo.dbf = test\fixtures\dbase_83_missing_memo.dbf 35 | test\fixtures\dbase_83_missing_memo_record_0.yml = test\fixtures\dbase_83_missing_memo_record_0.yml 36 | test\fixtures\dbase_83_record_0.yml = test\fixtures\dbase_83_record_0.yml 37 | test\fixtures\dbase_83_record_9.yml = test\fixtures\dbase_83_record_9.yml 38 | test\fixtures\dbase_83_schema.txt = test\fixtures\dbase_83_schema.txt 39 | test\fixtures\dbase_83_summary.txt = test\fixtures\dbase_83_summary.txt 40 | test\fixtures\dbase_8b.dbf = test\fixtures\dbase_8b.dbf 41 | test\fixtures\dbase_8b.dbt = test\fixtures\dbase_8b.dbt 42 | test\fixtures\dbase_8b_summary.txt = test\fixtures\dbase_8b_summary.txt 43 | test\fixtures\dbase_f5.csv = test\fixtures\dbase_f5.csv 44 | test\fixtures\dbase_f5.dbf = test\fixtures\dbase_f5.dbf 45 | test\fixtures\dbase_f5.fpt = test\fixtures\dbase_f5.fpt 46 | test\fixtures\dbase_f5_summary.txt = test\fixtures\dbase_f5_summary.txt 47 | test\fixtures\FolderRoot.csv = test\fixtures\FolderRoot.csv 48 | test\fixtures\FolderRoot.dbf = test\fixtures\FolderRoot.dbf 49 | test\fixtures\FolderRoot_summary.txt = test\fixtures\FolderRoot_summary.txt 50 | test\fixtures\foxpro_currency_01.csv = test\fixtures\foxpro_currency_01.csv 51 | test\fixtures\foxpro_currency_01.dbf = test\fixtures\foxpro_currency_01.dbf 52 | test\fixtures\foxpro_currency_01_summary.txt = test\fixtures\foxpro_currency_01_summary.txt 53 | test\fixtures\INNAKLKVT20180904.dbf = test\fixtures\INNAKLKVT20180904.dbf 54 | test\fixtures\INNAKLKVT20180904.dbt = test\fixtures\INNAKLKVT20180904.dbt 55 | test\fixtures\MS__KHDM.csv = test\fixtures\MS__KHDM.csv 56 | test\fixtures\MS__KHDM.DBF = test\fixtures\MS__KHDM.DBF 57 | test\fixtures\MS__KHDM_summary.txt = test\fixtures\MS__KHDM_summary.txt 58 | test\fixtures\tl_2019_01_place.csv = test\fixtures\tl_2019_01_place.csv 59 | test\fixtures\tl_2019_01_place.dbf = test\fixtures\tl_2019_01_place.dbf 60 | test\fixtures\tl_2019_01_place_summary.txt = test\fixtures\tl_2019_01_place_summary.txt 61 | EndProjectSection 62 | EndProject 63 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DbfDataReader", "src\DbfDataReader\DbfDataReader.csproj", "{40A3F4FC-21A9-48FC-8FBB-85B9AE7C3EC1}" 64 | EndProject 65 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DbfDataReader.Tests", "test\DbfDataReader.Tests\DbfDataReader.Tests.csproj", "{A0D45C13-CE45-444C-B5D3-FF9425BE0635}" 66 | EndProject 67 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DbfDataReader.Benchmarks", "test\DbfDataReader.Benchmarks\DbfDataReader.Benchmarks.csproj", "{8D20CC57-206C-47FE-BC3E-C80E198C3602}" 68 | EndProject 69 | Global 70 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 71 | Debug|Any CPU = Debug|Any CPU 72 | Release|Any CPU = Release|Any CPU 73 | EndGlobalSection 74 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 75 | {40A3F4FC-21A9-48FC-8FBB-85B9AE7C3EC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 76 | {40A3F4FC-21A9-48FC-8FBB-85B9AE7C3EC1}.Debug|Any CPU.Build.0 = Debug|Any CPU 77 | {40A3F4FC-21A9-48FC-8FBB-85B9AE7C3EC1}.Release|Any CPU.ActiveCfg = Release|Any CPU 78 | {40A3F4FC-21A9-48FC-8FBB-85B9AE7C3EC1}.Release|Any CPU.Build.0 = Release|Any CPU 79 | {A0D45C13-CE45-444C-B5D3-FF9425BE0635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 80 | {A0D45C13-CE45-444C-B5D3-FF9425BE0635}.Debug|Any CPU.Build.0 = Debug|Any CPU 81 | {A0D45C13-CE45-444C-B5D3-FF9425BE0635}.Release|Any CPU.ActiveCfg = Release|Any CPU 82 | {A0D45C13-CE45-444C-B5D3-FF9425BE0635}.Release|Any CPU.Build.0 = Release|Any CPU 83 | {8D20CC57-206C-47FE-BC3E-C80E198C3602}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 84 | {8D20CC57-206C-47FE-BC3E-C80E198C3602}.Debug|Any CPU.Build.0 = Debug|Any CPU 85 | {8D20CC57-206C-47FE-BC3E-C80E198C3602}.Release|Any CPU.ActiveCfg = Release|Any CPU 86 | {8D20CC57-206C-47FE-BC3E-C80E198C3602}.Release|Any CPU.Build.0 = Release|Any CPU 87 | EndGlobalSection 88 | GlobalSection(SolutionProperties) = preSolution 89 | HideSolutionNode = FALSE 90 | EndGlobalSection 91 | GlobalSection(NestedProjects) = preSolution 92 | {A58BB1E8-935E-4A73-B630-969EE5B4AF2F} = {6E1D36E2-2D05-4ACF-80B7-1AFB75A20839} 93 | {40A3F4FC-21A9-48FC-8FBB-85B9AE7C3EC1} = {02A96866-FEA4-483E-A6FD-CB8660F51FD3} 94 | {A0D45C13-CE45-444C-B5D3-FF9425BE0635} = {6E1D36E2-2D05-4ACF-80B7-1AFB75A20839} 95 | {8D20CC57-206C-47FE-BC3E-C80E198C3602} = {6E1D36E2-2D05-4ACF-80B7-1AFB75A20839} 96 | EndGlobalSection 97 | GlobalSection(ExtensibilityGlobals) = postSolution 98 | SolutionGuid = {4B7A9572-5B9E-4FDD-9BEC-4FBD8885C3F2} 99 | EndGlobalSection 100 | EndGlobal 101 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Yellow Feather Ltd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DbfDataReader 2 | 3 | [![CI](https://github.com/yellowfeather/DbfDataReader/actions/workflows/ci.yml/badge.svg)](https://github.com/yellowfeather/DbfDataReader/actions/workflows/ci.yml) 4 | [![NuGet](https://img.shields.io/nuget/dt/DbfDataReader.svg)](https://www.nuget.org/packages/DbfDataReader) 5 | [![NuGet](https://img.shields.io/nuget/vpre/DbfDataReader.svg)](https://www.nuget.org/packages/DbfDataReader) 6 | [![MyGet Build Status](https://www.myget.org/BuildSource/Badge/dbfdatareader?identifier=54ae0096-55d5-418c-8eb9-54a35df720fb)](https://www.myget.org/) 7 | 8 | DbfDataReader is a small fast .Net Core library for reading dBase, xBase, Clipper and FoxPro database files 9 | 10 | Usage, to get summary info: 11 | 12 | ```csharp 13 | var dbfPath = "path\\file.dbf"; 14 | using (var dbfTable = new DbfTable(dbfPath, Encoding.UTF8)) 15 | { 16 | var header = dbfTable.Header; 17 | 18 | var versionDescription = header.VersionDescription; 19 | var hasMemo = dbfTable.Memo != null; 20 | var recordCount = header.RecordCount; 21 | 22 | foreach (var dbfColumn in dbfTable.Columns) 23 | { 24 | var name = dbfColumn.ColumnName; 25 | var columnType = dbfColumn.ColumnType; 26 | var length = dbfColumn.Length; 27 | var decimalCount = dbfColumn.DecimalCount; 28 | } 29 | } 30 | ``` 31 | 32 | and to iterate over the rows: 33 | 34 | ```csharp 35 | var skipDeleted = true; 36 | 37 | var dbfPath = "path/file.dbf"; 38 | using (var dbfTable = new DbfTable(dbfPath, Encoding.UTF8)) 39 | { 40 | var dbfRecord = new DbfRecord(dbfTable); 41 | 42 | while (dbfTable.Read(dbfRecord)) 43 | { 44 | if (skipDeleted && dbfRecord.IsDeleted) 45 | { 46 | continue; 47 | } 48 | 49 | foreach (var dbfValue in dbfRecord.Values) 50 | { 51 | var stringValue = dbfValue.ToString(); 52 | var obj = dbfValue.GetValue(); 53 | } 54 | } 55 | } 56 | ``` 57 | 58 | There is also an implementation of DbDataReader: 59 | 60 | ```csharp 61 | var options = new DbfDataReaderOptions 62 | { 63 | SkipDeletedRecords = true 64 | // Encoding = EncodingProvider.GetEncoding(1252); 65 | }; 66 | 67 | var dbfPath = "path/file.dbf"; 68 | using (var dbfDataReader = new DbfDataReader(dbfPath, options)) 69 | { 70 | while (dbfDataReader.Read()) 71 | { 72 | var valueCol1 = dbfDataReader.GetString(0); 73 | var valueCol2 = dbfDataReader.GetDecimal(1); 74 | var valueCol3 = dbfDataReader.GetDateTime(2); 75 | var valueCol4 = dbfDataReader.GetInt32(3); 76 | } 77 | } 78 | ``` 79 | 80 | which also means you can bulk copy to MS SqlServer: 81 | 82 | ```csharp 83 | var options = new DbfDataReaderOptions 84 | { 85 | SkipDeletedRecords = true 86 | // Encoding = EncodingProvider.GetEncoding(1252); 87 | }; 88 | 89 | var dbfPath = "path/file.dbf"; 90 | using (var dbfDataReader = new DbfDataReader(dbfPath, options)) 91 | { 92 | using (var bulkCopy = new SqlBulkCopy(connection)) 93 | { 94 | bulkCopy.DestinationTableName = "DestinationTableName"; 95 | 96 | try 97 | { 98 | bulkCopy.WriteToServer(dbfDataReader); 99 | } 100 | catch (Exception ex) 101 | { 102 | Console.WriteLine($"Error importing: dbf file: '{dbfPath}', exception: {ex.Message}"); 103 | } 104 | } 105 | } 106 | ``` 107 | 108 | Used by 109 | 110 | - DbfBulkCopy 111 | 112 | Command line application to bulk copy from DBF files to MS SqlServer 113 | 114 | https://github.com/yellowfeather/DbfBulkCopy 115 | 116 | - dbf 117 | 118 | Command line utility to display DBF info and contents 119 | 120 | https://github.com/yellowfeather/dbf 121 | -------------------------------------------------------------------------------- /benchmarks.md: -------------------------------------------------------------------------------- 1 | # v0.5.6 2 | 3 | | Method | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | 4 | |-------- |---------:|----------:|----------:|---------:|------:|--------:|---------:|-------:|------:|-----------:| 5 | | Sylvan | 1.587 ms | 0.0310 ms | 0.0290 ms | 1.585 ms | 0.47 | 0.02 | 121.8750 | - | - | 750.47 KB | 6 | | NDbf | 3.310 ms | 0.0661 ms | 0.1558 ms | 3.363 ms | 1.00 | 0.00 | 213.2353 | - | - | 1309.15 KB | 7 | | DbfData | 5.914 ms | 0.1176 ms | 0.1997 ms | 5.975 ms | 1.79 | 0.11 | 710.2273 | 5.6818 | - | 4355.29 KB | 8 | 9 | // * Hints * 10 | Outliers 11 | DbfDataReaderBenchmarks.Sylvan: IterationTime=1.0000 s -> 2 outliers were removed, 3 outliers were detected (1.50 ms, 1.67 ms, 1.67 ms) 12 | DbfDataReaderBenchmarks.NDbf: IterationTime=1.0000 s -> 1 outlier was removed (3.97 ms) 13 | 14 | 15 | # v0.5.7 16 | 17 | | Method | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | 18 | |-------- |---------:|----------:|----------:|------:|--------:|---------:|-------:|------:|-----------:| 19 | | Sylvan | 1.505 ms | 0.0299 ms | 0.0674 ms | 0.46 | 0.03 | 121.9512 | 1.5244 | - | 750.47 KB | 20 | | NDbf | 3.300 ms | 0.0651 ms | 0.1301 ms | 1.00 | 0.00 | 211.8056 | - | - | 1309.15 KB | 21 | | DbfData | 6.362 ms | 0.1271 ms | 0.1463 ms | 1.92 | 0.08 | 906.2500 | 6.2500 | - | 5563.33 KB | 22 | 23 | // * Hints * 24 | Outliers 25 | DbfDataReaderBenchmarks.Sylvan: IterationTime=1.0000 s -> 3 outliers were removed (2.12 ms..2.18 ms) 26 | DbfDataReaderBenchmarks.NDbf: IterationTime=1.0000 s -> 2 outliers were removed (4.37 ms, 4.44 ms) 27 | 28 | -------------------------------------------------------------------------------- /src/DbfDataReader/BinaryReaderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | 5 | namespace DbfDataReader 6 | { 7 | internal static class BinaryReaderExtensions 8 | { 9 | private const char NullChar = '\0'; 10 | 11 | public static short ReadBigEndianInt16(this BinaryReader binaryReader) 12 | { 13 | var bytes = binaryReader.ReadBytes(2); 14 | Array.Reverse(bytes); 15 | return BitConverter.ToInt16(bytes, 0); 16 | } 17 | 18 | public static ushort ReadBigEndianUInt16(this BinaryReader binaryReader) 19 | { 20 | var bytes = binaryReader.ReadBytes(2); 21 | Array.Reverse(bytes); 22 | return BitConverter.ToUInt16(bytes, 0); 23 | } 24 | 25 | public static int ReadBigEndianInt32(this BinaryReader binaryReader) 26 | { 27 | var bytes = binaryReader.ReadBytes(4); 28 | Array.Reverse(bytes); 29 | return BitConverter.ToInt32(bytes, 0); 30 | } 31 | public static uint ReadBigEndianUInt32(this BinaryReader binaryReader) 32 | { 33 | var bytes = binaryReader.ReadBytes(4); 34 | Array.Reverse(bytes); 35 | return BitConverter.ToUInt32(bytes, 0); 36 | } 37 | 38 | public static string ReadString(this BinaryReader binaryReader, int fieldLength, Encoding encoding) 39 | { 40 | var chars = binaryReader.ReadBytes(fieldLength); 41 | if ((chars == null) || (chars.Length == 0)) 42 | { 43 | return null; 44 | } 45 | if (chars[0] == NullChar) 46 | { 47 | return null; 48 | } 49 | var value = encoding.GetString(chars); 50 | return value.Trim(NullChar, ' '); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/DbfDataReader/DbfColumn.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.Common; 3 | using System.Text; 4 | 5 | namespace DbfDataReader 6 | { 7 | public class DbfColumn : DbColumn 8 | { 9 | public const int DbfColumnSize = 32; 10 | 11 | public DbfColumn(ReadOnlySpan bytes, int start, int ordinal, Encoding encoding) 12 | { 13 | Start = start; 14 | ColumnOrdinal = ordinal; 15 | Read(bytes, encoding); 16 | } 17 | 18 | public DbfColumnType ColumnType { get; private set; } 19 | public int Start { get; } 20 | public int Length { get; private set; } 21 | public int DecimalCount { get; private set; } 22 | 23 | private void Read(ReadOnlySpan bytes, Encoding encoding) 24 | { 25 | var rawName = encoding.GetString(bytes.Slice(0, 11)); 26 | var nullIdx = rawName.IndexOf((char)0); 27 | if (nullIdx >= 0) 28 | { 29 | rawName = rawName.Substring(0, nullIdx); // trim off everything past & including the first NUL byte 30 | } 31 | ColumnName = rawName; 32 | 33 | ColumnType = (DbfColumnType) bytes[11]; 34 | 35 | // ignore field data address 36 | 37 | var length = bytes[16]; 38 | var decimalCount = bytes[17]; 39 | 40 | if (ColumnType == DbfColumnType.Character) 41 | { 42 | Length = BitConverter.ToInt16(bytes.Slice(16, 2)); 43 | DecimalCount = 0; 44 | } 45 | else if (ColumnType == DbfColumnType.WideCharacter) 46 | { 47 | Length = BitConverter.ToInt16(bytes.Slice(16, 2)); 48 | DecimalCount = 0; 49 | } 50 | else 51 | { 52 | Length = length; 53 | DecimalCount = decimalCount; 54 | } 55 | 56 | DataType = GetDataType(ColumnType); 57 | DataTypeName = DataType.ToString(); 58 | 59 | // skip the reserved bytes: 60 | // - Int16: reserved1 61 | // - Byte: workarea_id 62 | // - Byte: reserved2 63 | // - Byte: reserved3 64 | // - Byte: set_fields_flag 65 | // - String7: reserved4 66 | // - Byte: index_field_flag 67 | } 68 | 69 | private Type GetDataType(DbfColumnType columnType) 70 | { 71 | switch (columnType) 72 | { 73 | case DbfColumnType.Number: 74 | return DecimalCount == 0 ? typeof(int) : typeof(decimal); 75 | case DbfColumnType.SignedLong: 76 | return typeof(long); 77 | case DbfColumnType.Float: 78 | return typeof(float); 79 | case DbfColumnType.Currency: 80 | return typeof(decimal); 81 | case DbfColumnType.Date: 82 | return typeof(DateTime); 83 | case DbfColumnType.DateTime: 84 | return typeof(DateTime); 85 | case DbfColumnType.Boolean: 86 | return typeof(bool); 87 | case DbfColumnType.Memo: 88 | return typeof(string); 89 | case DbfColumnType.Double: 90 | return typeof(double); 91 | case DbfColumnType.General: 92 | case DbfColumnType.Character: 93 | case DbfColumnType.WideCharacter: 94 | return typeof(string); 95 | default: 96 | return typeof(object); 97 | } 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfColumnType.cs: -------------------------------------------------------------------------------- 1 | namespace DbfDataReader 2 | { 3 | public enum DbfColumnType 4 | { 5 | Number = 'N', 6 | SignedLong = 'I', 7 | Float = 'F', 8 | Currency = 'Y', 9 | Date = 'D', 10 | DateTime = 'T', 11 | Boolean = 'L', 12 | Memo = 'M', 13 | Double = 'B', 14 | General = 'G', 15 | Character = 'C', 16 | WideCharacter = 'W' 17 | } 18 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfDataReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.ObjectModel; 4 | using System.Data; 5 | using System.Data.Common; 6 | using System.IO; 7 | using System.Linq; 8 | 9 | namespace DbfDataReader 10 | { 11 | public class DbfDataReader : DbDataReader, IDbColumnSchemaGenerator 12 | { 13 | private readonly DbfDataReaderOptions _options; 14 | 15 | public DbfDataReader(string path) 16 | : this(path, new DbfDataReaderOptions()) 17 | { 18 | } 19 | 20 | public DbfDataReader(string path, DbfDataReaderOptions options) 21 | { 22 | _options = options; 23 | DbfTable = new DbfTable(path, options.Encoding); 24 | DbfRecord = new DbfRecord(DbfTable); 25 | } 26 | 27 | public DbfDataReader(Stream stream, DbfDataReaderOptions options) 28 | { 29 | _options = options; 30 | DbfTable = new DbfTable(stream, options.Encoding); 31 | DbfRecord = new DbfRecord(DbfTable); 32 | } 33 | 34 | public DbfDataReader(Stream stream, Stream memoStream, DbfDataReaderOptions options) 35 | { 36 | _options = options; 37 | DbfTable = new DbfTable(stream, memoStream, options.Encoding); 38 | DbfRecord = new DbfRecord(DbfTable); 39 | } 40 | 41 | public DbfTable DbfTable { get; private set; } 42 | 43 | public DbfRecord DbfRecord { get; private set; } 44 | 45 | public override void Close() 46 | { 47 | try 48 | { 49 | DbfTable?.Close(); 50 | } 51 | finally 52 | { 53 | DbfTable = null; 54 | DbfRecord = null; 55 | } 56 | } 57 | 58 | protected override void Dispose(bool disposing) 59 | { 60 | if (disposing) 61 | { 62 | Close(); 63 | } 64 | } 65 | 66 | public DbfRecord ReadRecord() 67 | { 68 | DbfRecord dbfRecord; 69 | bool skip; 70 | do 71 | { 72 | dbfRecord = DbfTable.ReadRecord(); 73 | if (dbfRecord == null) 74 | break; 75 | 76 | skip = _options.SkipDeletedRecords && DbfRecord.IsDeleted; 77 | } while (skip); 78 | 79 | return dbfRecord; 80 | } 81 | 82 | public T GetValue(int ordinal) 83 | { 84 | return DbfRecord.GetValue(ordinal); 85 | } 86 | 87 | public T? GetNullableValue(int ordinal) where T : struct 88 | { 89 | return GetValue(ordinal); 90 | } 91 | 92 | public override bool GetBoolean(int ordinal) 93 | { 94 | return (bool) GetValue(ordinal); 95 | } 96 | 97 | public override byte GetByte(int ordinal) 98 | { 99 | return GetValue(ordinal); 100 | } 101 | 102 | public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length) 103 | { 104 | throw new NotImplementedException(); 105 | } 106 | 107 | public override char GetChar(int ordinal) 108 | { 109 | return GetValue(ordinal); 110 | } 111 | 112 | public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length) 113 | { 114 | throw new NotImplementedException(); 115 | } 116 | 117 | public override string GetDataTypeName(int ordinal) 118 | { 119 | throw new NotImplementedException(); 120 | } 121 | 122 | public override DateTime GetDateTime(int ordinal) 123 | { 124 | return GetValue(ordinal); 125 | } 126 | 127 | public override decimal GetDecimal(int ordinal) 128 | { 129 | return GetValue(ordinal); 130 | } 131 | 132 | public override double GetDouble(int ordinal) 133 | { 134 | return GetValue(ordinal); 135 | } 136 | 137 | public override IEnumerator GetEnumerator() 138 | { 139 | throw new NotImplementedException(); 140 | } 141 | 142 | public override bool NextResult() 143 | { 144 | return false; 145 | } 146 | 147 | public override bool Read() 148 | { 149 | bool result; 150 | bool skip; 151 | do 152 | { 153 | result = DbfTable.Read(DbfRecord); 154 | if (!result) 155 | break; 156 | 157 | skip = _options.SkipDeletedRecords && DbfRecord.IsDeleted; 158 | } while (skip); 159 | 160 | return result; 161 | } 162 | 163 | public override int Depth => throw new NotImplementedException(); 164 | 165 | public override bool IsClosed => DbfTable.IsClosed; 166 | 167 | public override int RecordsAffected => throw new NotImplementedException(); 168 | 169 | public override object this[string name] 170 | { 171 | get 172 | { 173 | var ordinal = GetOrdinal(name); 174 | return GetValue(ordinal); 175 | } 176 | } 177 | 178 | public override object this[int ordinal] => GetValue(ordinal); 179 | 180 | public override int FieldCount => DbfTable.Columns.Count; 181 | 182 | public override bool HasRows => DbfTable.Header.RecordCount > 0; 183 | 184 | public override bool IsDBNull(int ordinal) 185 | { 186 | var value = GetValue(ordinal); 187 | return value == null; 188 | } 189 | 190 | public override int GetValues(object[] values) 191 | { 192 | throw new NotImplementedException(); 193 | } 194 | 195 | public override object GetValue(int ordinal) 196 | { 197 | return DbfRecord.GetValue(ordinal); 198 | } 199 | 200 | public override string GetString(int ordinal) 201 | { 202 | return DbfRecord.GetStringValue(ordinal); 203 | } 204 | 205 | public override int GetOrdinal(string name) 206 | { 207 | var ordinal = 0; 208 | 209 | foreach (var dbfColumn in DbfTable.Columns) 210 | { 211 | if (dbfColumn.ColumnName == name) return ordinal; 212 | ordinal++; 213 | } 214 | ordinal = 0; 215 | foreach (var dbfColumn in DbfTable.Columns) 216 | { 217 | if (String.Equals(dbfColumn.ColumnName,name,StringComparison.OrdinalIgnoreCase)) return ordinal; 218 | ordinal++; 219 | } 220 | 221 | throw new IndexOutOfRangeException(); 222 | } 223 | 224 | public override string GetName(int ordinal) 225 | { 226 | var dbfColumn = DbfTable.Columns[ordinal]; 227 | return dbfColumn.ColumnName; 228 | } 229 | 230 | public override long GetInt64(int ordinal) 231 | { 232 | return GetValue(ordinal); 233 | } 234 | 235 | public override int GetInt32(int ordinal) 236 | { 237 | return GetValue(ordinal); 238 | } 239 | 240 | public override short GetInt16(int ordinal) 241 | { 242 | return GetValue(ordinal); 243 | } 244 | 245 | public override Guid GetGuid(int ordinal) 246 | { 247 | throw new NotImplementedException(); 248 | } 249 | 250 | public override float GetFloat(int ordinal) 251 | { 252 | return GetValue(ordinal); 253 | } 254 | 255 | public override Type GetFieldType(int ordinal) 256 | { 257 | return DbfRecord.GetFieldType(ordinal); 258 | } 259 | 260 | public ReadOnlyCollection GetColumnSchema() 261 | { 262 | var columns = DbfTable.Columns.Select(c => c as DbColumn).ToList(); 263 | return columns.AsReadOnly(); 264 | } 265 | 266 | public override DataTable GetSchemaTable() 267 | { 268 | var columnSchema = GetColumnSchema(); 269 | return GetSchemaTable(columnSchema); 270 | } 271 | 272 | public static DataTable GetSchemaTable(ReadOnlyCollection columnSchema) 273 | { 274 | var table = new DataTable("SchemaTable") 275 | { 276 | Columns = 277 | { 278 | new DataColumn(SchemaTableColumn.ColumnName, typeof(string)), 279 | new DataColumn(SchemaTableColumn.ColumnOrdinal, typeof(int)), 280 | new DataColumn(SchemaTableColumn.ColumnSize, typeof(int)), 281 | new DataColumn(SchemaTableColumn.NumericPrecision, typeof(short)), 282 | new DataColumn(SchemaTableColumn.NumericScale, typeof(short)), 283 | new DataColumn(SchemaTableColumn.DataType, typeof(Type)), 284 | new DataColumn(SchemaTableColumn.AllowDBNull, typeof(bool)), 285 | 286 | new DataColumn(SchemaTableColumn.BaseColumnName, typeof(string)), 287 | new DataColumn(SchemaTableColumn.BaseSchemaName, typeof(string)), 288 | new DataColumn(SchemaTableColumn.BaseTableName, typeof(string)), 289 | 290 | new DataColumn(SchemaTableColumn.IsAliased, typeof(bool)), 291 | new DataColumn(SchemaTableColumn.IsExpression, typeof(bool)), 292 | new DataColumn(SchemaTableColumn.IsKey, typeof(bool)), 293 | new DataColumn(SchemaTableColumn.IsLong, typeof(bool)), 294 | new DataColumn(SchemaTableColumn.IsUnique, typeof(bool)), 295 | 296 | new DataColumn(SchemaTableColumn.ProviderType, typeof(int)), 297 | new DataColumn(SchemaTableColumn.NonVersionedProviderType, typeof(int)), 298 | } 299 | }; 300 | 301 | object dbNull = DBNull.Value; 302 | foreach (var column in columnSchema) 303 | { 304 | var row = table.NewRow(); 305 | row[0] = column.ColumnName ?? dbNull; 306 | row[1] = column.ColumnOrdinal ?? dbNull; 307 | row[2] = column.ColumnSize ?? dbNull; 308 | row[3] = column.NumericPrecision ?? dbNull; 309 | row[4] = column.NumericScale ?? dbNull; 310 | row[5] = column.DataType ?? dbNull; 311 | row[6] = column.AllowDBNull ?? dbNull; 312 | 313 | row[7] = column.BaseColumnName ?? dbNull; 314 | row[8] = column.BaseSchemaName ?? dbNull; 315 | row[9] = column.BaseTableName ?? dbNull; 316 | 317 | row[10] = column.IsAliased ?? dbNull; 318 | row[11] = column.IsExpression ?? dbNull; 319 | row[12] = column.IsKey ?? dbNull; 320 | row[13] = column.IsLong ?? dbNull; 321 | row[14] = column.IsUnique ?? dbNull; 322 | 323 | var code = (int)Type.GetTypeCode(column.DataType); 324 | row[15] = code; 325 | row[16] = code; 326 | 327 | table.Rows.Add(row); 328 | } 329 | 330 | return table; 331 | } 332 | } 333 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfDataReader.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | DbfDataReader is a small fast .Net Core library for reading dBase, xBase, Clipper and FoxPro database files 5 | Copyright Chris Richards 6 | Chris Richards 7 | net6.0;netstandard2.1 8 | DbfDataReader 9 | DbfDataReader 10 | dbf;dBase;xBase;Clipper;FoxPro 11 | https://github.com/yellowfeather/DbfDataReader 12 | LICENSE 13 | README.md 14 | git 15 | git://github.com/yellowfeather/DbfDataReader 16 | true 17 | true 18 | snupkg 19 | true 20 | v 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/DbfDataReader/DbfDataReaderOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace DbfDataReader 4 | { 5 | public class DbfDataReaderOptions 6 | { 7 | public DbfDataReaderOptions() 8 | { 9 | SkipDeletedRecords = false; 10 | Encoding = null; 11 | } 12 | 13 | public bool SkipDeletedRecords { get; set; } 14 | public Encoding Encoding { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfFileFormatException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DbfDataReader 4 | { 5 | public class DbfFileFormatException : Exception 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfHeader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace DbfDataReader 5 | { 6 | public class DbfHeader 7 | { 8 | public const int DbfHeaderSize = 32; 9 | 10 | public DbfHeader(Stream stream) 11 | { 12 | var buffer = new byte[DbfHeaderSize]; 13 | stream.Read(buffer, 0, DbfHeaderSize); 14 | var span = new ReadOnlySpan(buffer); 15 | Read(span); 16 | } 17 | 18 | public int Version { get; private set; } 19 | 20 | public DateTime UpdatedAt { get; private set; } 21 | public int HeaderLength { get; private set; } 22 | public int RecordLength { get; private set; } 23 | public long RecordCount { get; private set; } 24 | public byte LanguageDriver { get; private set; } 25 | 26 | public string VersionDescription 27 | { 28 | get 29 | { 30 | string description; 31 | switch (Version) 32 | { 33 | case 0x02: 34 | description = "FoxPro"; 35 | break; 36 | case 0x03: 37 | description = "dBase III without memo file"; 38 | break; 39 | case 0x04: 40 | description = "dBase IV without memo file"; 41 | break; 42 | case 0x05: 43 | description = "dBase V without memo file"; 44 | break; 45 | case 0x07: 46 | description = "Visual Objects 1.x"; 47 | break; 48 | case 0x30: 49 | description = "Visual FoxPro"; 50 | break; 51 | case 0x31: 52 | description = "Visual FoxPro with AutoIncrement field"; 53 | break; 54 | case 0x43: 55 | description = "dBASE IV SQL table files, no memo"; 56 | break; 57 | case 0x63: 58 | description = "dBASE IV SQL system files, no memo"; 59 | break; 60 | case 0x7b: 61 | description = "dBase IV with memo file"; 62 | break; 63 | case 0x83: 64 | description = "dBase III with memo file"; 65 | break; 66 | case 0x87: 67 | description = "Visual Objects 1.x with memo file"; 68 | break; 69 | case 0x8b: 70 | description = "dBase IV with memo file"; 71 | break; 72 | case 0x8e: 73 | description = "dBase IV with SQL table"; 74 | break; 75 | case 0xcb: 76 | description = "dBASE IV SQL table files, with memo"; 77 | break; 78 | case 0xf5: 79 | description = "FoxPro with memo file"; 80 | break; 81 | case 0xfb: 82 | description = "FoxPro without memo file"; 83 | break; 84 | default: 85 | description = "Unknown"; 86 | break; 87 | } 88 | 89 | return description; 90 | } 91 | } 92 | 93 | public bool IsFoxPro => Version == 0x30 || Version == 0x31 || Version == 0xf5 || Version == 0xfb; 94 | 95 | public void Read(ReadOnlySpan bytes) 96 | { 97 | Version = bytes[0]; 98 | 99 | var year = bytes[1]; 100 | var month = bytes[2]; 101 | var day = bytes[3]; 102 | 103 | UpdatedAt = new DateTime(year + 1900, month, day); 104 | 105 | RecordCount = BitConverter.ToUInt32(bytes[4..]); 106 | HeaderLength = BitConverter.ToUInt16(bytes[8..]); 107 | RecordLength = BitConverter.ToUInt16(bytes[10..]); 108 | 109 | // See https://www.clicketyclick.dk/databases/xbase/format/dbf.html 110 | 111 | // 12 - 13 - reserved 112 | // 14 - incomplete transaction 113 | // 15 - encryption flag 114 | // 16 - 19 - free record thread 115 | // 20 - 27 - reserved for multi-user dbase 116 | // 28 - MDX flag 117 | 118 | LanguageDriver = bytes[29]; 119 | 120 | // 30 - 31 - reserved 121 | } 122 | } 123 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfMemo.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | 4 | namespace DbfDataReader 5 | { 6 | public abstract class DbfMemo : Disposable 7 | { 8 | protected const int BlockHeaderSize = 8; 9 | protected const int DefaultBlockSize = 512; 10 | 11 | protected BinaryReader BinaryReader; 12 | 13 | protected DbfMemo(string path) 14 | : this(path, EncodingProvider.GetEncoding(1252)) 15 | { 16 | } 17 | 18 | protected DbfMemo(string path, Encoding encoding) 19 | { 20 | if (!File.Exists(path)) throw new FileNotFoundException(); 21 | 22 | Path = path; 23 | CurrentEncoding = encoding; 24 | 25 | var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); 26 | BinaryReader = new BinaryReader(stream, encoding, false); 27 | } 28 | 29 | protected DbfMemo(Stream stream, Encoding encoding) 30 | { 31 | Path = string.Empty; 32 | CurrentEncoding = encoding; 33 | 34 | BinaryReader = new BinaryReader(stream, encoding, true); 35 | } 36 | 37 | public Encoding CurrentEncoding { get; set; } 38 | 39 | public virtual int BlockSize => DefaultBlockSize; 40 | 41 | public string Path { get; set; } 42 | 43 | public void Close() 44 | { 45 | Dispose(true); 46 | } 47 | 48 | protected override void Dispose(bool disposing) 49 | { 50 | try 51 | { 52 | if (!disposing) return; 53 | BinaryReader?.Dispose(); 54 | } 55 | finally 56 | { 57 | BinaryReader = null; 58 | } 59 | } 60 | 61 | public abstract string BuildMemo(long startBlock); 62 | 63 | public string Get(long startBlock) 64 | { 65 | return startBlock <= 0 ? string.Empty : BuildMemo(startBlock); 66 | } 67 | 68 | public long Offset(long startBlock) 69 | { 70 | return startBlock * BlockSize; 71 | } 72 | 73 | public int ContentSize(int memoSize) 74 | { 75 | return memoSize - BlockSize + BlockHeaderSize; 76 | } 77 | 78 | public int BlockContentSize() 79 | { 80 | return BlockSize + BlockHeaderSize; 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfMemoDbase3.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | 4 | namespace DbfDataReader 5 | { 6 | public class DbfMemoDbase3 : DbfMemo 7 | { 8 | public DbfMemoDbase3(string path) : base(path) 9 | { 10 | } 11 | 12 | public DbfMemoDbase3(string path, Encoding encoding) : base(path, encoding) 13 | { 14 | } 15 | 16 | public DbfMemoDbase3(Stream stream, Encoding encoding) : base(stream, encoding) 17 | { 18 | } 19 | 20 | public override string BuildMemo(long startBlock) 21 | { 22 | var offset = Offset(startBlock); 23 | BinaryReader.BaseStream.Seek(offset, SeekOrigin.Begin); 24 | 25 | var finished = false; 26 | var stringBuilder = new StringBuilder(); 27 | 28 | do 29 | { 30 | var block = BinaryReader.ReadString(DefaultBlockSize, CurrentEncoding); 31 | if ((block == null) || (block.Length == 0)) 32 | { 33 | break; 34 | } 35 | stringBuilder.Append(block); 36 | 37 | if (block.Length >= DefaultBlockSize) finished = true; 38 | } while (!finished); 39 | 40 | var value = stringBuilder.ToString(); 41 | var nullIdx = value.IndexOf((char)0); 42 | if (nullIdx >= 0) 43 | { 44 | value = value.Substring(0, nullIdx); // trim off everything past & including the first NUL byte 45 | } 46 | value = value.TrimEnd(' '); 47 | return value; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/DbfDataReader/DbfMemoDbase4.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | 4 | namespace DbfDataReader 5 | { 6 | public class DbfMemoDbase4 : DbfMemo 7 | { 8 | public DbfMemoDbase4(string path) : base(path) 9 | { 10 | } 11 | 12 | public DbfMemoDbase4(string path, Encoding encoding) : base(path, encoding) 13 | { 14 | } 15 | 16 | public DbfMemoDbase4(Stream stream, Encoding encoding) : base(stream, encoding) 17 | { 18 | } 19 | 20 | public override string BuildMemo(long startBlock) 21 | { 22 | var offset = Offset(startBlock); 23 | BinaryReader.BaseStream.Seek(offset, SeekOrigin.Begin); 24 | // Read the bytes 4-7 as length of the memo field 25 | BinaryReader.BaseStream.Seek(offset + 4, SeekOrigin.Begin); 26 | var memoLength = BinaryReader.ReadInt32() - 8; 27 | // Set at beginning of memo 28 | BinaryReader.BaseStream.Seek(offset + 8, SeekOrigin.Begin); 29 | var stringBuilder = new StringBuilder(); 30 | var block = BinaryReader.ReadString(memoLength, CurrentEncoding); 31 | stringBuilder.Append(block); 32 | return stringBuilder.ToString(); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfMemoFoxPro.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | 4 | namespace DbfDataReader 5 | { 6 | public class DbfMemoFoxPro : DbfMemo 7 | { 8 | public DbfMemoFoxPro(string path) : this(path, EncodingProvider.GetEncoding(1252)) 9 | { 10 | } 11 | 12 | public DbfMemoFoxPro(string path, Encoding encoding) : base(path, encoding) 13 | { 14 | BlockSize = CalculateBlockSize(); 15 | } 16 | 17 | public DbfMemoFoxPro(Stream stream, Encoding encoding) : base(stream, encoding) 18 | { 19 | BlockSize = CalculateBlockSize(); 20 | } 21 | 22 | public override int BlockSize { get; } 23 | 24 | private int CalculateBlockSize() 25 | { 26 | BinaryReader.BaseStream.Seek(0, SeekOrigin.Begin); 27 | 28 | BinaryReader.ReadUInt32(); // next block 29 | BinaryReader.ReadUInt16(); // unused 30 | return BinaryReader.ReadBigEndianInt16(); 31 | } 32 | 33 | public override string BuildMemo(long startBlock) 34 | { 35 | var offset = Offset(startBlock); 36 | BinaryReader.BaseStream.Seek(offset, SeekOrigin.Begin); 37 | 38 | var blockType = BinaryReader.ReadBigEndianInt32(); 39 | var memoLength = BinaryReader.ReadBigEndianInt32(); 40 | 41 | if (blockType != 1 || memoLength == 0) return string.Empty; 42 | 43 | var value = BinaryReader.ReadString(memoLength, CurrentEncoding); 44 | if (value != null) 45 | { 46 | var nullIdx = value.IndexOf((char)0); 47 | if (nullIdx >= 0) 48 | { 49 | value = value.Substring(0, nullIdx); // trim off everything past & including the first NUL byte 50 | } 51 | value = value.TrimEnd(' '); 52 | } 53 | return value; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/DbfDataReader/DbfRecord.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.SqlTypes; 4 | using System.IO; 5 | using System.Text; 6 | 7 | namespace DbfDataReader 8 | { 9 | public class DbfRecord 10 | { 11 | private const byte EndOfFile = 0x1a; 12 | 13 | private readonly Encoding _encoding; 14 | private readonly int _recordLength; 15 | private readonly byte[] _buffer; 16 | 17 | public DbfRecord(DbfTable dbfTable) 18 | { 19 | _encoding = dbfTable.CurrentEncoding; 20 | _recordLength = dbfTable.Header.RecordLength; 21 | _buffer = new byte[_recordLength]; 22 | 23 | Values = new List(); 24 | 25 | foreach (var dbfColumn in dbfTable.Columns) 26 | { 27 | var dbfValue = CreateDbfValue(dbfColumn, dbfTable.Memo); 28 | Values.Add(dbfValue); 29 | } 30 | } 31 | 32 | public bool IsDeleted { get; private set; } 33 | 34 | public IList Values { get; set; } 35 | 36 | private IDbfValue CreateDbfValue(DbfColumn dbfColumn, DbfMemo memo) 37 | { 38 | IDbfValue value; 39 | 40 | switch (dbfColumn.ColumnType) 41 | { 42 | case DbfColumnType.Number: 43 | if (dbfColumn.DecimalCount == 0) { 44 | if (dbfColumn.Length < 10) { 45 | value = new DbfValueInt(dbfColumn.Start, dbfColumn.Length); 46 | } 47 | else { 48 | value = new DbfValueInt64(dbfColumn.Start, dbfColumn.Length); 49 | } 50 | } 51 | else 52 | value = new DbfValueDecimal(dbfColumn.Start, dbfColumn.Length, dbfColumn.DecimalCount); 53 | break; 54 | case DbfColumnType.SignedLong: 55 | value = new DbfValueLong(dbfColumn.Start, dbfColumn.Length); 56 | break; 57 | case DbfColumnType.Float: 58 | value = new DbfValueFloat(dbfColumn.Start, dbfColumn.Length, dbfColumn.DecimalCount); 59 | break; 60 | case DbfColumnType.Currency: 61 | value = new DbfValueCurrency(dbfColumn.Start, dbfColumn.Length, dbfColumn.DecimalCount); 62 | break; 63 | case DbfColumnType.Date: 64 | value = new DbfValueDate(dbfColumn.Start, dbfColumn.Length); 65 | break; 66 | case DbfColumnType.DateTime: 67 | value = new DbfValueDateTime(dbfColumn.Start, dbfColumn.Length); 68 | break; 69 | case DbfColumnType.Boolean: 70 | value = new DbfValueBoolean(dbfColumn.Start, dbfColumn.Length); 71 | break; 72 | case DbfColumnType.Memo: 73 | value = new DbfValueMemo(dbfColumn.Start, dbfColumn.Length, memo, _encoding); 74 | break; 75 | case DbfColumnType.Double: 76 | value = new DbfValueDouble(dbfColumn.Start, dbfColumn.Length, dbfColumn.DecimalCount); 77 | break; 78 | case DbfColumnType.General: 79 | case DbfColumnType.Character: 80 | value = new DbfValueString(dbfColumn.Start, dbfColumn.Length, _encoding); 81 | break; 82 | case DbfColumnType.WideCharacter: 83 | value = new DbfValueWideString(dbfColumn.Start, dbfColumn.Length); 84 | break; 85 | default: 86 | value = new DbfValueNull(dbfColumn.Start, dbfColumn.Length); 87 | break; 88 | } 89 | 90 | return value; 91 | } 92 | 93 | public bool Read(Stream stream) 94 | { 95 | if (stream.Position == stream.Length) return false; 96 | 97 | try 98 | { 99 | var read = stream.Read(_buffer, 0, _recordLength); 100 | if (read <= 0) 101 | return false; 102 | while (read < _recordLength) 103 | { 104 | var r = stream.Read(_buffer, read, _recordLength - read); 105 | if (r == 0) 106 | return false; 107 | read += r; 108 | } 109 | var span = new ReadOnlySpan(_buffer); 110 | 111 | var value = span[0]; 112 | if (value == EndOfFile) return false; 113 | 114 | IsDeleted = value == 0x2A; 115 | 116 | foreach (var dbfValue in Values) 117 | { 118 | var slice = span.Slice(dbfValue.Start, dbfValue.Length); 119 | dbfValue.Read(slice); 120 | } 121 | return true; 122 | } 123 | catch (EndOfStreamException) 124 | { 125 | return false; 126 | } 127 | } 128 | 129 | public object GetValue(int ordinal) 130 | { 131 | var dbfValue = Values[ordinal]; 132 | return dbfValue.GetValue(); 133 | } 134 | 135 | public T GetValue(int ordinal) 136 | { 137 | var dbfValue = Values[ordinal]; 138 | try 139 | { 140 | var value = dbfValue.GetValue(); 141 | if (value is null) 142 | throw new SqlNullValueException($"Data is Null. This method or property cannot be called on Null values. Ordinal {ordinal}"); 143 | return (T) value; 144 | } 145 | catch (InvalidCastException) 146 | { 147 | throw new InvalidCastException( 148 | $"Unable to cast object of type '{dbfValue.GetValue().GetType().FullName}' to type '{typeof(T).FullName}' at ordinal '{ordinal}'."); 149 | } 150 | } 151 | 152 | public string GetStringValue(int ordinal) 153 | { 154 | var dbfValue = Values[ordinal]; 155 | try 156 | { 157 | return (string) dbfValue.GetValue(); 158 | } 159 | catch (InvalidCastException) 160 | { 161 | throw new InvalidCastException( 162 | $"Unable to cast object of type '{dbfValue.GetValue().GetType().FullName}' to type '{typeof(string).FullName}' at ordinal '{ordinal}'."); 163 | } 164 | } 165 | 166 | public Type GetFieldType(int ordinal) 167 | { 168 | var dbfValue = Values[ordinal]; 169 | return dbfValue.GetFieldType(); 170 | } 171 | } 172 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfTable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace DbfDataReader 7 | { 8 | public class DbfTable : Disposable 9 | { 10 | private const byte Terminator = 0x0d; 11 | 12 | public DbfTable(string path, Encoding encoding = null) 13 | { 14 | if (!File.Exists(path)) throw new FileNotFoundException(); 15 | 16 | Path = path; 17 | CurrentEncoding = encoding; 18 | 19 | Stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); 20 | 21 | Init(); 22 | 23 | var memoPath = MemoPath(); 24 | if (!string.IsNullOrEmpty(memoPath)) Memo = CreateMemo(memoPath); 25 | } 26 | 27 | public DbfTable(Stream stream, Encoding encoding = null) 28 | : this(stream, null, encoding) 29 | { 30 | } 31 | 32 | public DbfTable(Stream stream, Stream memoStream, Encoding encoding = null) 33 | { 34 | Path = string.Empty; 35 | CurrentEncoding = encoding; 36 | Stream = stream; 37 | 38 | Init(); 39 | 40 | if (memoStream != null) 41 | Memo = CreateMemo(memoStream); 42 | } 43 | 44 | private void Init() 45 | { 46 | Header = new DbfHeader(Stream); 47 | CurrentEncoding ??= EncodingProvider.GetEncoding(Header.LanguageDriver); 48 | Columns = ReadColumns(Stream); 49 | } 50 | 51 | public string Path { get; } 52 | 53 | public Encoding CurrentEncoding { get; private set; } 54 | 55 | public DbfHeader Header { get; private set; } 56 | 57 | public Stream Stream { get; private set; } 58 | 59 | public DbfMemo Memo { get; private set; } 60 | 61 | public IList Columns { get; private set; } 62 | 63 | public bool IsClosed => Stream == null; 64 | 65 | public void Close() 66 | { 67 | Dispose(true); 68 | } 69 | 70 | protected override void Dispose(bool disposing) 71 | { 72 | try 73 | { 74 | if (!disposing) return; 75 | Stream?.Dispose(); 76 | Memo?.Dispose(); 77 | } 78 | finally 79 | { 80 | Stream = null; 81 | Memo = null; 82 | } 83 | } 84 | 85 | public string MemoPath() 86 | { 87 | var paths = new[] 88 | { 89 | System.IO.Path.ChangeExtension(Path, "fpt"), 90 | System.IO.Path.ChangeExtension(Path, "FPT"), 91 | System.IO.Path.ChangeExtension(Path, "dbt"), 92 | System.IO.Path.ChangeExtension(Path, "DBT") 93 | }; 94 | 95 | foreach (var path in paths) 96 | if (File.Exists(path)) 97 | return path; 98 | 99 | return string.Empty; 100 | } 101 | 102 | public DbfMemo CreateMemo(Stream memoStream) 103 | { 104 | DbfMemo memo; 105 | 106 | if (Header.IsFoxPro) 107 | { 108 | memo = new DbfMemoFoxPro(memoStream, CurrentEncoding); 109 | } 110 | else 111 | { 112 | if (Header.Version == 0x83) 113 | memo = new DbfMemoDbase3(memoStream, CurrentEncoding); 114 | else 115 | memo = new DbfMemoDbase4(memoStream, CurrentEncoding); 116 | } 117 | 118 | return memo; 119 | } 120 | 121 | public DbfMemo CreateMemo(string path) 122 | { 123 | DbfMemo memo; 124 | 125 | if (Header.IsFoxPro) 126 | { 127 | memo = new DbfMemoFoxPro(path, CurrentEncoding); 128 | } 129 | else 130 | { 131 | if (Header.Version == 0x83) 132 | memo = new DbfMemoDbase3(path, CurrentEncoding); 133 | else 134 | memo = new DbfMemoDbase4(path, CurrentEncoding); 135 | } 136 | 137 | return memo; 138 | } 139 | 140 | public IList ReadColumns(Stream stream) 141 | { 142 | var count = Header.HeaderLength - DbfHeader.DbfHeaderSize; 143 | var buffer = new byte[count]; 144 | stream.Read(buffer, 0, count); 145 | var span = new ReadOnlySpan(buffer); 146 | 147 | var columns = new List(); 148 | 149 | var start = 0; 150 | var startField = 1; 151 | var ordinal = 0; 152 | while (span[start] != Terminator) 153 | { 154 | var slice = span.Slice(start, DbfColumn.DbfColumnSize); 155 | var column = new DbfColumn(slice, startField, ordinal, CurrentEncoding); 156 | columns.Add(column); 157 | 158 | ordinal++; 159 | start = ordinal * DbfColumn.DbfColumnSize; 160 | startField += column.Length; 161 | } 162 | 163 | return columns; 164 | } 165 | 166 | public DbfRecord ReadRecord() 167 | { 168 | var dbfRecord = new DbfRecord(this); 169 | return !dbfRecord.Read(Stream) ? null : dbfRecord; 170 | } 171 | 172 | public bool Read(DbfRecord dbfRecord) 173 | { 174 | return dbfRecord.Read(Stream); 175 | } 176 | } 177 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DbfDataReader 4 | { 5 | public abstract class DbfValue : IDbfValue 6 | { 7 | protected DbfValue(int offset, int length) 8 | { 9 | Start = offset; 10 | Length = length; 11 | } 12 | 13 | public int Start { get; } 14 | 15 | public int Length { get; } 16 | 17 | public T Value { get; protected set; } 18 | 19 | public abstract void Read(ReadOnlySpan bytes); 20 | 21 | public object GetValue() 22 | { 23 | return Value; 24 | } 25 | 26 | public override string ToString() 27 | { 28 | return Value == null ? string.Empty : Value.ToString(); 29 | } 30 | 31 | public Type GetFieldType() 32 | { 33 | return typeof(T); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfValueBoolean.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DbfDataReader 4 | { 5 | public class DbfValueBoolean : DbfValue 6 | { 7 | public DbfValueBoolean(int start, int length) : base(start, length) 8 | { 9 | } 10 | 11 | public override void Read(ReadOnlySpan bytes) 12 | { 13 | var value = bytes[0]; 14 | if (value == 'Y' || value == 'y' || value == 'T' || value == 't') 15 | Value = true; 16 | else if (value == 'N' || value == 'n' || value == 'F' || value == 'f') 17 | Value = false; 18 | else 19 | Value = null; 20 | } 21 | 22 | public override string ToString() 23 | { 24 | return !Value.HasValue ? string.Empty : Value.Value ? "T" : "F"; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfValueCurrency.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | namespace DbfDataReader 5 | { 6 | public class DbfValueCurrency : DbfValue 7 | { 8 | public DbfValueCurrency(int start, int length, int decimalCount) : base(start, length) 9 | { 10 | DecimalCount = decimalCount; 11 | } 12 | 13 | public int DecimalCount { get; set; } 14 | 15 | public override void Read(ReadOnlySpan bytes) 16 | { 17 | var value = BitConverter.ToInt64(bytes); 18 | Value = value / 10000.0f; 19 | } 20 | 21 | public override string ToString() 22 | { 23 | var format = DecimalCount != 0 24 | ? $"F{DecimalCount}" 25 | : null; 26 | 27 | return Value?.ToString(format, NumberFormatInfo.CurrentInfo) ?? string.Empty; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfValueDate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Text; 4 | 5 | namespace DbfDataReader 6 | { 7 | public class DbfValueDate : DbfValue 8 | { 9 | public DbfValueDate(int start, int length) : base(start, length) 10 | { 11 | } 12 | 13 | public override void Read(ReadOnlySpan bytes) 14 | { 15 | var value = Encoding.ASCII.GetString(bytes); 16 | var nullIdx = value.IndexOf((char)0); 17 | if (nullIdx >= 0) 18 | { 19 | value = value.Substring(0, nullIdx); // trim off everything past & including the first NUL byte 20 | } 21 | 22 | if (string.IsNullOrWhiteSpace(value)) 23 | Value = null; 24 | else if (DateTime.TryParseExact(value, "yyyyMMdd", null, 25 | DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite, 26 | out DateTime valueOut)) 27 | Value = valueOut; 28 | else 29 | Value = null; 30 | } 31 | 32 | public override string ToString() 33 | { 34 | return Value?.ToString("d") ?? string.Empty; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/DbfDataReader/DbfValueDateTime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DbfDataReader 4 | { 5 | // DateTime values are encoded as two 32 bits numbers. The high word is 6 | // the date, encoded as the number of days since the beginning of the 7 | // Julian period (Jan 1, 4713BC), and the low word is the time, encoded 8 | // as the number of milliseconds since midnight. 9 | public class DbfValueDateTime : DbfValue 10 | { 11 | public DbfValueDateTime(int start, int length) : base(start, length) 12 | { 13 | } 14 | 15 | public override void Read(ReadOnlySpan bytes) 16 | { 17 | var datePart = BitConverter.ToInt32(bytes); 18 | var timePart = BitConverter.ToInt32(bytes[4..]); 19 | 20 | if (datePart == 0 && timePart == 0) 21 | { 22 | Value = null; 23 | return; 24 | } 25 | 26 | const int numberOfDaysSinceBeginJulianPeriod = 1721426; 27 | 28 | Value = new DateTime(1, 1, 1) 29 | .AddDays(datePart - numberOfDaysSinceBeginJulianPeriod) 30 | .AddMilliseconds(timePart); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfValueDecimal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Text; 4 | 5 | namespace DbfDataReader 6 | { 7 | public class DbfValueDecimal : DbfValue 8 | { 9 | private static readonly NumberFormatInfo _decimalNumberFormat = new NumberFormatInfo 10 | {NumberDecimalSeparator = "."}; 11 | 12 | public DbfValueDecimal(int start, int length, int decimalCount) : base(start, length) 13 | { 14 | DecimalCount = decimalCount; 15 | } 16 | 17 | public int DecimalCount { get; } 18 | 19 | public override void Read(ReadOnlySpan bytes) 20 | { 21 | if (bytes[0] == '\0') 22 | { 23 | Value = null; 24 | } 25 | else 26 | { 27 | var stringValue = Encoding.ASCII.GetString(bytes); 28 | 29 | if (decimal.TryParse(stringValue, 30 | NumberStyles.Float | NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, 31 | _decimalNumberFormat, out var value)) 32 | Value = value; 33 | else 34 | Value = null; 35 | } 36 | } 37 | 38 | public override string ToString() 39 | { 40 | var format = DecimalCount != 0 41 | ? $"F{DecimalCount}" 42 | : null; 43 | 44 | return Value?.ToString(format, NumberFormatInfo.CurrentInfo) ?? string.Empty; 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfValueDouble.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | namespace DbfDataReader 5 | { 6 | public class DbfValueDouble : DbfValue 7 | { 8 | public DbfValueDouble(int start, int length, int decimalCount) : base(start, length) 9 | { 10 | DecimalCount = decimalCount; 11 | } 12 | 13 | public int DecimalCount { get; } 14 | 15 | public override void Read(ReadOnlySpan bytes) 16 | { 17 | Value = BitConverter.ToDouble(bytes); 18 | } 19 | 20 | public override string ToString() 21 | { 22 | var format = DecimalCount != 0 23 | ? $"F{DecimalCount}" 24 | : null; 25 | 26 | return Value?.ToString(format, NumberFormatInfo.CurrentInfo) ?? string.Empty; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfValueFloat.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Text; 4 | 5 | namespace DbfDataReader 6 | { 7 | public class DbfValueFloat : DbfValue 8 | { 9 | private static readonly NumberFormatInfo 10 | _floatNumberFormat = new NumberFormatInfo {NumberDecimalSeparator = "."}; 11 | 12 | [Obsolete("This constructor should no longer be used. Use DbfValueFloat(System.Int32, System.Int32) instead.")] 13 | public DbfValueFloat(int start, int length) : this(start, length, 0) 14 | { 15 | } 16 | 17 | public DbfValueFloat(int start, int length, int decimalCount) : base(start, length) 18 | { 19 | DecimalCount = decimalCount; 20 | } 21 | 22 | public int DecimalCount { get; } 23 | 24 | public override void Read(ReadOnlySpan bytes) 25 | { 26 | if (bytes[0] == '\0') 27 | { 28 | Value = null; 29 | } 30 | else 31 | { 32 | var stringValue = Encoding.ASCII.GetString(bytes); 33 | 34 | if (float.TryParse(stringValue, 35 | NumberStyles.Float | NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, 36 | _floatNumberFormat, out var value)) 37 | Value = value; 38 | else 39 | Value = null; 40 | } 41 | } 42 | 43 | public override string ToString() 44 | { 45 | var format = DecimalCount != 0 46 | ? $"F{DecimalCount}" 47 | : null; 48 | 49 | return Value?.ToString(format, NumberFormatInfo.CurrentInfo) ?? string.Empty; 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfValueInt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Text; 4 | 5 | namespace DbfDataReader 6 | { 7 | public class DbfValueInt : DbfValue 8 | { 9 | private static readonly NumberFormatInfo _intNumberFormat = new NumberFormatInfo(); 10 | 11 | public DbfValueInt(int start, int length) : base(start, length) 12 | { 13 | } 14 | 15 | public override void Read(ReadOnlySpan bytes) 16 | { 17 | if (bytes[0] == '\0') 18 | { 19 | Value = null; 20 | } 21 | else 22 | { 23 | var stringValue = Encoding.ASCII.GetString(bytes); 24 | 25 | if (int.TryParse(stringValue, 26 | NumberStyles.Integer | NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, 27 | _intNumberFormat, out var value)) 28 | Value = value; 29 | else 30 | Value = null; 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfValueInt64.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Text; 4 | 5 | namespace DbfDataReader 6 | { 7 | public class DbfValueInt64 : DbfValue 8 | { 9 | private static readonly NumberFormatInfo _intNumberFormat = new NumberFormatInfo(); 10 | 11 | public DbfValueInt64(int start, int length) : base(start, length) 12 | { 13 | } 14 | 15 | public override void Read(ReadOnlySpan bytes) 16 | { 17 | if (bytes[0] == '\0') 18 | { 19 | Value = null; 20 | } 21 | else 22 | { 23 | var stringValue = Encoding.ASCII.GetString(bytes); 24 | 25 | if (Int64.TryParse(stringValue, 26 | NumberStyles.Integer | NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, 27 | _intNumberFormat, out var value)) 28 | Value = value; 29 | else 30 | Value = null; 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfValueLong.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DbfDataReader 4 | { 5 | public class DbfValueLong : DbfValue 6 | { 7 | public DbfValueLong(int start, int length) : base(start, length) 8 | { 9 | } 10 | 11 | public override void Read(ReadOnlySpan bytes) 12 | { 13 | Value = BitConverter.ToInt32(bytes); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfValueMemo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace DbfDataReader 5 | { 6 | public class DbfValueMemo : DbfValueString 7 | { 8 | private readonly DbfMemo _memo; 9 | 10 | public DbfValueMemo(int start, int length, DbfMemo memo, Encoding encoding) 11 | : base(start, length, encoding) 12 | { 13 | _memo = memo; 14 | } 15 | 16 | public override void Read(ReadOnlySpan bytes) 17 | { 18 | if (Length == 4) 19 | { 20 | var startBlock = BitConverter.ToUInt32(bytes); 21 | Value = _memo?.Get(startBlock); 22 | } 23 | else 24 | { 25 | var value = Encoding.GetString(bytes); 26 | 27 | if (string.IsNullOrWhiteSpace(value)) 28 | { 29 | Value = string.Empty; 30 | } 31 | else if (long.TryParse(value, out long startBlock)) 32 | { 33 | Value = _memo?.Get(startBlock); 34 | } 35 | else 36 | { 37 | Value = string.Empty; 38 | } 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/DbfDataReader/DbfValueNull.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DbfDataReader 4 | { 5 | public class DbfValueNull : IDbfValue 6 | { 7 | public DbfValueNull(int start, int length) 8 | { 9 | Start = start; 10 | Length = length; 11 | } 12 | 13 | public int Start { get; } 14 | 15 | public int Length { get; } 16 | 17 | public object GetValue() 18 | { 19 | return null; 20 | } 21 | 22 | public void Read(ReadOnlySpan bytes) 23 | { 24 | // do nothing 25 | } 26 | 27 | public T GetValue() 28 | { 29 | throw new NotImplementedException(); 30 | } 31 | 32 | public override string ToString() 33 | { 34 | return string.Empty; 35 | } 36 | 37 | public Type GetFieldType() 38 | { 39 | return null; 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfValueString.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace DbfDataReader 5 | { 6 | public class DbfValueString : DbfValue 7 | { 8 | private const char NullChar = '\0'; 9 | 10 | public DbfValueString(int start, int length, Encoding encoding) : base(start, length) 11 | { 12 | Encoding = encoding; 13 | } 14 | 15 | protected readonly Encoding Encoding; 16 | 17 | public override void Read(ReadOnlySpan bytes) 18 | { 19 | if (bytes[0] == NullChar) 20 | { 21 | Value = null; 22 | return; 23 | } 24 | 25 | var value = Encoding.GetString(bytes); 26 | Value = value.Trim(NullChar, ' '); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/DbfDataReader/DbfValueWideString.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace DbfDataReader 5 | { 6 | public class DbfValueWideString : DbfValue 7 | { 8 | private const char NullChar = '\0'; 9 | 10 | public DbfValueWideString(int start, int length) : base(start, length) 11 | { 12 | } 13 | 14 | public override void Read(ReadOnlySpan bytes) 15 | { 16 | if (bytes[0] == NullChar) 17 | { 18 | Value = null; 19 | return; 20 | } 21 | 22 | var value = Encoding.Unicode.GetString(bytes); 23 | Value = value.Trim(NullChar, ' '); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/DbfDataReader/Disposable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DbfDataReader 4 | { 5 | public abstract class Disposable : IDisposable 6 | { 7 | public void Dispose() 8 | { 9 | Dispose(true); 10 | GC.SuppressFinalize(this); 11 | } 12 | 13 | ~Disposable() 14 | { 15 | Dispose(false); 16 | } 17 | 18 | protected abstract void Dispose(bool disposing); 19 | } 20 | } -------------------------------------------------------------------------------- /src/DbfDataReader/EncodingProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace DbfDataReader 4 | { 5 | public static class EncodingProvider 6 | { 7 | static EncodingProvider() 8 | { 9 | Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); 10 | } 11 | 12 | public static Encoding GetEncoding(int codePage) 13 | { 14 | return Encoding.GetEncoding(codePage); 15 | } 16 | 17 | public static Encoding GetEncoding(byte languageDriver) 18 | { 19 | // Table from dbf.py by Ethan Furman at https://github.com/ethanfurman/dbf/blob/master/dbf/__init__.py, 20 | // mixed with table at https://github.com/olemb/dbfread/blob/master/dbfread/codepages.py 21 | // and https://github.com/infused/dbf/blob/master/lib/dbf/encodings.rb 22 | switch (languageDriver) 23 | { 24 | case 0x00: 25 | return Encoding.ASCII; // 20127, "ascii", "plain ol' ascii" 26 | case 0x01: 27 | return Encoding.GetEncoding(437); // "cp437", "U.S. MS-DOS" 28 | case 0x02: 29 | return Encoding.GetEncoding(850); // "cp850", "International MS-DOS" 30 | case 0x03: 31 | return Encoding.GetEncoding(1252); // "cp1252", "Windows ANSI" 32 | case 0x04: 33 | return Encoding.GetEncoding(1000); // "mac_roman", "Standard Macintosh" 34 | case 0x08: 35 | return Encoding.GetEncoding(865); // "cp865", "Danish OEM" 36 | case 0x09: 37 | return Encoding.GetEncoding(437); // "cp437", "Dutch OEM" 38 | case 0x0A: 39 | return Encoding.GetEncoding(850); // "cp850", "Dutch OEM (secondary)" 40 | case 0x0B: 41 | return Encoding.GetEncoding(437); // "cp437", "Finnish OEM" 42 | case 0x0D: 43 | return Encoding.GetEncoding(437); // "cp437", "French OEM" 44 | case 0x0E: 45 | return Encoding.GetEncoding(850); // "cp850", "French OEM (secondary)" 46 | case 0x0F: 47 | return Encoding.GetEncoding(437); // "cp437", "German OEM" 48 | case 0x10: 49 | return Encoding.GetEncoding(850); // "cp850", "German OEM (secondary)" 50 | case 0x11: 51 | return Encoding.GetEncoding(437); // "cp437", "Italian OEM" 52 | case 0x12: 53 | return Encoding.GetEncoding(850); // "cp850", "Italian OEM (secondary)" 54 | case 0x13: 55 | return Encoding.GetEncoding(932); // "cp932", "Japanese Shift-JIS" 56 | case 0x14: 57 | return Encoding.GetEncoding(850); // "cp850", "Spanish OEM (secondary)" 58 | case 0x15: 59 | return Encoding.GetEncoding(437); // "cp437", "Swedish OEM" 60 | case 0x16: 61 | return Encoding.GetEncoding(850); // "cp850", "Swedish OEM (secondary)" 62 | case 0x17: 63 | return Encoding.GetEncoding(865); // "cp865", "Norwegian OEM" 64 | case 0x18: 65 | return Encoding.GetEncoding(437); // "cp437", "Spanish OEM" 66 | case 0x19: 67 | return Encoding.GetEncoding(437); // "cp437", "English OEM (Britain)" 68 | case 0x1A: 69 | return Encoding.GetEncoding(850); // "cp850", "English OEM (Britain) (secondary)" 70 | case 0x1B: 71 | return Encoding.GetEncoding(437); // "cp437", "English OEM (U.S.)" 72 | case 0x1C: 73 | return Encoding.GetEncoding(863); // "cp863", "French OEM (Canada)" 74 | case 0x1D: 75 | return Encoding.GetEncoding(850); // "cp850", "French OEM (secondary)" 76 | case 0x1F: 77 | return Encoding.GetEncoding(852); // "cp852", "Czech OEM" 78 | case 0x22: 79 | return Encoding.GetEncoding(852); // "cp852", "Hungarian OEM" 80 | case 0x23: 81 | return Encoding.GetEncoding(852); // "cp852", "Polish OEM" 82 | case 0x24: 83 | return Encoding.GetEncoding(860); // "cp860", "Portuguese OEM" 84 | case 0x25: 85 | return Encoding.GetEncoding(850); // "cp850", "Portuguese OEM (secondary)" 86 | case 0x26: 87 | return Encoding.GetEncoding(866); // "cp866", "Russian OEM" 88 | case 0x37: 89 | return Encoding.GetEncoding(850); // "cp850", "English OEM (U.S.) (secondary)" 90 | case 0x40: 91 | return Encoding.GetEncoding(852); // "cp852", "Romanian OEM" 92 | case 0x4D: 93 | return Encoding.GetEncoding(936); // "cp936", "Chinese GBK (PRC)" 94 | case 0x4E: 95 | return Encoding.GetEncoding(949); // "cp949", "Korean (ANSI/OEM)" 96 | case 0x4F: 97 | return Encoding.GetEncoding(950); // "cp950", "Chinese Big 5 (Taiwan)" 98 | case 0x50: 99 | return Encoding.GetEncoding(874); // "cp874", "Thai (ANSI/OEM)" 100 | case 0x57: 101 | return Encoding.GetEncoding(1252); // "cp1252", "ANSI" 102 | case 0x58: 103 | return Encoding.GetEncoding(1252); // "cp1252", "Western European ANSI" 104 | case 0x59: 105 | return Encoding.GetEncoding(1252); // "cp1252", "Spanish ANSI" 106 | case 0x64: 107 | return Encoding.GetEncoding(852); // "cp852", "Eastern European MS-DOS" 108 | case 0x65: 109 | return Encoding.GetEncoding(866); // "cp866", "Russian MS-DOS" 110 | case 0x66: 111 | return Encoding.GetEncoding(865); // "cp865", "Nordic MS-DOS" 112 | case 0x67: 113 | return Encoding.GetEncoding(861); // "cp861", "Icelandic MS-DOS" 114 | case 0x68: 115 | return Encoding.GetEncoding(895); // Kamenicky (Czech) MS-DOS 116 | case 0x69: 117 | return Encoding.GetEncoding(620); // "Mazovia (Polish) MS-DOS" 118 | case 0x6a: 119 | return Encoding.GetEncoding(737); // "cp737", "Greek MS-DOS (437G)" 120 | case 0x6b: 121 | return Encoding.GetEncoding(857); // "cp857", "Turkish MS-DOS" 122 | case 0x6c: 123 | return Encoding.GetEncoding(863); // "cp863", "French-Canadian MS-DOS" 124 | case 0x78: 125 | return Encoding.GetEncoding(950); // "cp950", "Traditional Chinese (Hong Kong SAR, Taiwan) Windows" 126 | case 0x79: 127 | return Encoding.GetEncoding(949); // "cp949", "Korean Windows" 128 | case 0x7a: 129 | return Encoding.GetEncoding(936); // "cp936", "Chinese Simplified (PRC, Singapore) Windows" 130 | case 0x7b: 131 | return Encoding.GetEncoding(932); // "cp932", "Japanese Windows" 132 | case 0x7c: 133 | return Encoding.GetEncoding(874); // "cp874", "Thai Windows" 134 | case 0x7d: 135 | return Encoding.GetEncoding(1255); // "cp1255", "Hebrew Windows" 136 | case 0x7e: 137 | return Encoding.GetEncoding(1256); // "cp1256", "Arabic Windows" 138 | case 0x86: 139 | return Encoding.GetEncoding(737); // "cp737", "Greek OEM" 140 | case 0x87: 141 | return Encoding.GetEncoding(852); // "cp852", "Slovenian OEM" 142 | case 0x88: 143 | return Encoding.GetEncoding(857); // "cp857", "Turkish OEM" 144 | case 0x96: 145 | return Encoding.GetEncoding(10007); // "mac_cyrillic", "Russian Macintosh" 146 | case 0x97: 147 | return Encoding.GetEncoding(10029); // "mac_latin2", "Macintosh EE" 148 | case 0x98: 149 | return Encoding.GetEncoding(10006); // "mac_greek", "Greek Macintosh" 150 | case 0xc8: 151 | return Encoding.GetEncoding(1250); // "cp1250", "Eastern European Windows" 152 | case 0xc9: 153 | return Encoding.GetEncoding(1251); // "cp1251", "Russian Windows" 154 | case 0xca: 155 | return Encoding.GetEncoding(1254); // "cp1254", "Turkish Windows" 156 | case 0xcb: 157 | return Encoding.GetEncoding(1253); // "cp1253", "Greek Windows" 158 | case 0xcc: 159 | return Encoding.GetEncoding(1257); // "cp1257", "Baltic Windows" 160 | case 0xf0: 161 | return Encoding.UTF8; // "utf8", "8-bit unicode" 162 | default: 163 | return Encoding.GetEncoding(1252); // Unable to guess encoding for language driver 164 | } 165 | } 166 | } 167 | } -------------------------------------------------------------------------------- /src/DbfDataReader/IDbfValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DbfDataReader 4 | { 5 | public interface IDbfValue 6 | { 7 | int Start { get; } 8 | 9 | int Length { get; } 10 | 11 | void Read(ReadOnlySpan bytes); 12 | 13 | object GetValue(); 14 | 15 | Type GetFieldType(); 16 | } 17 | } -------------------------------------------------------------------------------- /src/DbfDataReader/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: CLSCompliant(true)] 5 | [assembly: ComVisible(false)] -------------------------------------------------------------------------------- /test/DbfDataReader.Benchmarks/DbfDataReader.Benchmarks.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | 7 | 8 | 9 | AnyCPU 10 | portable 11 | true 12 | true 13 | true 14 | Release 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | PreserveNewest 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /test/DbfDataReader.Benchmarks/DbfDataReaderBenchmarks.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using BenchmarkDotNet.Attributes; 3 | 4 | namespace DbfDataReader.Benchmarks 5 | { 6 | public class DbfDataReaderBenchmarks 7 | { 8 | private const string FixturePath = "./fixtures/dbase_03.dbf"; 9 | 10 | [Benchmark] 11 | public void DbfDataReader() 12 | { 13 | using (var dbfDataReader = new DbfDataReader(FixturePath)) 14 | { 15 | var cols = dbfDataReader.GetColumnSchema(); 16 | var allowDbNull = cols.Select(c => c.AllowDBNull != false).ToArray(); 17 | while (dbfDataReader.Read()) 18 | { 19 | for (var ordinal = 0; ordinal < dbfDataReader.FieldCount; ordinal++) 20 | { 21 | if (allowDbNull[ordinal] && dbfDataReader.IsDBNull(ordinal)) 22 | continue; 23 | dbfDataReader.ReadField(ordinal); 24 | } 25 | } 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /test/DbfDataReader.Benchmarks/DbfDataReaderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DbfDataReader.Benchmarks 4 | { 5 | public static class DbfDataReaderExtensions 6 | { 7 | public static void ReadField(this DbfDataReader dbfDataReader, int ordinal) 8 | { 9 | var fieldType = dbfDataReader.GetFieldType(ordinal); 10 | var typeCode = Type.GetTypeCode(fieldType); 11 | switch (typeCode) 12 | { 13 | case TypeCode.Boolean: 14 | dbfDataReader.GetBoolean(ordinal); 15 | break; 16 | case TypeCode.Int32: 17 | dbfDataReader.GetInt32(ordinal); 18 | break; 19 | case TypeCode.DateTime: 20 | dbfDataReader.GetDateTime(ordinal); 21 | break; 22 | case TypeCode.Single: 23 | dbfDataReader.GetFloat(ordinal); 24 | break; 25 | case TypeCode.Double: 26 | dbfDataReader.GetDouble(ordinal); 27 | break; 28 | case TypeCode.Decimal: 29 | dbfDataReader.GetDecimal(ordinal); 30 | break; 31 | case TypeCode.String: 32 | dbfDataReader.GetString(ordinal); 33 | break; 34 | case TypeCode.Object: 35 | dbfDataReader.GetValue(ordinal); 36 | break; 37 | default: 38 | Console.WriteLine($"Unrecognised type: fieldType: {fieldType}, typeCode: {typeCode} "); 39 | // no cheating 40 | throw new NotSupportedException(); 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /test/DbfDataReader.Benchmarks/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Configs; 2 | using BenchmarkDotNet.Diagnosers; 3 | using BenchmarkDotNet.Running; 4 | 5 | namespace DbfDataReader.Benchmarks 6 | { 7 | public class Program 8 | { 9 | public static void Main(string[] args) 10 | { 11 | var config = DefaultConfig.Instance.AddDiagnoser(MemoryDiagnoser.Default); 12 | BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly) 13 | .Run(args, config); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/DbfDataReader.Tests/Dbase03NullCharTests.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using Xunit; 3 | 4 | namespace DbfDataReader.Tests 5 | { 6 | [Collection("dbase_03")] 7 | public class Dbase03NullCharTests : DbaseTests 8 | { 9 | private const string Dbase03FixturePath = "../../../../fixtures/dbase_03_nullchar.dbf"; 10 | 11 | public Dbase03NullCharTests() : base(Dbase03FixturePath) 12 | { 13 | } 14 | 15 | [Fact] 16 | public void Should_report_correct_record_count() 17 | { 18 | DbfHeader.RecordCount.ShouldBe(3); 19 | } 20 | 21 | [Fact] 22 | public void Should_report_correct_version_number() 23 | { 24 | DbfHeader.Version.ShouldBe(0x03); 25 | } 26 | 27 | [Fact] 28 | public void Should_report_that_the_file_is_not_foxpro() 29 | { 30 | DbfHeader.IsFoxPro.ShouldBeFalse(); 31 | } 32 | 33 | [Fact] 34 | public void Should_have_the_correct_number_of_columns() 35 | { 36 | DbfTable.Columns.Count.ShouldBe(2); 37 | } 38 | 39 | [Fact] 40 | public void Should_have_the_correct_column_schema() 41 | { 42 | ValidateColumnSchema("../../../../fixtures/dbase_03_nullchar_summary.txt"); 43 | } 44 | 45 | [Fact] 46 | [UseCulture("it-IT")] 47 | public void Should_have_correct_row_values() 48 | { 49 | ValidateRowValues("../../../../fixtures/dbase_03_nullchar.csv"); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/DbfDataReader.Tests/Dbase03Tests.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using Xunit; 3 | 4 | namespace DbfDataReader.Tests 5 | { 6 | [Collection("dbase_03")] 7 | public class Dbase03Tests : DbaseTests 8 | { 9 | private const string Dbase03FixturePath = "../../../../fixtures/dbase_03.dbf"; 10 | 11 | public Dbase03Tests() : base(Dbase03FixturePath) 12 | { 13 | } 14 | 15 | [Fact] 16 | public void Should_report_correct_record_count() 17 | { 18 | DbfHeader.RecordCount.ShouldBe(14); 19 | } 20 | 21 | [Fact] 22 | public void Should_report_correct_version_number() 23 | { 24 | DbfHeader.Version.ShouldBe(0x03); 25 | } 26 | 27 | [Fact] 28 | public void Should_report_that_the_file_is_not_foxpro() 29 | { 30 | DbfHeader.IsFoxPro.ShouldBeFalse(); 31 | } 32 | 33 | [Fact] 34 | public void Should_have_the_correct_number_of_columns() 35 | { 36 | DbfTable.Columns.Count.ShouldBe(31); 37 | } 38 | 39 | [Fact] 40 | public void Should_have_the_correct_column_schema() 41 | { 42 | ValidateColumnSchema("../../../../fixtures/dbase_03_summary.txt"); 43 | } 44 | 45 | [Fact] 46 | [UseCulture("en-GB")] 47 | public void Should_have_correct_row_values() 48 | { 49 | ValidateRowValues("../../../../fixtures/dbase_03.csv"); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/DbfDataReader.Tests/Dbase30Tests.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using Xunit; 3 | 4 | namespace DbfDataReader.Tests 5 | { 6 | [Collection("dbase_30")] 7 | public class Dbase30Tests : DbaseTests 8 | { 9 | private const string Dbase30FixturePath = "../../../../fixtures/dbase_30.dbf"; 10 | 11 | public Dbase30Tests() : base(Dbase30FixturePath) 12 | { 13 | } 14 | 15 | [Fact] 16 | public void Should_report_correct_record_count() 17 | { 18 | DbfHeader.RecordCount.ShouldBe(34); 19 | } 20 | 21 | [Fact] 22 | public void Should_report_correct_version_number() 23 | { 24 | DbfHeader.Version.ShouldBe(0x30); 25 | } 26 | 27 | [Fact] 28 | public void Should_report_that_the_file_is_not_foxpro() 29 | { 30 | DbfHeader.IsFoxPro.ShouldBeTrue(); 31 | } 32 | 33 | [Fact] 34 | public void Should_have_the_correct_number_of_columns() 35 | { 36 | DbfTable.Columns.Count.ShouldBe(145); 37 | } 38 | 39 | [Fact] 40 | public void Should_have_the_correct_column_schema() 41 | { 42 | ValidateColumnSchema("../../../../fixtures/dbase_30_summary.txt"); 43 | } 44 | 45 | 46 | [Fact] 47 | [UseCulture("en-GB")] 48 | public void Should_have_correct_row_values() 49 | { 50 | ValidateRowValues("../../../../fixtures/dbase_30.csv"); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /test/DbfDataReader.Tests/Dbase31Tests.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using Xunit; 3 | 4 | namespace DbfDataReader.Tests 5 | { 6 | [Collection("dbase_31")] 7 | public class Dbase31Tests : DbaseTests 8 | { 9 | private const string Dbase31FixturePath = "../../../../fixtures/dbase_31.dbf"; 10 | 11 | public Dbase31Tests() : base(Dbase31FixturePath) 12 | { 13 | } 14 | 15 | [Fact] 16 | public void Should_report_correct_record_count() 17 | { 18 | DbfHeader.RecordCount.ShouldBe(77); 19 | } 20 | 21 | [Fact] 22 | public void Should_report_correct_version_number() 23 | { 24 | DbfHeader.Version.ShouldBe(0x31); 25 | } 26 | 27 | [Fact] 28 | public void Should_report_that_the_file_is_not_foxpro() 29 | { 30 | DbfHeader.IsFoxPro.ShouldBeTrue(); 31 | } 32 | 33 | [Fact] 34 | public void Should_have_the_correct_number_of_columns() 35 | { 36 | DbfTable.Columns.Count.ShouldBe(13); 37 | } 38 | 39 | [Fact] 40 | public void Should_have_the_correct_column_schema() 41 | { 42 | ValidateColumnSchema("../../../../fixtures/dbase_31_summary.txt"); 43 | } 44 | 45 | [Fact] 46 | [UseCulture("en-GB")] 47 | public void Should_have_correct_row_values() 48 | { 49 | ValidateRowValues("../../../../fixtures/dbase_31.csv"); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /test/DbfDataReader.Tests/Dbase8BTests.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using Xunit; 3 | 4 | namespace DbfDataReader.Tests; 5 | 6 | [Collection("dbase_8B")] 7 | public class Dbase8BTests : DbaseTests 8 | { 9 | private const string Dbase8BFixturePath = "../../../../fixtures/client.dbf"; 10 | 11 | public Dbase8BTests() : base(Dbase8BFixturePath) 12 | { 13 | } 14 | 15 | [Fact] 16 | public void Should_report_correct_record_count() 17 | { 18 | DbfHeader.RecordCount.ShouldBe(8); 19 | } 20 | 21 | [Fact] 22 | public void Should_report_correct_version_number() 23 | { 24 | DbfHeader.Version.ShouldBe(0x8B); 25 | } 26 | 27 | [Fact] 28 | public void Should_have_the_correct_column_schema() 29 | { 30 | ValidateColumnSchema("../../../../fixtures/client_summary.txt"); 31 | } 32 | 33 | [Fact] 34 | public void Should_have_correct_row_values() 35 | { 36 | ValidateRowValues("../../../../fixtures/client.csv"); 37 | } 38 | } -------------------------------------------------------------------------------- /test/DbfDataReader.Tests/DbaseF5Tests.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using Xunit; 3 | 4 | namespace DbfDataReader.Tests 5 | { 6 | [Collection("dbase_f5")] 7 | public class DbaseF5Tests : DbaseTests 8 | { 9 | private const string DbaseF5FixturePath = "../../../../fixtures/dbase_f5.dbf"; 10 | 11 | // override default encoding to use codepage 1252 12 | public DbaseF5Tests() : base(DbaseF5FixturePath, EncodingProvider.GetEncoding(1252)) 13 | { 14 | } 15 | 16 | [Fact] 17 | public void Should_report_correct_record_count() 18 | { 19 | DbfHeader.RecordCount.ShouldBe(975); 20 | } 21 | 22 | [Fact] 23 | public void Should_report_correct_version_number() 24 | { 25 | DbfHeader.Version.ShouldBe(0xf5); 26 | } 27 | 28 | [Fact] 29 | public void Should_report_that_the_file_is_foxpro() 30 | { 31 | DbfHeader.IsFoxPro.ShouldBeTrue(); 32 | } 33 | 34 | [Fact] 35 | public void Should_have_the_correct_number_of_columns() 36 | { 37 | DbfTable.Columns.Count.ShouldBe(59); 38 | } 39 | 40 | [Fact] 41 | public void Should_have_the_correct_column_schema() 42 | { 43 | ValidateColumnSchema("../../../../fixtures/dbase_f5_summary.txt"); 44 | } 45 | 46 | [Fact] 47 | [UseCulture("en-GB")] 48 | public void Should_have_correct_row_values() 49 | { 50 | ValidateRowValues("../../../../fixtures/dbase_f5.csv"); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /test/DbfDataReader.Tests/DbaseTests.cs: -------------------------------------------------------------------------------- 1 | using CsvHelper; 2 | using Shouldly; 3 | using System; 4 | using System.Globalization; 5 | using System.IO; 6 | using System.Text; 7 | 8 | namespace DbfDataReader.Tests 9 | { 10 | public abstract class DbaseTests : IDisposable 11 | { 12 | protected DbaseTests(string fixturePath, Encoding encoding = null) 13 | { 14 | FixturePath = fixturePath; 15 | DbfTable = new DbfTable(fixturePath, encoding); 16 | } 17 | 18 | public void Dispose() 19 | { 20 | DbfTable.Dispose(); 21 | DbfTable = null; 22 | } 23 | 24 | public string FixturePath { get; } 25 | 26 | public DbfTable DbfTable { get; protected set; } 27 | 28 | public DbfHeader DbfHeader => DbfTable.Header; 29 | 30 | protected void ValidateColumnSchema(string path) 31 | { 32 | using (var dbfColumns = DbfTable.Columns.GetEnumerator()) 33 | { 34 | dbfColumns.MoveNext(); 35 | 36 | foreach (var line in FixtureHelpers.GetFieldLines(path)) 37 | { 38 | var dbfColumn = dbfColumns.Current; 39 | ValidateColumn(dbfColumn, line); 40 | 41 | dbfColumns.MoveNext(); 42 | } 43 | } 44 | } 45 | 46 | protected void ValidateColumn(DbfColumn dbfColumn, string line) 47 | { 48 | var expectedName = line.Substring(0, 16).Trim(); 49 | var expectedColumnType = (DbfColumnType)line.Substring(17, 1)[0]; 50 | var expectedLength = int.Parse(line.Substring(28, 10)); 51 | var expectedDecimalCount = int.Parse(line.Substring(39)); 52 | 53 | dbfColumn.ColumnName.ShouldBe(expectedName); 54 | dbfColumn.ColumnType.ShouldBe(expectedColumnType); 55 | dbfColumn.Length.ShouldBe(expectedLength); 56 | dbfColumn.DecimalCount.ShouldBe(expectedDecimalCount); 57 | } 58 | 59 | protected void ValidateRowValues(string path) 60 | { 61 | var dbfRecord = new DbfRecord(DbfTable); 62 | 63 | using (var textReader = File.OpenText(path)) 64 | using (var csvParser = new CsvParser(textReader, CultureInfo.InvariantCulture)) 65 | { 66 | csvParser.Read(); 67 | 68 | var row = 1; 69 | while (DbfTable.Read(dbfRecord)) 70 | { 71 | csvParser.Read(); 72 | 73 | var index = 0; 74 | foreach (var dbfValue in dbfRecord.Values) 75 | { 76 | var value = dbfValue.ToString(); 77 | var csvValue = csvParser[index]; 78 | value.ShouldBe(csvValue, $"Row: {row}, column: {index} ({DbfTable.Columns[index].ColumnName})", StringCompareShould.IgnoreLineEndings); 79 | 80 | index++; 81 | } 82 | 83 | row++; 84 | } 85 | } 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /test/DbfDataReader.Tests/DbfDataReader.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DbfDataReader.Tests 5 | net6.0 6 | DbfDataReader.Tests 7 | DbfDataReader.Tests 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | all 19 | runtime; build; native; contentfiles; analyzers 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /test/DbfDataReader.Tests/DbfDataReaderTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using System.Data.Common; 4 | using Shouldly; 5 | using Xunit; 6 | 7 | namespace DbfDataReader.Tests 8 | { 9 | [Collection("dbase_03")] 10 | public class DbfDataReaderTests 11 | { 12 | private const string FixturePath = "../../../../fixtures/dbase_03.dbf"; 13 | private const string FixtureSummaryPath = "../../../../fixtures/dbase_03_summary.txt"; 14 | 15 | [Fact] 16 | public void Should_resolve_names_to_ordinals() 17 | { 18 | // Test if GetOrdinal resolves names according to IDataRecord.GetOrdinal() 19 | // see https://learn.microsoft.com/en-us/dotnet/api/system.data.idatarecord.getordinal?view=net-7.0#system-data-idatarecord-getordinal(system-string) 20 | using (var dbfDataReader = new DbfDataReader(FixturePath)) 21 | { 22 | dbfDataReader.GetOrdinal("Point_ID").ShouldBe(0); 23 | dbfDataReader.GetOrdinal("POINT_ID").ShouldBe(0); 24 | dbfDataReader.GetOrdinal("point_id").ShouldBe(0); 25 | dbfDataReader.GetOrdinal("Std_Dev").ShouldBe(27); 26 | dbfDataReader.GetOrdinal("STD_DEV").ShouldBe(27); 27 | dbfDataReader.GetOrdinal("std_dev").ShouldBe(27); 28 | Assert.Throws(() => dbfDataReader.GetOrdinal("NO_SUCH_FIELD")); 29 | } 30 | } 31 | [Fact] 32 | public void Should_have_valid_first_row_values() 33 | { 34 | using (var dbfDataReader = new DbfDataReader(FixturePath)) 35 | { 36 | dbfDataReader.Read().ShouldBeTrue(); 37 | 38 | dbfDataReader.GetString(0).ShouldBe("0507121"); 39 | dbfDataReader.GetString(1).ShouldBe("CMP"); 40 | dbfDataReader.GetString(2).ShouldBe("circular"); 41 | dbfDataReader.GetString(3).ShouldBe("12"); 42 | dbfDataReader.GetString(4).ShouldBe(string.Empty); 43 | dbfDataReader.GetString(5).ShouldBe("no"); 44 | dbfDataReader.GetString(6).ShouldBe("Good"); 45 | dbfDataReader.GetString(7).ShouldBe(string.Empty); 46 | dbfDataReader.GetDateTime(8).ShouldBe(new DateTime(2005, 7, 12)); 47 | dbfDataReader.GetString(9).ShouldBe("10:56:30am"); 48 | dbfDataReader.GetDecimal(10).ShouldBe(5.2m); 49 | dbfDataReader.GetDecimal(11).ShouldBe(2.0m); 50 | dbfDataReader.GetString(12).ShouldBe("Postprocessed Code"); 51 | dbfDataReader.GetString(13).ShouldBe("GeoXT"); 52 | dbfDataReader.GetDateTime(14).ShouldBe(new DateTime(2005, 7, 12)); 53 | dbfDataReader.GetString(15).ShouldBe("10:56:52am"); 54 | dbfDataReader.GetString(16).ShouldBe("New"); 55 | dbfDataReader.GetString(17).ShouldBe("Driveway"); 56 | dbfDataReader.GetString(18).ShouldBe("050712TR2819.cor"); 57 | dbfDataReader.GetInt64(19).ShouldBe(2); 58 | dbfDataReader.GetInt64(20).ShouldBe(2); 59 | dbfDataReader.GetString(21).ShouldBe("MS4"); 60 | dbfDataReader.GetInt32(22).ShouldBe(1331); 61 | dbfDataReader.GetDecimal(23).ShouldBe(226625.000m); 62 | dbfDataReader.GetDecimal(24).ShouldBe(1131.323m); 63 | dbfDataReader.GetDecimal(25).ShouldBe(3.1m); 64 | dbfDataReader.GetDecimal(26).ShouldBe(1.3m); 65 | dbfDataReader.GetDecimal(27).ShouldBe(0.897088m); 66 | dbfDataReader.GetDecimal(28).ShouldBe(557904.898m); 67 | dbfDataReader.GetDecimal(29).ShouldBe(2212577.192m); 68 | dbfDataReader.GetInt32(30).ShouldBe(401); 69 | } 70 | } 71 | 72 | [Fact] 73 | public void Should_be_able_to_read_all_the_rows() 74 | { 75 | using (var dbfDataReader = new DbfDataReader(FixturePath)) 76 | { 77 | var rowCount = 0; 78 | while (dbfDataReader.Read()) 79 | { 80 | rowCount++; 81 | 82 | dbfDataReader.GetString(0); 83 | dbfDataReader.GetDecimal(10); 84 | } 85 | 86 | rowCount.ShouldBe(14); 87 | } 88 | } 89 | 90 | [Fact] 91 | public void Should_skip_deleted_rows() 92 | { 93 | var options = new DbfDataReaderOptions 94 | { 95 | SkipDeletedRecords = true 96 | }; 97 | using (var dbfDataReader = new DbfDataReader(FixturePath, options)) 98 | { 99 | var rowCount = 0; 100 | while (dbfDataReader.Read()) 101 | { 102 | rowCount++; 103 | 104 | dbfDataReader.GetString(0); 105 | dbfDataReader.GetDecimal(10); 106 | } 107 | 108 | rowCount.ShouldBe(12); 109 | } 110 | } 111 | 112 | [Fact] 113 | public void Should_throw_exception_when_casting_to_wrong_type() 114 | { 115 | using (var dbfDataReader = new DbfDataReader(FixturePath)) 116 | { 117 | dbfDataReader.Read(); 118 | 119 | var exception = Should.Throw(() => dbfDataReader.GetInt32(0)); 120 | exception.Message.ShouldBe( 121 | "Unable to cast object of type 'System.String' to type 'System.Int32' at ordinal '0'."); 122 | } 123 | } 124 | 125 | [Fact] 126 | public void Should_support_get_column_schema() 127 | { 128 | using (var dbfDataReader = new DbfDataReader(FixturePath)) 129 | { 130 | dbfDataReader.CanGetColumnSchema().ShouldBeTrue(); 131 | } 132 | } 133 | 134 | [Fact] 135 | public void Should_get_valid_column_schema() 136 | { 137 | using (var dbfDataReader = new DbfDataReader(FixturePath)) 138 | { 139 | var columns = dbfDataReader.GetColumnSchema(); 140 | columns.Count.ShouldBe(31); 141 | 142 | using (var dbColumns = columns.GetEnumerator()) 143 | { 144 | dbColumns.MoveNext(); 145 | 146 | foreach (var line in FixtureHelpers.GetFieldLines(FixtureSummaryPath)) 147 | { 148 | var dbColumn = dbColumns.Current; 149 | ValidateColumn(dbColumn, line); 150 | 151 | dbColumns.MoveNext(); 152 | } 153 | } 154 | } 155 | } 156 | 157 | [Fact] 158 | public void Should_get_typed_values() 159 | { 160 | using (var dbfDataReader = new DbfDataReader(FixturePath)) 161 | { 162 | var columns = dbfDataReader.GetColumnSchema(); 163 | columns.Count.ShouldBe(31); 164 | 165 | for (var i = 0; i < dbfDataReader.FieldCount; i++) 166 | { 167 | if (dbfDataReader.IsDBNull(i)) 168 | { 169 | var value = dbfDataReader.GetValue(i); 170 | value.ShouldBeNull(); 171 | continue; 172 | } 173 | 174 | var fieldType = dbfDataReader.GetFieldType(i); 175 | var typeCode = Type.GetTypeCode(fieldType); 176 | switch (typeCode) 177 | { 178 | case TypeCode.Boolean: 179 | dbfDataReader.GetBoolean(i); 180 | break; 181 | case TypeCode.Int32: 182 | dbfDataReader.GetInt32(i); 183 | break; 184 | case TypeCode.DateTime: 185 | dbfDataReader.GetDateTime(i); 186 | break; 187 | case TypeCode.Single: 188 | dbfDataReader.GetFloat(i); 189 | break; 190 | case TypeCode.Double: 191 | dbfDataReader.GetDouble(i); 192 | break; 193 | case TypeCode.Decimal: 194 | dbfDataReader.GetDecimal(i); 195 | break; 196 | case TypeCode.String: 197 | dbfDataReader.GetString(i); 198 | break; 199 | default: 200 | // no cheating 201 | throw new NotSupportedException($"Unsupported field type: {fieldType} for column at index: {i}"); 202 | } 203 | } 204 | } 205 | } 206 | 207 | [Fact] 208 | public void Should_get_valid_schema_table() 209 | { 210 | using (var dbfDataReader = new DbfDataReader(FixturePath)) 211 | { 212 | var schemaTable = dbfDataReader.GetSchemaTable(); 213 | schemaTable.ShouldNotBeNull(); 214 | schemaTable.Rows.Count.ShouldBe(31); 215 | 216 | var index = 0; 217 | var rows = schemaTable.Rows; 218 | foreach (var line in FixtureHelpers.GetFieldLines(FixtureSummaryPath)) 219 | { 220 | var row = rows[index++]; 221 | ValidateRow(row, line); 222 | } 223 | } 224 | } 225 | 226 | private void ValidateRow(DataRow row, string line) 227 | { 228 | var expectedName = line.Substring(0, 16).Trim(); 229 | var dbfColumnType = (DbfColumnType)line.Substring(17, 1)[0]; 230 | var decimalLength = int.Parse(line.Substring(39, 1)); 231 | 232 | var expectedDataType = GetDataType(dbfColumnType, decimalLength); 233 | 234 | row[SchemaTableColumn.ColumnName].ShouldBe(expectedName); 235 | row[SchemaTableColumn.DataType].ShouldBe(expectedDataType); 236 | } 237 | 238 | private static void ValidateColumn(DbColumn dbColumn, string line) 239 | { 240 | var expectedName = line.Substring(0, 16).Trim(); 241 | var dbfColumnType = (DbfColumnType)line.Substring(17, 1)[0]; 242 | var decimalLength = int.Parse(line.Substring(39, 1)); 243 | 244 | var expectedDataType = GetDataType(dbfColumnType, decimalLength); 245 | 246 | dbColumn.ColumnName.ShouldBe(expectedName); 247 | dbColumn.DataType.ShouldBe(expectedDataType); 248 | } 249 | 250 | private static Type GetDataType(DbfColumnType dbfColumnType, int decimalLength) 251 | { 252 | switch (dbfColumnType) 253 | { 254 | case DbfColumnType.Number: 255 | return decimalLength == 0 ? typeof(int) : typeof(decimal); 256 | case DbfColumnType.SignedLong: 257 | return typeof(long); 258 | case DbfColumnType.Float: 259 | return typeof(float); 260 | case DbfColumnType.Currency: 261 | return typeof(decimal); 262 | case DbfColumnType.Date: 263 | return typeof(DateTime); 264 | case DbfColumnType.DateTime: 265 | return typeof(DateTime); 266 | case DbfColumnType.Boolean: 267 | return typeof(bool); 268 | case DbfColumnType.Memo: 269 | return typeof(string); 270 | case DbfColumnType.Double: 271 | return typeof(double); 272 | case DbfColumnType.General: 273 | case DbfColumnType.Character: 274 | return typeof(string); 275 | default: 276 | throw new ArgumentOutOfRangeException(nameof(dbfColumnType), dbfColumnType, null); 277 | } 278 | } 279 | } 280 | } -------------------------------------------------------------------------------- /test/DbfDataReader.Tests/DbfMemo1251Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using Shouldly; 4 | using Xunit; 5 | 6 | namespace DbfDataReader.Tests 7 | { 8 | [Collection("INNAKLKVT20180904")] 9 | public class DbfMemo1251Tests : IDisposable 10 | { 11 | private const string FixturePath = "../../../../fixtures/INNAKLKVT20180904.dbf"; 12 | 13 | public DbfMemo1251Tests() 14 | { 15 | var options = new DbfDataReaderOptions 16 | { 17 | Encoding = Encoding.GetEncoding("WINDOWS-1251") 18 | }; 19 | DbfDataReader = new DbfDataReader(FixturePath, options); 20 | } 21 | 22 | public void Dispose() 23 | { 24 | DbfDataReader.Dispose(); 25 | DbfDataReader = null; 26 | } 27 | 28 | public DbfDataReader DbfDataReader { get; set; } 29 | 30 | [Fact(Skip = "WIP")] 31 | public void Should_be_able_to_read_all_the_rows() 32 | { 33 | var rowCount = 0; 34 | while (DbfDataReader.Read()) 35 | { 36 | rowCount++; 37 | 38 | var valueCol1 = DbfDataReader.GetDateTime(0); 39 | var valueCol2 = DbfDataReader.GetString(1); 40 | 41 | var valueCol14 = DbfDataReader.GetString(13); 42 | } 43 | 44 | rowCount.ShouldBe(14); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /test/DbfDataReader.Tests/DbfTableTests.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Shouldly; 3 | using Xunit; 4 | 5 | namespace DbfDataReader.Tests 6 | { 7 | [Collection("dbase_30")] 8 | public class DbfTableTests 9 | { 10 | [Fact] 11 | public void Should_dispose_stream() 12 | { 13 | const string fixturePath = "../../../../fixtures/dbase_30.dbf"; 14 | 15 | var dbfTable = new DbfTable(fixturePath, Encoding.GetEncoding(1252)); 16 | 17 | dbfTable.Stream.ShouldNotBeNull(); 18 | 19 | dbfTable.Dispose(); 20 | 21 | dbfTable.Stream.ShouldBeNull(); 22 | } 23 | 24 | [Fact] 25 | public void Should_dispose_memo() 26 | { 27 | const string fixturePath = "../../../../fixtures/dbase_30.dbf"; 28 | 29 | var dbfTable = new DbfTable(fixturePath, Encoding.GetEncoding(1252)); 30 | 31 | dbfTable.Memo.ShouldNotBeNull(); 32 | 33 | dbfTable.Dispose(); 34 | 35 | dbfTable.Memo.ShouldBeNull(); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /test/DbfDataReader.Tests/EsriShapefileTests.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using Xunit; 3 | 4 | namespace DbfDataReader.Tests 5 | { 6 | [Collection("tl_2019_01_place")] 7 | public class EsriShapefileTests : DbaseTests 8 | { 9 | private const string Dbase03FixturePath = "../../../../fixtures/tl_2019_01_place.dbf"; 10 | 11 | public EsriShapefileTests() : base(Dbase03FixturePath) 12 | { 13 | } 14 | 15 | [Fact] 16 | public void Should_report_correct_record_count() 17 | { 18 | DbfHeader.RecordCount.ShouldBe(587); 19 | } 20 | 21 | [Fact] 22 | public void Should_report_correct_version_number() 23 | { 24 | DbfHeader.Version.ShouldBe(0x03); 25 | } 26 | 27 | [Fact] 28 | public void Should_report_that_the_file_is_not_foxpro() 29 | { 30 | DbfHeader.IsFoxPro.ShouldBeFalse(); 31 | } 32 | 33 | [Fact] 34 | public void Should_have_the_correct_number_of_columns() 35 | { 36 | DbfTable.Columns.Count.ShouldBe(16); 37 | } 38 | 39 | [Fact] 40 | public void Should_have_the_correct_column_schema() 41 | { 42 | ValidateColumnSchema("../../../../fixtures/tl_2019_01_place_summary.txt"); 43 | } 44 | 45 | [Fact] 46 | [UseCulture("en-GB")] 47 | public void Should_have_correct_row_values() 48 | { 49 | ValidateRowValues("../../../../fixtures/tl_2019_01_place.csv"); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/DbfDataReader.Tests/FixtureHelpers.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | 4 | namespace DbfDataReader.Tests 5 | { 6 | public static class FixtureHelpers 7 | { 8 | public static IEnumerable GetFieldLines(string path) 9 | { 10 | using (var stream = new FileStream(path, FileMode.Open)) 11 | using (var summaryFile = new StreamReader(stream)) 12 | { 13 | var line = summaryFile.ReadLine(); 14 | while (line != null && !line.StartsWith("---")) 15 | { 16 | line = summaryFile.ReadLine(); 17 | } 18 | 19 | line = summaryFile.ReadLine(); 20 | while (line != null) 21 | { 22 | yield return line; 23 | line = summaryFile.ReadLine(); 24 | } 25 | } 26 | } 27 | 28 | } 29 | } -------------------------------------------------------------------------------- /test/DbfDataReader.Tests/FoxproCurrencyTests.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using Xunit; 3 | 4 | namespace DbfDataReader.Tests 5 | { 6 | [Collection("foxpro_currency")] 7 | public class FoxproCurrencyTests : DbaseTests 8 | { 9 | private const string FoxproCurrency01FixturePath = "../../../../fixtures/foxpro_currency_01.dbf"; 10 | 11 | public FoxproCurrencyTests() : base(FoxproCurrency01FixturePath) 12 | { 13 | } 14 | 15 | [Fact] 16 | public void Should_report_correct_record_count() 17 | { 18 | DbfHeader.RecordCount.ShouldBe(3); 19 | } 20 | 21 | [Fact] 22 | public void Should_report_correct_version_number() 23 | { 24 | DbfHeader.Version.ShouldBe(0x30); 25 | } 26 | 27 | [Fact] 28 | public void Should_report_that_the_file_is_foxpro() 29 | { 30 | DbfHeader.IsFoxPro.ShouldBeTrue(); 31 | } 32 | 33 | [Fact] 34 | public void Should_have_the_correct_number_of_columns() 35 | { 36 | DbfTable.Columns.Count.ShouldBe(2); 37 | } 38 | 39 | [Fact] 40 | public void Should_have_the_correct_column_schema() 41 | { 42 | ValidateColumnSchema("../../../../fixtures/foxpro_currency_01_summary.txt"); 43 | } 44 | 45 | [Fact] 46 | [UseCulture("it-IT")] 47 | public void Should_have_correct_row_values() 48 | { 49 | ValidateRowValues("../../../../fixtures/foxpro_currency_01.csv"); 50 | } 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /test/DbfDataReader.Tests/LargeCharacterColumnTests.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using Xunit; 3 | 4 | namespace DbfDataReader.Tests 5 | { 6 | [Collection("FolderRoot")] 7 | public class LargeCharacterColumnTests : DbaseTests 8 | { 9 | private const string FolderRootFixturePath = "../../../../fixtures/FolderRoot.dbf"; 10 | 11 | public LargeCharacterColumnTests() : base(FolderRootFixturePath) 12 | { 13 | } 14 | 15 | [Fact] 16 | public void Should_report_correct_record_count() 17 | { 18 | DbfHeader.RecordCount.ShouldBe(9); 19 | } 20 | 21 | [Fact] 22 | public void Should_report_correct_version_number() 23 | { 24 | DbfHeader.Version.ShouldBe(0x31); 25 | } 26 | 27 | [Fact] 28 | public void Should_report_that_the_file_is_foxpro() 29 | { 30 | DbfHeader.IsFoxPro.ShouldBeTrue(); 31 | } 32 | 33 | [Fact] 34 | public void Should_have_the_correct_number_of_columns() 35 | { 36 | DbfTable.Columns.Count.ShouldBe(10); 37 | } 38 | 39 | [Fact] 40 | public void Should_have_the_correct_column_schema() 41 | { 42 | ValidateColumnSchema("../../../../fixtures/FolderRoot_summary.txt"); 43 | } 44 | 45 | [Fact] 46 | [UseCulture("en-GB")] 47 | public void Should_have_correct_row_values() 48 | { 49 | ValidateRowValues("../../../../fixtures/FolderRoot.csv"); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /test/DbfDataReader.Tests/MS_KHDMTests.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using Xunit; 3 | 4 | namespace DbfDataReader.Tests 5 | { 6 | [Collection("ms_khdm")] 7 | public class MsKhdmTests : DbaseTests 8 | { 9 | private const string MsKhdmFixturePath = "../../../../fixtures/MS__KHDM.DBF"; 10 | 11 | // override default encoding to use 1251 codepage 12 | public MsKhdmTests() 13 | : base(MsKhdmFixturePath, EncodingProvider.GetEncoding(1251)) 14 | { 15 | } 16 | 17 | [Fact] 18 | public void Should_report_correct_record_count() 19 | { 20 | DbfHeader.RecordCount.ShouldBe(193); 21 | } 22 | 23 | [Fact] 24 | public void Should_report_correct_version_number() 25 | { 26 | DbfHeader.Version.ShouldBe(0x03); 27 | } 28 | 29 | [Fact] 30 | public void Should_report_that_the_file_is_not_foxpro() 31 | { 32 | DbfHeader.IsFoxPro.ShouldBeFalse(); 33 | } 34 | 35 | [Fact] 36 | public void Should_have_the_correct_number_of_columns() 37 | { 38 | DbfTable.Columns.Count.ShouldBe(33); 39 | } 40 | 41 | [Fact] 42 | public void Should_have_the_correct_column_schema() 43 | { 44 | ValidateColumnSchema("../../../../fixtures/MS__KHDM_summary.txt"); 45 | } 46 | 47 | [Fact] 48 | public void Should_have_correct_row_values() 49 | { 50 | ValidateRowValues("../../../../fixtures/MS__KHDM.csv"); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /test/DbfDataReader.Tests/UseCultureAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Reflection; 4 | using System.Threading; 5 | using Xunit.Sdk; 6 | 7 | namespace DbfDataReader.Tests 8 | { 9 | /// 10 | /// Apply this attribute to your test method to replace the 11 | /// and 12 | /// with another culture. 13 | /// Note: copied from https://github.com/xunit/samples.xunit/blob/master/UseCulture/UseCultureAttribute.cs 14 | /// 15 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 16 | public class UseCultureAttribute : BeforeAfterTestAttribute 17 | { 18 | readonly Lazy _culture; 19 | readonly Lazy _uiCulture; 20 | 21 | CultureInfo _originalCulture; 22 | CultureInfo _originalUiCulture; 23 | 24 | /// 25 | /// Replaces the culture and UI culture of the current thread with 26 | /// 27 | /// 28 | /// The name of the culture. 29 | /// 30 | /// 31 | /// This constructor overload uses for both 32 | /// and . 33 | /// 34 | /// 35 | public UseCultureAttribute(string culture) 36 | : this(culture, culture) 37 | { 38 | } 39 | 40 | /// 41 | /// Replaces the culture and UI culture of the current thread with 42 | /// and 43 | /// 44 | /// The name of the culture. 45 | /// The name of the UI culture. 46 | public UseCultureAttribute(string culture, string uiCulture) 47 | { 48 | _culture = new Lazy(() => new CultureInfo(culture, false)); 49 | _uiCulture = new Lazy(() => new CultureInfo(uiCulture, false)); 50 | } 51 | 52 | /// 53 | /// Gets the culture. 54 | /// 55 | public CultureInfo Culture => _culture.Value; 56 | 57 | /// 58 | /// Gets the UI culture. 59 | /// 60 | public CultureInfo UiCulture => _uiCulture.Value; 61 | 62 | /// 63 | /// Stores the current 64 | /// and 65 | /// and replaces them with the new cultures defined in the constructor. 66 | /// 67 | /// The method under test 68 | public override void Before(MethodInfo methodUnderTest) 69 | { 70 | _originalCulture = Thread.CurrentThread.CurrentCulture; 71 | _originalUiCulture = Thread.CurrentThread.CurrentUICulture; 72 | 73 | Thread.CurrentThread.CurrentCulture = Culture; 74 | Thread.CurrentThread.CurrentUICulture = UiCulture; 75 | 76 | CultureInfo.CurrentCulture.ClearCachedData(); 77 | CultureInfo.CurrentUICulture.ClearCachedData(); 78 | } 79 | 80 | /// 81 | /// Restores the original and 82 | /// to 83 | /// 84 | /// The method under test 85 | public override void After(MethodInfo methodUnderTest) 86 | { 87 | Thread.CurrentThread.CurrentCulture = _originalCulture; 88 | Thread.CurrentThread.CurrentUICulture = _originalUiCulture; 89 | 90 | CultureInfo.CurrentCulture.ClearCachedData(); 91 | CultureInfo.CurrentUICulture.ClearCachedData(); 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /test/fixtures/FolderRoot.csv: -------------------------------------------------------------------------------- 1 | NAME,DISC_ID,ROOT_TYPE,SYS_PATH,DB_RT_PATH,VOL_NAME,FS_TYPE,PNP_ID,FOLD_RT_ID,TS 2 | C:,F0A65FAE,Local,C:,Local\F0A65FAE,System,NTFS,,1, 3 | D:,F64AADF0,Local,D:,Local\F64AADF0,Data,NTFS,,2, 4 | \\diskserveur,00000000,Remote,\\diskserveur,Remote\diskserveur,,,,3, 5 | G:,4A5A67B8,Removable,G:,Removable\4A5A67B8,,NTFS,,4, 6 | G:,022EAD81,Removable,G:,Removable\022EAD81,USB 4GO,FAT32,,5, 7 | Galaxy S10e,5841b71d-8727-4349-b3db-46fa9ca47660,WPDStorageDrive,Galaxy S10e,WPDStorageDrive\5841b71d-8727-4349-b3db-46fa9ca47660,,,\\?\usb#vid_04e8&pid_6860&ms_comp_mtp&samsung_android#6&22918cac&0&0000#{6ac27878-a6fa-4155-ba85-f98f491d4f33},6, 8 | Mini GS5 (Galaxy S5 mini),bdbc2acc-097f-4488-a3a8-894c94864fe9,WPDStorageDrive,Mini GS5 (Galaxy S5 mini),WPDStorageDrive\bdbc2acc-097f-4488-a3a8-894c94864fe9,,,\\?\usb#vid_04e8&pid_6860&ms_comp_mtp&samsung_android#6&15dd26f&0&0000#{6ac27878-a6fa-4155-ba85-f98f491d4f33},7, 9 | D:,002300CA,Local,D:,Local\002300CA,Data,NTFS,,8, 10 | E:,0DD50EBD,Local,E:,Local\0DD50EBD,Raid1,NTFS,,9, 11 | -------------------------------------------------------------------------------- /test/fixtures/FolderRoot.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/FolderRoot.dbf -------------------------------------------------------------------------------- /test/fixtures/FolderRoot_summary.txt: -------------------------------------------------------------------------------- 1 | 2 | Database: FolderRoot.dbf 3 | Type: (31) Visual FoxPro with AutoIncrement field 4 | Memo File: false 5 | Records: 9 6 | 7 | Fields: 8 | Name Type Length Decimal 9 | ------------------------------------------------------------------------------ 10 | NAME C 1040 0 11 | DISC_ID C 255 0 12 | ROOT_TYPE C 255 0 13 | SYS_PATH C 1040 0 14 | DB_RT_PATH C 600 0 15 | VOL_NAME C 64 0 16 | FS_TYPE C 32 0 17 | PNP_ID C 255 0 18 | FOLD_RT_ID B 8 0 19 | TS 7 8 0 20 | -------------------------------------------------------------------------------- /test/fixtures/INNAKLKVT20180904.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/INNAKLKVT20180904.dbf -------------------------------------------------------------------------------- /test/fixtures/INNAKLKVT20180904.dbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/INNAKLKVT20180904.dbt -------------------------------------------------------------------------------- /test/fixtures/MS__KHDM.DBF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/MS__KHDM.DBF -------------------------------------------------------------------------------- /test/fixtures/MS__KHDM_summary.txt: -------------------------------------------------------------------------------- 1 | 2 | Database: MS__KHDM.DBF 3 | Type: (03) dBase III without memo file 4 | Memo File: false 5 | Records: 193 6 | 7 | Fields: 8 | Name Type Length Decimal 9 | ------------------------------------------------------------------------------ 10 | KHD_CODE C 8 0 11 | KHD_OSTAN C 17 0 12 | KHD_SHAHR C 16 0 13 | KHD_BAKHSH C 16 0 14 | KHD_DEH C 16 0 15 | KHD_STASHI N 1 0 16 | KHD_SABHVA N 1 0 17 | KHD_SMARZI N 1 0 18 | KHD_BTASHI N 1 0 19 | KHD_BABHVA N 1 0 20 | KHD_BMARZI N 1 0 21 | KHD_DTASHI N 1 0 22 | KHD_DMARZI N 1 0 23 | KHD_DABHVA N 1 0 24 | KHD_SP C 9 0 25 | KHD_BP C 9 0 26 | KHD_DP C 9 0 27 | GOROH1 N 1 0 28 | GOROH2 N 1 0 29 | KHD_MAHRM N 1 0 30 | NO_PRN C 1 0 31 | KHD_HAV_HY N 2 0 32 | KHD_JAZ_HY N 2 0 33 | KHD_SGAZB N 1 0 34 | KHD_BGAZB N 1 0 35 | KHD_DGAZB N 1 0 36 | KHD_MIBBAZ N 6 0 37 | KHD_ROT1 N 2 0 38 | KHD_ROT2 N 2 0 39 | KHD_ROT3 N 2 0 40 | KHD_ROT4 N 2 0 41 | KHD_ROT5 N 2 0 42 | KHD_ROT6 N 2 0 43 | -------------------------------------------------------------------------------- /test/fixtures/client.csv: -------------------------------------------------------------------------------- 1 | CLIENT_ID,CLIENT,LASTNAME,FIRSTNAME,ADDRESS,CITY,STATE,ZIP,PHONE,CLIEN_HIST 2 | A00001,"WRIGHT & SONS, LTD.",Wright,Fred,3232 48th St.,New York,NY,10092,(212)555-7474,"85-200 08/02/85 3 | C-300-400 BOOK CASE 535.00 1 4 | " 5 | L00001,BAILEY & BAILEY,Bailey,Sandra,5132 Livingston Dr.,Long Beach,CA,90801,(213)555-1104,"86-245 09/22/86 6 | C-700-2020 FILE CABINET,2 DRAWER 100.00 2 7 | C-700-4030 FILE CABINET,4 DRAWER 150.00 2 8 | 86-303 12/06/87 9 | C-222-1001 CHAIR, DESK 1750.00 1 10 | 87-109 03/09/87 11 | C-400-2060 TABLE, END 250.00 1 12 | C-500-6050 LAMP, FLOOR 165.00 1 13 | 87-112 03/20/87 14 | C-222-3020 CHAIR, SIDE 350.00 2 15 | " 16 | C00001,L. G. BLUM & ASSOCIATES,Martinez,Ric,4818 Allendale Ave.,Santa Fe,NM,87501,(505)555-3232,"86-155 06/31/86 17 | C-600-5050 DESK, SECRETARY 1100.00 1 18 | C-222-3010 CHAIR, SIDE 500.00 1 19 | C-400-2080 TABLE, END 250.00 1 20 | 86-248 09/28/86 21 | C-600-5050 DESK, SECRETARY 1100.00 1 22 | C-222-3010 CHAIR, SIDE 500.00 1 23 | C-700-2020 FILE CABINET,2 DRAWER 100.00 1 24 | C-500-6050 LAMP, FLOOR 1100.00 1 25 | 86-312 12/17/86 26 | C-400-5000 TABLE, COFFEE 875.00 1 27 | 87-106 02/10/87 28 | C-111-8050 SOFA, 8-FOOT 1200.00 1 29 | 87-108 02/23/87 30 | C-222-1000 CHAIR, DESK 1250.00 1 31 | " 32 | L00002,SAWYER LONGFELLOWS,Peters,Kimberly,12300 N. Elm St.,Dallas,TX,75277,(214)555-5603, 33 | A00005,SMITH ASSOCIATES,Yamada,George J.,7500 Santa Monica Blvd.,Los Angeles,CA,90055-1319,(213)555-4300,"85-187 06/07/85 34 | C-111-6045 SOFA, 8-FOOT 1325.00 1 35 | 87-113 03/24/87 36 | C-300-2020 BOOK CASE 125.00 1 37 | " 38 | C00002,"TIMMONS & CASEY, LTD.",Timmons,Gene,310-2090 Comex St.,Vancouver,BC,V6G 1E8,(604)555-7644,"87-107 02/12/87 39 | C-222-1000 CHAIR, DESK 1250.00 1 40 | 87-110 03/09/87 41 | C-700-2020 FILE CABINET,2 DRAWER 75.00 1 42 | C-700-4020 FILE CABINET,4 DRAWER 100.00 1 43 | " 44 | B12000,VOLTAGE IMPORTS,Maxwell,Florence,8506 Habana Ave.,Tampa,FL,33614,(813)555-5522, 45 | A10025,PUBLIC EVENTS,Beckman,Riener,332 S. Michigan Ave.,Pasadena,CA,91125-0001,(818)555-3842,"87-105 02/03/87 46 | C-111-6010 SOFA, 6-FOOT 1200.00 1 47 | C-111-6015 SOFA, 6-FOOT 650.00 1 48 | " -------------------------------------------------------------------------------- /test/fixtures/client.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/client.dbf -------------------------------------------------------------------------------- /test/fixtures/client.dbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/client.dbt -------------------------------------------------------------------------------- /test/fixtures/client_summary.txt: -------------------------------------------------------------------------------- 1 | 2 | Database: client.dbf 3 | Type: (8b) dBase IV with memo file 4 | Memo File: true 5 | Records: 8 6 | 7 | Fields: 8 | Name Type Length Decimal 9 | ------------------------------------------------------------------------------ 10 | CLIENT_ID C 6 0 11 | CLIENT C 30 0 12 | LASTNAME C 15 0 13 | FIRSTNAME C 15 0 14 | ADDRESS C 30 0 15 | CITY C 20 0 16 | STATE C 2 0 17 | ZIP C 10 0 18 | PHONE C 13 0 19 | CLIEN_HIST M 10 0 20 | -------------------------------------------------------------------------------- /test/fixtures/cp1251.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/cp1251.dbf -------------------------------------------------------------------------------- /test/fixtures/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 | -------------------------------------------------------------------------------- /test/fixtures/dbase_03.csv: -------------------------------------------------------------------------------- 1 | Point_ID,Type,Shape,Circular_D,Non_circul,Flow_prese,Condition,Comments,Date_Visit,Time,Max_PDOP,Max_HDOP,Corr_Type,Rcvr_Type,GPS_Date,GPS_Time,Update_Sta,Feat_Name,Datafile,Unfilt_Pos,Filt_Pos,Data_Dicti,GPS_Week,GPS_Second,GPS_Height,Vert_Prec,Horz_Prec,Std_Dev,Northing,Easting,Point_ID,deleted 2 | 0507121,CMP,circular,12,,no,Good,,12/07/2005,10:56:30am,5.2,2.0,Postprocessed Code,GeoXT,12/07/2005,10:56:52am,New,Driveway,050712TR2819.cor,2,2,MS4,1331,226625.000,1131.323,3.1,1.3,0.897088,557904.898,2212577.192,401,F 3 | 0507122,CMP,circular,12,,no,Good,,12/07/2005,10:57:34am,4.9,2.0,Postprocessed Code,GeoXT,12/07/2005,10:57:37am,New,Driveway,050712TR2819.cor,1,1,MS4,1331,226670.000,1125.142,2.8,1.3,,557997.831,2212576.868,402,F 4 | 0507123,CMP,circular,12,,no,Good,,12/07/2005,10:59:03am,5.4,4.4,Postprocessed Code,GeoXT,12/07/2005,10:59:12am,New,Driveway,050712TR2819.cor,1,1,MS4,1331,226765.000,1127.570,2.2,3.5,,558184.757,2212571.349,403,F 5 | 0507125,CMP,circular,12,,no,Good,,12/07/2005,11:02:43am,3.4,1.5,Postprocessed Code,GeoXT,12/07/2005,11:03:12am,New,Driveway,050712TR2819.cor,1,1,MS4,1331,227005.000,1125.364,3.2,1.6,,558703.723,2212562.547,405,F 6 | 05071210,CMP,circular,15,,no,Good,,12/07/2005,11:15:20am,3.7,2.2,Postprocessed Code,GeoXT,12/07/2005,11:14:52am,New,Driveway,050712TR2819.cor,1,1,MS4,1331,227705.000,1118.605,1.8,2.1,,558945.763,2212739.979,410,F 7 | 05071216,CMP,circular,12,,no,Good,,12/07/2005,12:13:23pm,4.4,1.8,Postprocessed Code,GeoXT,12/07/2005,12:13:57pm,New,Driveway,050712TR2819.cor,1,1,MS4,1331,231250.000,1117.390,3.1,1.2,,559024.234,2212856.927,416,F 8 | 05071217,CMP,circular,12,,no,Good,,12/07/2005,12:16:46pm,4.4,1.8,Postprocessed Code,GeoXT,12/07/2005,12:17:12pm,New,Driveway,050712TR2819.cor,1,1,MS4,1331,231445.000,1125.714,3.2,1.3,,559342.534,2213340.161,417,F 9 | 05071219,CMP,circular,12,,no,Plugged,,12/07/2005,12:22:55pm,4.4,1.8,Postprocessed Code,GeoXT,12/07/2005,12:22:22pm,New,Driveway,050712TR2819.cor,1,1,MS4,1331,231755.000,1110.786,2.5,1.1,,559578.776,2213560.247,419,F 10 | 05071224,CMP,circular,12,,no,Good,,12/07/2005,12:37:17pm,4.1,1.7,Postprocessed Code,GeoXT,12/07/2005,12:38:32pm,New,Driveway,050712TR2819.cor,1,1,MS4,1331,232725.000,1077.924,2.8,1.4,,560582.575,2213759.022,424,F 11 | 05071225,CMP,circular,12,,no,Good,,12/07/2005,12:39:48pm,4.0,1.7,Postprocessed Code,GeoXT,12/07/2005,12:39:52pm,New,Driveway,050712TR2819.cor,1,1,MS4,1331,232805.000,1082.990,2.0,1.0,,560678.501,2213716.657,425,F 12 | 05071229,CMP,circular,12,,no,Good,,12/07/2005,12:49:05pm,3.7,1.7,Postprocessed Code,GeoXT,12/07/2005,12:49:07pm,New,Driveway,050712TR2819.cor,1,1,MS4,1331,233360.000,1096.860,2.4,1.2,,560126.094,2213720.301,429,F 13 | 05071231,CMP,circular,12,,no,Plugged,,12/07/2005,12:53:58pm,3.0,1.6,Postprocessed Code,GeoXT,12/07/2005,12:54:02pm,New,Driveway,050712TR2819.cor,1,1,MS4,1331,233655.000,1105.113,1.8,1.1,,559952.331,2213689.001,431,F 14 | 05071232,CMP,circular,12,,no,Plugged,,12/07/2005,12:55:47pm,3.5,1.7,Postprocessed Code,GeoXT,12/07/2005,12:55:47pm,New,Driveway,050712TR2819.cor,2,2,MS4,1331,233760.000,1101.939,2.1,1.1,1.223112,559870.352,2213661.918,432,F 15 | 05071236,CMP,circular,12,,no,Plugged,,12/07/2005,01:08:40pm,3.3,1.6,Postprocessed Code,GeoXT,12/07/2005,01:08:42pm,New,Driveway,050712TR2819.cor,1,1,MS4,1331,234535.000,1125.517,1.8,1.2,,559195.031,2213046.199,436,F 16 | -------------------------------------------------------------------------------- /test/fixtures/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 -------------------------------------------------------------------------------- /test/fixtures/dbase_03_nullchar.csv: -------------------------------------------------------------------------------- 1 | TXTCOL,OTHERTXT 2 | 1234test,5678test 3 | ,ping 4 | test1234, -------------------------------------------------------------------------------- /test/fixtures/dbase_03_nullchar.dbf: -------------------------------------------------------------------------------- 1 | a3TXTCOLCOTHERTXTC 1234test 5678test reak ping test1234  -------------------------------------------------------------------------------- /test/fixtures/dbase_03_nullchar_summary.txt: -------------------------------------------------------------------------------- 1 | 2 | Database: dbase_03_nullchar.dbf 3 | Type: (03) dBase III without memo file 4 | Memo File: false 5 | Records: 3 6 | 7 | Fields: 8 | Name Type Length Decimal 9 | ------------------------------------------------------------------------------ 10 | TXTCOL C 25 0 11 | OTHERTXT C 25 0 12 | -------------------------------------------------------------------------------- /test/fixtures/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 | -------------------------------------------------------------------------------- /test/fixtures/dbase_30.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/dbase_30.dbf -------------------------------------------------------------------------------- /test/fixtures/dbase_30.fpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/dbase_30.fpt -------------------------------------------------------------------------------- /test/fixtures/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 B 8 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 F 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 | -------------------------------------------------------------------------------- /test/fixtures/dbase_31.csv: -------------------------------------------------------------------------------- 1 | PRODUCTID,PRODUCTNAM,SUPPLIERID,CATEGORYID,QUANTITYPE,UNITPRICE,UNITSINSTO,UNITSONORD,REORDERLEV,DISCONTINU,FLOAT,DOUBLE,_NullFlags 2 | 1,Chai,1,1,10 boxes x 20 bags,18.0000,39,0,10,F,0.00,0, 3 | 2,Chang,1,1,24 - 12 oz bottles,19.0000,17,40,25,F,42.50,33.33, 4 | 3,Aniseed Syrup,1,2,12 - 550 ml bottles,10.0000,13,70,25,F,0.00,0, 5 | 4,Chef Anton's Cajun Seasoning,2,2,48 - 6 oz jars,22.0000,53,0,0,F,55.21,0, 6 | 5,Chef Anton's Gumbo Mix,2,2,36 boxes,21.3500,0,0,0,T,0.00,43.23, 7 | 6,Grandma's Boysenberry Spread,3,2,12 - 8 oz jars,25.0000,120,0,25,F,0.00,0, 8 | 7,Uncle Bob's Organic Dried Pears,3,7,12 - 1 lb pkgs.,30.0000,15,0,10,F,66.24,0, 9 | 8,Northwoods Cranberry Sauce,3,2,12 - 12 oz jars,40.0000,6,0,0,F,0.00,55.77, 10 | 9,Mishi Kobe Niku,4,6,18 - 500 g pkgs.,97.0000,29,0,0,T,72.45,0, 11 | 10,Ikura,4,8,12 - 200 ml jars,31.0000,31,0,0,F,0.00,0, 12 | 11,Queso Cabrales,5,4,1 kg pkg.,21.0000,22,30,30,F,1.50,7.8, 13 | 12,Queso Manchego La Pastora,5,4,10 - 500 g pkgs.,38.0000,86,0,0,F,4.20,88.42, 14 | 13,Konbu,6,8,2 kg box,6.0000,24,0,5,F,0.00,0, 15 | 14,Tofu,6,7,40 - 100 g pkgs.,23.2500,35,0,0,F,45452.20,0, 16 | 15,Genen Shouyu,6,2,24 - 250 ml bottles,15.5000,39,0,5,F,0.00,923.23, 17 | 16,Pavlova,7,3,32 - 500 g boxes,17.4500,29,0,10,F,23.23,0, 18 | 17,Alice Mutton,7,6,20 - 1 kg tins,39.0000,0,0,0,T,0.00,0, 19 | 18,Carnarvon Tigers,7,8,16 kg pkg.,62.5000,42,0,0,F,0.00,0, 20 | 19,Teatime Chocolate Biscuits,8,3,10 boxes x 12 pieces,9.2000,25,0,5,F,0.00,7.8, 21 | 20,Sir Rodney's Marmalade,8,3,30 gift boxes,81.0000,40,0,0,F,0.00,0, 22 | 21,Sir Rodney's Scones,8,3,24 pkgs. x 4 pieces,10.0000,3,40,5,F,0.00,0, 23 | 22,Gustaf's Knäckebröd,9,5,24 - 500 g pkgs.,21.0000,104,0,25,F,0.00,0, 24 | 23,Tunnbröd,9,5,12 - 250 g pkgs.,9.0000,61,0,25,F,0.00,0, 25 | 24,Guaran  Fant stica,10,1,12 - 355 ml cans,4.5000,20,0,0,T,0.00,0, 26 | 25,NuNuCa Nuá-Nougat-Creme,11,3,20 - 450 g glasses,14.0000,76,0,30,F,0.00,0, 27 | 26,Gumbär Gummibärchen,11,3,100 - 250 g bags,31.2300,15,0,0,F,0.00,0, 28 | 27,Schoggi Schokolade,11,3,100 - 100 g pieces,43.9000,49,0,30,F,0.00,0, 29 | 28,Rössle Sauerkraut,12,7,25 - 825 g cans,45.6000,26,0,0,T,0.00,0, 30 | 29,Thüringer Rostbratwurst,12,6,50 bags x 30 sausgs.,123.7900,0,0,0,T,0.00,0, 31 | 30,Nord-Ost Matjeshering,13,8,10 - 200 g glasses,25.8900,10,0,15,F,0.00,0, 32 | 31,Gorgonzola Telino,14,4,12 - 100 g pkgs,12.5000,0,70,20,F,0.00,0, 33 | 32,Mascarpone Fabioli,14,4,24 - 200 g pkgs.,32.0000,9,40,25,F,0.00,0, 34 | 33,Geitost,15,4,500 g,2.5000,112,0,20,F,0.00,0, 35 | 34,Sasquatch Ale,16,1,24 - 12 oz bottles,14.0000,111,0,15,F,0.00,0, 36 | 35,Steeleye Stout,16,1,24 - 12 oz bottles,18.0000,20,0,15,F,0.00,0, 37 | 36,Inlagd Sill,17,8,24 - 250 g jars,19.0000,112,0,20,F,0.00,0, 38 | 37,Gravad lax,17,8,12 - 500 g pkgs.,26.0000,11,50,25,F,0.00,0, 39 | 38,Côte de Blaye,18,1,12 - 75 cl bottles,263.5000,17,0,15,F,0.00,0, 40 | 39,Chartreuse verte,18,1,750 cc per bottle,18.0000,69,0,5,F,0.00,0, 41 | 40,Boston Crab Meat,19,8,24 - 4 oz tins,18.4000,123,0,30,F,0.00,0, 42 | 41,Jack's New England Clam Chowder,19,8,12 - 12 oz cans,9.6500,85,0,10,F,0.00,0, 43 | 42,Singaporean Hokkien Fried Mee,20,5,32 - 1 kg pkgs.,14.0000,26,0,0,T,0.00,0, 44 | 43,Ipoh Coffee,20,1,16 - 500 g tins,46.0000,17,10,25,F,0.00,0, 45 | 44,Gula Malacca,20,2,20 - 2 kg bags,19.4500,27,0,15,F,0.00,0, 46 | 45,Rogede sild,21,8,1k pkg.,9.5000,5,70,15,F,0.00,0, 47 | 46,Spegesild,21,8,4 - 450 g glasses,12.0000,95,0,0,F,0.00,0, 48 | 47,Zaanse koeken,22,3,10 - 4 oz boxes,9.5000,36,0,0,F,0.00,0, 49 | 48,Chocolade,22,3,10 pkgs.,12.7500,15,70,25,F,0.00,0, 50 | 49,Maxilaku,23,3,24 - 50 g pkgs.,20.0000,10,60,15,F,0.00,0, 51 | 50,Valkoinen suklaa,23,3,12 - 100 g bars,16.2500,65,0,30,F,0.00,0, 52 | 51,Manjimup Dried Apples,24,7,50 - 300 g pkgs.,53.0000,20,0,10,F,0.00,0, 53 | 52,Filo Mix,24,5,16 - 2 kg boxes,7.0000,38,0,25,F,0.00,0, 54 | 53,Perth Pasties,24,6,48 pieces,32.8000,0,0,0,T,0.00,0, 55 | 54,Tourtière,25,6,16 pies,7.4500,21,0,10,F,0.00,0, 56 | 55,Pâté chinois,25,6,24 boxes x 2 pies,24.0000,115,0,20,F,0.00,0, 57 | 56,Gnocchi di nonna Alice,26,5,24 - 250 g pkgs.,38.0000,21,10,30,F,0.00,0, 58 | 57,Ravioli Angelo,26,5,24 - 250 g pkgs.,19.5000,36,0,20,F,0.00,0, 59 | 58,Escargots de Bourgogne,27,8,24 pieces,13.2500,62,0,20,F,0.00,0, 60 | 59,Raclette Courdavault,28,4,5 kg pkg.,55.0000,79,0,0,F,0.00,0, 61 | 60,Camembert Pierrot,28,4,15 - 300 g rounds,34.0000,19,0,0,F,0.00,0, 62 | 61,Sirop d'érable,29,2,24 - 500 ml bottles,28.5000,113,0,25,F,0.00,0, 63 | 62,Tarte au sucre,29,3,48 pies,49.3000,17,0,0,F,0.00,0, 64 | 63,Vegie-spread,7,2,15 - 625 g jars,43.9000,24,0,5,F,0.00,0, 65 | 64,Wimmers gute Semmelknödel,12,5,20 bags x 4 pieces,33.2500,22,80,30,F,0.00,0, 66 | 65,Louisiana Fiery Hot Pepper Sauce,2,2,32 - 8 oz bottles,21.0500,76,0,0,F,0.00,0, 67 | 66,Louisiana Hot Spiced Okra,2,2,24 - 8 oz jars,17.0000,4,100,20,F,0.00,0, 68 | 67,Laughing Lumberjack Lager,16,1,24 - 12 oz bottles,14.0000,52,0,10,F,0.00,0, 69 | 68,Scottish Longbreads,8,3,10 boxes x 8 pieces,12.5000,6,10,15,F,0.00,0, 70 | 69,Gudbrandsdalsost,15,4,10 kg pkg.,36.0000,26,0,15,F,0.00,0, 71 | 70,Outback Lager,7,1,24 - 355 ml bottles,15.0000,15,10,30,F,0.00,0, 72 | 71,Flotemysost,15,4,10 - 500 g pkgs.,21.5000,26,0,0,F,0.00,0, 73 | 72,Mozzarella di Giovanni,14,4,24 - 200 g pkgs.,34.8000,14,0,0,F,0.00,0, 74 | 73,Röd Kaviar,17,8,24 - 150 g jars,15.0000,101,0,5,F,0.00,0, 75 | 74,Longlife Tofu,4,7,5 kg pkg.,10.0000,4,20,5,F,0.00,0, 76 | 75,Rhönbräu Klosterbier,12,1,24 - 0.5 l bottles,7.7500,125,0,25,F,0.00,0, 77 | 76,Lakkalikööri,23,1,500 ml,18.0000,57,0,20,F,0.00,0, 78 | 77,Original Frankfurter grüne Soáe,12,2,12 boxes,13.0000,32,0,15,F,0.00,0, 79 | -------------------------------------------------------------------------------- /test/fixtures/dbase_31.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/dbase_31.dbf -------------------------------------------------------------------------------- /test/fixtures/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 | FLOAT F 10 2 21 | DOUBLE B 8 0 22 | _NullFlags 0 1 0 23 | -------------------------------------------------------------------------------- /test/fixtures/dbase_83.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/dbase_83.dbf -------------------------------------------------------------------------------- /test/fixtures/dbase_83.dbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/dbase_83.dbt -------------------------------------------------------------------------------- /test/fixtures/dbase_83_missing_memo.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/dbase_83_missing_memo.dbf -------------------------------------------------------------------------------- /test/fixtures/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 | -------------------------------------------------------------------------------- /test/fixtures/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 | -------------------------------------------------------------------------------- /test/fixtures/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 | -------------------------------------------------------------------------------- /test/fixtures/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 -------------------------------------------------------------------------------- /test/fixtures/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 | -------------------------------------------------------------------------------- /test/fixtures/dbase_8b.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/dbase_8b.dbf -------------------------------------------------------------------------------- /test/fixtures/dbase_8b.dbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/dbase_8b.dbt -------------------------------------------------------------------------------- /test/fixtures/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 | -------------------------------------------------------------------------------- /test/fixtures/dbase_f5.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/dbase_f5.dbf -------------------------------------------------------------------------------- /test/fixtures/dbase_f5.fpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/dbase_f5.fpt -------------------------------------------------------------------------------- /test/fixtures/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 | -------------------------------------------------------------------------------- /test/fixtures/foxpro_currency_01.csv: -------------------------------------------------------------------------------- 1 | currencyp,currencyn 2 | 20,-20 3 | 50,-50 4 | 1,-1 -------------------------------------------------------------------------------- /test/fixtures/foxpro_currency_01.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/foxpro_currency_01.dbf -------------------------------------------------------------------------------- /test/fixtures/foxpro_currency_01_summary.txt: -------------------------------------------------------------------------------- 1 | 2 | Database: dbase_03_nullchar.dbf 3 | Type: (03) dBase III without memo file 4 | Memo File: false 5 | Records: 3 6 | 7 | Fields: 8 | Name Type Length Decimal 9 | ------------------------------------------------------------------------------ 10 | currencyp Y 8 0 11 | currencyn Y 8 0 12 | -------------------------------------------------------------------------------- /test/fixtures/foxprodb/FOXPRO-DB-TEST.DBC: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/foxprodb/FOXPRO-DB-TEST.DBC -------------------------------------------------------------------------------- /test/fixtures/foxprodb/FOXPRO-DB-TEST.DCT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/foxprodb/FOXPRO-DB-TEST.DCT -------------------------------------------------------------------------------- /test/fixtures/foxprodb/FOXPRO-DB-TEST.DCX: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/foxprodb/FOXPRO-DB-TEST.DCX -------------------------------------------------------------------------------- /test/fixtures/foxprodb/calls.CDX: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/foxprodb/calls.CDX -------------------------------------------------------------------------------- /test/fixtures/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. -------------------------------------------------------------------------------- /test/fixtures/foxprodb/calls.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/foxprodb/calls.dbf -------------------------------------------------------------------------------- /test/fixtures/foxprodb/contacts.CDX: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/foxprodb/contacts.CDX -------------------------------------------------------------------------------- /test/fixtures/foxprodb/contacts.FPT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/foxprodb/contacts.FPT -------------------------------------------------------------------------------- /test/fixtures/foxprodb/contacts.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/foxprodb/contacts.dbf -------------------------------------------------------------------------------- /test/fixtures/foxprodb/setup.CDX: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/foxprodb/setup.CDX -------------------------------------------------------------------------------- /test/fixtures/foxprodb/setup.dbf: -------------------------------------------------------------------------------- 1 | 0h7KEY_NAMEC2VALUEI3 foxpro-db-test.dbc CALLS  CONTACTS  CONTACT_TYPES  -------------------------------------------------------------------------------- /test/fixtures/foxprodb/types.CDX: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yellowfeather/DbfDataReader/cb514beb49b50e12ac4ff633e8ca9dcaca51aee6/test/fixtures/foxprodb/types.CDX -------------------------------------------------------------------------------- /test/fixtures/foxprodb/types.dbf: -------------------------------------------------------------------------------- 1 | 0h7CONTACT_TYICONTACT_T2C2 foxpro-db-test.dbc Buyer Seller  -------------------------------------------------------------------------------- /test/fixtures/tl_2019_01_place_summary.txt: -------------------------------------------------------------------------------- 1 | 2 | Database: tl_2019_01_place.dbf 3 | Type: (03) dBase III without memo file 4 | Memo File: false 5 | Records: 586 6 | 7 | Fields: 8 | Name Type Length Decimal 9 | ------------------------------------------------------------------------------ 10 | STATEFP C 2 0 11 | PLACEFP C 5 0 12 | PLACENS C 8 0 13 | GEOID C 7 0 14 | NAME C 100 0 15 | NAMELSAD C 100 0 16 | LSAD C 2 0 17 | CLASSFP C 2 0 18 | PCICBSA C 1 0 19 | PCINECTA C 1 0 20 | MTFCC C 5 0 21 | FUNCSTAT C 1 0 22 | ALAND N 14 0 23 | AWATER N 14 0 24 | INTPTLAT C 11 0 25 | INTPTLON C 12 0 26 | --------------------------------------------------------------------------------