Repository files navigation

FixedWidthParserWriter

Library (C# .NET) for Parsing(Reading) & Writing fixed-width/flat data files (.txt, others).
Uses FastMember instead of slower Reflection.
It is targeting NetStandard 2.0 so it can be used on project targeting NetCore(2.0+) or NetFramework(4.6.1+).
Config base on POCO class/object model with Attributes.

There are 2 main types of usage that are explained in the following segments:
1. LineFields
2. FileFields and
3. CustomFileFields (additional third type or 2.b)
All are simple to use and easily configured with Attributes.

Logo:

NuGet

Installation

Available on NuGet latest version.
Package manager console command for installation: Install-Package FixedWidthParserWriter

Support

If you find this project useful you can mark it by leaving a Github Star
And even with community license, if you want help development, you can make a DONATION:
Buy Me A Coffee _ or _ Button
Want to Contact for Development & Consulting: www.codis.tech (Quality Assurance)

Contributing

Please read CONTRIBUTING for details on code of conduct, and the process for submitting pull requests.
When opening issues do write detailed explanation of the problem or feature with reproducible example.

**Also take a look into others packages:
Open source (MIT or cFOSS) authored .Net libraries (@Infopedia.io personal blog post)

.Net libraryDescription
1EFCore.BulkExtensionsEF Core Bulk CRUD Ops (Flagship Lib)
2EFCore.UtilExtensionsEF Core Custom Annotations and AuditInfo
3EFCore.FluentApiToAnnotationConverting FluentApi configuration to Annotations
4*FixedWidthParserWriterReading & Writing fixed-width/flat data files
5CsCodeGeneratorC# code generation based on Classes and elements
6CsCodeExampleExamples of C# code in form of a simple tutorial

CONFIG

FixedWidthConfig options (provides configuration for the Provider): defaultValue

intStructureTypeId:0	...............................// for multiple Attributes structureDictionary<string,FixedWidthAttribute> DynamicSettings	// Attributes defined at runtimebool LogAndSkipErrors:false .......................... // when True parsing exception skippedList<string> ErrorsLog :newList<string>() ...........// output field, logs ErrorMessagesList<string> WarningsLog :new List<string>() .........// output field, List for Warnings

*-Special feature is 'DYNAMIC Settings' with which Attributes values can be defined at runtime, for all usage types.
Data is forwarded using Dict with PropertyName and new independent Attribute with parameter values:
Dictionary<string, FixedWidthAttribute> dynamicSettings.
It can be set for all needed Properties when having no Attributes, or just add/override some specific.
And if need to exclude ones that has regular Atribute then set it with Null.
Sample in test LineParserTest.

WarningsLog filled when Writing string is cut to fit into defined position.

1. Data in LineFields

First is regular flat data file;
Record per Line (Fixed-Width), for example:

No | Description | Qty | Price | Amount |
1.Laptop Dell xps13 1 821.00 821.00
2.Monitor Asus 32'' 2 478.00 956.00

For parsing/writing we make a model which Properties have [FixedWidthLineField] Attribute:

publicclassInvoiceItem{[FixedWidthLineField(Start=1,Length=3)]publicintNumber{get;set;}[FixedWidthLineField(Start=4,Length=1)]publicstringNumberedBullet{get;set;}=".";[FixedWidthLineField(Start=5,Length=30)]publicstringDescription{get;set;}[FixedWidthLineField(Start=35,Length=6)]publicintQuantity{get;set;}[FixedWidthLineField(Start=41,Length=13)]publicdecimalPrice{get;set;}[FixedWidthLineField(Start=54,Length=13)]publicdecimalAmount=>Quantity*Price;}

Then we can call it like this:

// dataLines stripped of headerpublicList<Invoiceitem>ParseFieldsFromLines(newList<string> dataLines){
List<InvoiceItem> invoiceItems =newFixedWidthLinesProvider<InvoiceItem>().Parse(dataLines);returninvoiceItems;}publicList<string>WriteFieldsToLines(List<InvoiceItem>invoiceItems){List<string>dataLines=newFixedWidthLinesProvider<InvoiceItem>().Write(invoiceItems);returndataLines;}

[FixedWidthLineField] has following parameters that can be configured for each Property:

  • Start - required for LineType so that order of lineFields does not depends on order of modelPropertis
  • Length - when writing if Property value longer then defined in Length it is cut from the right to fit - valueTrim;
    zero 0 value means entire line and negative values means start from Right side
  • Format - Defaults per data type or group
  • Pad - Defaults per data category: { PadNumeric = ' ', PadNonNumeric = ' ' }
  • PadSide - Defaults per data category: { PadSideNumeric = PadSide.Left, PadSideNonNumeric = PadSide.Right }
  • DoTrim - Default is 'True' when text will be trimmed before casting
  • StructureTypeId - Default = 0, used when having multiple files with different structure or format for same data
  • NullPattern - Default = string.Empty, Pattern used to represent a null value

*_Format types:
-FormatNumberInteger Default = "0", *groupFormat:Int32,Int64
-FormatNumberDecimal Default = "0.00", *groupFormat:Decimal,Single,Double
("0;00" - Special custom Format that removes decimal separator: 123.45 -> 12345)
-FormatBoolean . . . . . . Default = "1; ;0" ("ValueForTrue; ValueForNull; ValueForFalse")
-FormatDateTime. . . . . .Default = "yyyyMMdd"
Custom format strings for Numeric and DateTime.

When need more then 1 file structure/format we can put multiple Attributes per Property with different StructureTypeId.
Next example shows 2 structures, second has one less Property and different PadNumeric: '0' instead of ' '(space).
To change DefaultConfig per StructureType, model should implement IFixedWidth interface with SetDefaultConfig() func.

publicenumConfigType{Alpha,Beta}publicclassInvoiceItem:IFixedWidth{publicDefaultConfigGetDefaultConfig(intStructureTypeId){vardefaultConfig=newDefaultConfig();switch((ConfigType)StructureTypeId){caseConfigType.Alpha:// config remains initial defaultbreak;caseConfigType.Beta:defaultConfig.PadNumeric='0';break;}returndefaultConfig;}[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=1,Length=3)][FixedWidthLineField(StructureTypeId=(int)ConfigType.Beta,Start=1,Length=4)]publicintNumber{get;set;}[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=4,Length=1)]publicstringNumberedBullet{get;set;}=".";[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=5,Length=30)][FixedWidthLineField(StructureTypeId=(int)ConfigType.Beta,Start=5,Length=30)]publicstringDescription{get;set;}//... Others Properties}

Beta Structure:

No | Description | Qty | Price | Amount |
0001Laptop Dell xps13 0000010000000821.000000000821.00
0002Monitor Asus 32'' 0000020000000478.000000000956.00

Calling the methods:

var linesProvider = new FixedWidthLinesProvider<InvoiceItem>();
List<InvoiceItem> itemsA = linesProvider.Parse(dataLinesA, (int)ConfigType.Alpha);
List<InvoiceItem> itemsB = linesProvider.Parse(dataLinesB, (int)ConfigType.Alpha);

PARSE method for all use cases can also optionally have third parameter List<string> errorLog which when sent as Empty list(not null) will be loaded with list of Exceptions if any were to happen during parsing and casting operations. When method is called without this param, which remains null, in that case first error with throw Exception and procedure will be stopped.

Full Examples are in Tests of the project.

2. Data in FileFields

Second usage is when one data record is in different rows at defined positions;
Record per File (Fixed/Relative-Height), E.g.:

SoftysTech LCC
__________________________________________________________________
Invoice Date: 2018-10-30 Buyer: SysCompanik
INVOICE no. 0169/18
No | Description | Qty | Price | Amount |
...
...
------------------------------------------------------------------
1,299.00 Date: 2018-10-31 Financial Manager
John Doe

For parsing/writing [FixedWidthFileField] attributes are used, that have additional parameter:

  • Line - in which we define line number where the value is (Negative values represent row number from bottom)

For type FileField param. Length not required, if not set means value goes till end of row(trimmed), and Start has default = 1.

publicclassInvoice{[FixedWidthFileField(Line=1)]publicstringCompanyName{get;set;}[FixedWidthFileField(Line=4,Start=15,Length=19,Format="yyyy-MM-dd")]publicDateTimeDate{get;set;}[FixedWidthFileField(Line=4,Start=43)]publicstringBuyerName{get;set;}[FixedWidthFileField(Line=6,Start=37)]publicstringInvoiceNumber{get;set;}[FixedWidthFileField(Line=-4,Length=66,Format="0,000.00")]publicdecimalAmountTotal{get;set;}[FixedWidthFileField(Line=-2,Start=7,Length=10,Format="yyyy-MM-dd")]publicDateTimeDateCreated{get;set;}[FixedWidthFileField(Line=-2,Start=17,Length=50,PadSide=PadSide.Left)]publicstringSignatoryTitle{get;set;}// Line Negative - counted from bottom [FixedWidthFileField(Line=-1,Length=66,PadSide=PadSide.Left)]publicstringSignatureName{get;set;}}

Usage:

publicInvoiceParseFieldsFromFile(newList<string> fileLines){
invoice invoice =newFixedWidthFileProvider<Invoice>().Parse(fileLines);returninvoice;}publicList<string>WriteFieldsToFile(Invoice){List<string>templateLines=GetDataFormTemplate();varfileProvider=newFixedWidthFileProvider<Invoice>(){Content=templateLines};fileProvider.UpdateContent(invoice);invoice.UpdateContent();returninvoice.Content;}

DataFormTemplate looks like this:

{CompanyName}
__________________________________________________________________
Invoice Date: {InvoiceDate} Buyer: {BuyerName}
INVOICE no. NNNN/YY
No | Description | Qty | Price | Amount |
...
...
------------------------------------------------------------------
0.00 Date: {DateCreated} {SignatoryTitle}
{SignatureName}

In situation where many same type properties have Format different from default one, instead of setting custom format individually for each Property, it is possible to override DefaultConfig for certain data types/groups in that class:

publicclassInvoice:IFixedWidth{publicDefaultConfigGetDefaultConfig(intStructureTypeId){returnnewDefaultConfig{FormatDateTime="yyyy-MM-dd"};}[FixedWidthFileField(Line=1)]publicstringCompanyName{get;set;}// Format set on class with FormatDateTime - not required on each Attribute of DateTime Property[FixedWidthFileField(Line=4,Start=15,Length=19/*, Format = "yyyy-MM-dd"*/)]publicDateTimeDate{get;set;}/* ... Other Properties */}

If we need to changed DefaultConfig(Format) for multiple models then we could override entire Provider to keep it DRY.

-Combining both previous usages we can make complex file structures like invoiceFull.
-In adddition we could also have grouped line with say header per segment in which case we can use multiple Line Models and then perfilter lines based on category, example invoiceGrouped.

When there is some special situation where some Fields can not be configured with existing options, then we can make additional custom parsing prior or/and after calling the method - PRE & POST Processing.

3. Data in CustomFileFields

Third use case (Parsing only) is when one data field is relative to some text position.

SoftysTech LCC company
__________________________________________________________________
Date generated: 30.06.2020.
Q REPORT no. 11/20** 1569ccACTN.162-677-169-796
...
Revenue:
1,234.55
...
xx
publicclassReport{[CustomFileField(EndsWith=" company")]publicstringCompanyName{get;set;}[CustomFileField(StartsWith="Date generated: ",Format="d.M.yyyy.")]publicDateTimeDate{get;set;}[CustomFileField(Contains="Q REPORT no. ",RemoveText="**")]publicstringNumber{get;set;}[CustomFileField(Contains="ACTN.",Length=-15)]publicstringAccount{get;set;}[CustomFileField(StartsWith="Revenue:",Offset=1)]publicdecimalRevenue{get;set;}}

For parsing [CustomFileField] attributes are used, with additional params:

  • StartsWith, EndsWith, Contains - finds lines with first occurance of search criteria
  • Offset - moves found line up(is positive) or down(negative value) for defined number of rows
  • RemoveText - to clear custom substring from text value before additional casting
  • RemoveStartsWith, RemoveEndsWith, RemoveContains - defaults are 'True' meaning that search string is also cleared

Code Structure:

FixedWidthBaseProvider { ParseData(); WriteData() }
|
|---FixedWidthLinesProvider : FWBaseProvider { Parse(); Write() }
|---FixedWidthFileProvider : FWBaseProvider { Parse(); UpdateContent() }
|---CustomFileProvider : FWBaseProvider { Parse() }

About

Reading & Writing fixed-width data (files) with defined field position in line or file(relative height).

Resources

Code of conduct

Contributing

Stars

38 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

FixedWidthParserWriter

Library (C# .NET) for Parsing(Reading) & Writing fixed-width/flat data files (.txt, others).
Uses FastMember instead of slower Reflection.
It is targeting NetStandard 2.0 so it can be used on project targeting NetCore(2.0+) or NetFramework(4.6.1+).
Config base on POCO class/object model with Attributes.

There are 2 main types of usage that are explained in the following segments:
1. LineFields
2. FileFields and
3. CustomFileFields (additional third type or 2.b)
All are simple to use and easily configured with Attributes.

Logo:

NuGet

Installation

Available on NuGet latest version.
Package manager console command for installation: Install-Package FixedWidthParserWriter

Support

If you find this project useful you can mark it by leaving a Github Star
And even with community license, if you want help development, you can make a DONATION:
Buy Me A Coffee _ or _ Button
Want to Contact for Development & Consulting: www.codis.tech (Quality Assurance)

Contributing

Please read CONTRIBUTING for details on code of conduct, and the process for submitting pull requests.
When opening issues do write detailed explanation of the problem or feature with reproducible example.

**Also take a look into others packages:
Open source (MIT or cFOSS) authored .Net libraries (@Infopedia.io personal blog post)

.Net libraryDescription
1EFCore.BulkExtensionsEF Core Bulk CRUD Ops (Flagship Lib)
2EFCore.UtilExtensionsEF Core Custom Annotations and AuditInfo
3EFCore.FluentApiToAnnotationConverting FluentApi configuration to Annotations
4*FixedWidthParserWriterReading & Writing fixed-width/flat data files
5CsCodeGeneratorC# code generation based on Classes and elements
6CsCodeExampleExamples of C# code in form of a simple tutorial

CONFIG

FixedWidthConfig options (provides configuration for the Provider): defaultValue

intStructureTypeId:0	...............................// for multiple Attributes structureDictionary<string,FixedWidthAttribute> DynamicSettings	// Attributes defined at runtimebool LogAndSkipErrors:false .......................... // when True parsing exception skippedList<string> ErrorsLog :newList<string>() ...........// output field, logs ErrorMessagesList<string> WarningsLog :new List<string>() .........// output field, List for Warnings

*-Special feature is 'DYNAMIC Settings' with which Attributes values can be defined at runtime, for all usage types.
Data is forwarded using Dict with PropertyName and new independent Attribute with parameter values:
Dictionary<string, FixedWidthAttribute> dynamicSettings.
It can be set for all needed Properties when having no Attributes, or just add/override some specific.
And if need to exclude ones that has regular Atribute then set it with Null.
Sample in test LineParserTest.

WarningsLog filled when Writing string is cut to fit into defined position.

1. Data in LineFields

First is regular flat data file;
Record per Line (Fixed-Width), for example:

No | Description | Qty | Price | Amount |
1.Laptop Dell xps13 1 821.00 821.00
2.Monitor Asus 32'' 2 478.00 956.00

For parsing/writing we make a model which Properties have [FixedWidthLineField] Attribute:

publicclassInvoiceItem{[FixedWidthLineField(Start=1,Length=3)]publicintNumber{get;set;}[FixedWidthLineField(Start=4,Length=1)]publicstringNumberedBullet{get;set;}=".";[FixedWidthLineField(Start=5,Length=30)]publicstringDescription{get;set;}[FixedWidthLineField(Start=35,Length=6)]publicintQuantity{get;set;}[FixedWidthLineField(Start=41,Length=13)]publicdecimalPrice{get;set;}[FixedWidthLineField(Start=54,Length=13)]publicdecimalAmount=>Quantity*Price;}

Then we can call it like this:

// dataLines stripped of headerpublicList<Invoiceitem>ParseFieldsFromLines(newList<string> dataLines){
List<InvoiceItem> invoiceItems =newFixedWidthLinesProvider<InvoiceItem>().Parse(dataLines);returninvoiceItems;}publicList<string>WriteFieldsToLines(List<InvoiceItem>invoiceItems){List<string>dataLines=newFixedWidthLinesProvider<InvoiceItem>().Write(invoiceItems);returndataLines;}

[FixedWidthLineField] has following parameters that can be configured for each Property:

  • Start - required for LineType so that order of lineFields does not depends on order of modelPropertis
  • Length - when writing if Property value longer then defined in Length it is cut from the right to fit - valueTrim;
    zero 0 value means entire line and negative values means start from Right side
  • Format - Defaults per data type or group
  • Pad - Defaults per data category: { PadNumeric = ' ', PadNonNumeric = ' ' }
  • PadSide - Defaults per data category: { PadSideNumeric = PadSide.Left, PadSideNonNumeric = PadSide.Right }
  • DoTrim - Default is 'True' when text will be trimmed before casting
  • StructureTypeId - Default = 0, used when having multiple files with different structure or format for same data
  • NullPattern - Default = string.Empty, Pattern used to represent a null value

*_Format types:
-FormatNumberInteger Default = "0", *groupFormat:Int32,Int64
-FormatNumberDecimal Default = "0.00", *groupFormat:Decimal,Single,Double
("0;00" - Special custom Format that removes decimal separator: 123.45 -> 12345)
-FormatBoolean . . . . . . Default = "1; ;0" ("ValueForTrue; ValueForNull; ValueForFalse")
-FormatDateTime. . . . . .Default = "yyyyMMdd"
Custom format strings for Numeric and DateTime.

When need more then 1 file structure/format we can put multiple Attributes per Property with different StructureTypeId.
Next example shows 2 structures, second has one less Property and different PadNumeric: '0' instead of ' '(space).
To change DefaultConfig per StructureType, model should implement IFixedWidth interface with SetDefaultConfig() func.

publicenumConfigType{Alpha,Beta}publicclassInvoiceItem:IFixedWidth{publicDefaultConfigGetDefaultConfig(intStructureTypeId){vardefaultConfig=newDefaultConfig();switch((ConfigType)StructureTypeId){caseConfigType.Alpha:// config remains initial defaultbreak;caseConfigType.Beta:defaultConfig.PadNumeric='0';break;}returndefaultConfig;}[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=1,Length=3)][FixedWidthLineField(StructureTypeId=(int)ConfigType.Beta,Start=1,Length=4)]publicintNumber{get;set;}[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=4,Length=1)]publicstringNumberedBullet{get;set;}=".";[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=5,Length=30)][FixedWidthLineField(StructureTypeId=(int)ConfigType.Beta,Start=5,Length=30)]publicstringDescription{get;set;}//... Others Properties}

Beta Structure:

No | Description | Qty | Price | Amount |
0001Laptop Dell xps13 0000010000000821.000000000821.00
0002Monitor Asus 32'' 0000020000000478.000000000956.00

Calling the methods:

var linesProvider = new FixedWidthLinesProvider<InvoiceItem>();
List<InvoiceItem> itemsA = linesProvider.Parse(dataLinesA, (int)ConfigType.Alpha);
List<InvoiceItem> itemsB = linesProvider.Parse(dataLinesB, (int)ConfigType.Alpha);

PARSE method for all use cases can also optionally have third parameter List<string> errorLog which when sent as Empty list(not null) will be loaded with list of Exceptions if any were to happen during parsing and casting operations. When method is called without this param, which remains null, in that case first error with throw Exception and procedure will be stopped.

Full Examples are in Tests of the project.

2. Data in FileFields

Second usage is when one data record is in different rows at defined positions;
Record per File (Fixed/Relative-Height), E.g.:

SoftysTech LCC
__________________________________________________________________
Invoice Date: 2018-10-30 Buyer: SysCompanik
INVOICE no. 0169/18
No | Description | Qty | Price | Amount |
...
...
------------------------------------------------------------------
1,299.00 Date: 2018-10-31 Financial Manager
John Doe

For parsing/writing [FixedWidthFileField] attributes are used, that have additional parameter:

  • Line - in which we define line number where the value is (Negative values represent row number from bottom)

For type FileField param. Length not required, if not set means value goes till end of row(trimmed), and Start has default = 1.

publicclassInvoice{[FixedWidthFileField(Line=1)]publicstringCompanyName{get;set;}[FixedWidthFileField(Line=4,Start=15,Length=19,Format="yyyy-MM-dd")]publicDateTimeDate{get;set;}[FixedWidthFileField(Line=4,Start=43)]publicstringBuyerName{get;set;}[FixedWidthFileField(Line=6,Start=37)]publicstringInvoiceNumber{get;set;}[FixedWidthFileField(Line=-4,Length=66,Format="0,000.00")]publicdecimalAmountTotal{get;set;}[FixedWidthFileField(Line=-2,Start=7,Length=10,Format="yyyy-MM-dd")]publicDateTimeDateCreated{get;set;}[FixedWidthFileField(Line=-2,Start=17,Length=50,PadSide=PadSide.Left)]publicstringSignatoryTitle{get;set;}// Line Negative - counted from bottom [FixedWidthFileField(Line=-1,Length=66,PadSide=PadSide.Left)]publicstringSignatureName{get;set;}}

Usage:

publicInvoiceParseFieldsFromFile(newList<string> fileLines){
invoice invoice =newFixedWidthFileProvider<Invoice>().Parse(fileLines);returninvoice;}publicList<string>WriteFieldsToFile(Invoice){List<string>templateLines=GetDataFormTemplate();varfileProvider=newFixedWidthFileProvider<Invoice>(){Content=templateLines};fileProvider.UpdateContent(invoice);invoice.UpdateContent();returninvoice.Content;}

DataFormTemplate looks like this:

{CompanyName}
__________________________________________________________________
Invoice Date: {InvoiceDate} Buyer: {BuyerName}
INVOICE no. NNNN/YY
No | Description | Qty | Price | Amount |
...
...
------------------------------------------------------------------
0.00 Date: {DateCreated} {SignatoryTitle}
{SignatureName}

In situation where many same type properties have Format different from default one, instead of setting custom format individually for each Property, it is possible to override DefaultConfig for certain data types/groups in that class:

publicclassInvoice:IFixedWidth{publicDefaultConfigGetDefaultConfig(intStructureTypeId){returnnewDefaultConfig{FormatDateTime="yyyy-MM-dd"};}[FixedWidthFileField(Line=1)]publicstringCompanyName{get;set;}// Format set on class with FormatDateTime - not required on each Attribute of DateTime Property[FixedWidthFileField(Line=4,Start=15,Length=19/*, Format = "yyyy-MM-dd"*/)]publicDateTimeDate{get;set;}/* ... Other Properties */}

If we need to changed DefaultConfig(Format) for multiple models then we could override entire Provider to keep it DRY.

-Combining both previous usages we can make complex file structures like invoiceFull.
-In adddition we could also have grouped line with say header per segment in which case we can use multiple Line Models and then perfilter lines based on category, example invoiceGrouped.

When there is some special situation where some Fields can not be configured with existing options, then we can make additional custom parsing prior or/and after calling the method - PRE & POST Processing.

3. Data in CustomFileFields

Third use case (Parsing only) is when one data field is relative to some text position.

SoftysTech LCC company
__________________________________________________________________
Date generated: 30.06.2020.
Q REPORT no. 11/20** 1569ccACTN.162-677-169-796
...
Revenue:
1,234.55
...
xx
publicclassReport{[CustomFileField(EndsWith=" company")]publicstringCompanyName{get;set;}[CustomFileField(StartsWith="Date generated: ",Format="d.M.yyyy.")]publicDateTimeDate{get;set;}[CustomFileField(Contains="Q REPORT no. ",RemoveText="**")]publicstringNumber{get;set;}[CustomFileField(Contains="ACTN.",Length=-15)]publicstringAccount{get;set;}[CustomFileField(StartsWith="Revenue:",Offset=1)]publicdecimalRevenue{get;set;}}

For parsing [CustomFileField] attributes are used, with additional params:

  • StartsWith, EndsWith, Contains - finds lines with first occurance of search criteria
  • Offset - moves found line up(is positive) or down(negative value) for defined number of rows
  • RemoveText - to clear custom substring from text value before additional casting
  • RemoveStartsWith, RemoveEndsWith, RemoveContains - defaults are 'True' meaning that search string is also cleared

Code Structure:

FixedWidthBaseProvider { ParseData(); WriteData() }
|
|---FixedWidthLinesProvider : FWBaseProvider { Parse(); Write() }
|---FixedWidthFileProvider : FWBaseProvider { Parse(); UpdateContent() }
|---CustomFileProvider : FWBaseProvider { Parse() }

About

Reading & Writing fixed-width data (files) with defined field position in line or file(relative height).

Resources

Code of conduct

Contributing

Stars

38 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

FixedWidthParserWriter

Library (C# .NET) for Parsing(Reading) & Writing fixed-width/flat data files (.txt, others).
Uses FastMember instead of slower Reflection.
It is targeting NetStandard 2.0 so it can be used on project targeting NetCore(2.0+) or NetFramework(4.6.1+).
Config base on POCO class/object model with Attributes.

There are 2 main types of usage that are explained in the following segments:
1. LineFields
2. FileFields and
3. CustomFileFields (additional third type or 2.b)
All are simple to use and easily configured with Attributes.

Logo:

NuGet

Installation

Available on NuGet latest version.
Package manager console command for installation: Install-Package FixedWidthParserWriter

Support

If you find this project useful you can mark it by leaving a Github Star
And even with community license, if you want help development, you can make a DONATION:
Buy Me A Coffee _ or _ Button
Want to Contact for Development & Consulting: www.codis.tech (Quality Assurance)

Contributing

Please read CONTRIBUTING for details on code of conduct, and the process for submitting pull requests.
When opening issues do write detailed explanation of the problem or feature with reproducible example.

**Also take a look into others packages:
Open source (MIT or cFOSS) authored .Net libraries (@Infopedia.io personal blog post)

.Net libraryDescription
1EFCore.BulkExtensionsEF Core Bulk CRUD Ops (Flagship Lib)
2EFCore.UtilExtensionsEF Core Custom Annotations and AuditInfo
3EFCore.FluentApiToAnnotationConverting FluentApi configuration to Annotations
4*FixedWidthParserWriterReading & Writing fixed-width/flat data files
5CsCodeGeneratorC# code generation based on Classes and elements
6CsCodeExampleExamples of C# code in form of a simple tutorial

CONFIG

FixedWidthConfig options (provides configuration for the Provider): defaultValue

intStructureTypeId:0	...............................// for multiple Attributes structureDictionary<string,FixedWidthAttribute> DynamicSettings	// Attributes defined at runtimebool LogAndSkipErrors:false .......................... // when True parsing exception skippedList<string> ErrorsLog :newList<string>() ...........// output field, logs ErrorMessagesList<string> WarningsLog :new List<string>() .........// output field, List for Warnings

*-Special feature is 'DYNAMIC Settings' with which Attributes values can be defined at runtime, for all usage types.
Data is forwarded using Dict with PropertyName and new independent Attribute with parameter values:
Dictionary<string, FixedWidthAttribute> dynamicSettings.
It can be set for all needed Properties when having no Attributes, or just add/override some specific.
And if need to exclude ones that has regular Atribute then set it with Null.
Sample in test LineParserTest.

WarningsLog filled when Writing string is cut to fit into defined position.

1. Data in LineFields

First is regular flat data file;
Record per Line (Fixed-Width), for example:

No | Description | Qty | Price | Amount |
1.Laptop Dell xps13 1 821.00 821.00
2.Monitor Asus 32'' 2 478.00 956.00

For parsing/writing we make a model which Properties have [FixedWidthLineField] Attribute:

publicclassInvoiceItem{[FixedWidthLineField(Start=1,Length=3)]publicintNumber{get;set;}[FixedWidthLineField(Start=4,Length=1)]publicstringNumberedBullet{get;set;}=".";[FixedWidthLineField(Start=5,Length=30)]publicstringDescription{get;set;}[FixedWidthLineField(Start=35,Length=6)]publicintQuantity{get;set;}[FixedWidthLineField(Start=41,Length=13)]publicdecimalPrice{get;set;}[FixedWidthLineField(Start=54,Length=13)]publicdecimalAmount=>Quantity*Price;}

Then we can call it like this:

// dataLines stripped of headerpublicList<Invoiceitem>ParseFieldsFromLines(newList<string> dataLines){
List<InvoiceItem> invoiceItems =newFixedWidthLinesProvider<InvoiceItem>().Parse(dataLines);returninvoiceItems;}publicList<string>WriteFieldsToLines(List<InvoiceItem>invoiceItems){List<string>dataLines=newFixedWidthLinesProvider<InvoiceItem>().Write(invoiceItems);returndataLines;}

[FixedWidthLineField] has following parameters that can be configured for each Property:

  • Start - required for LineType so that order of lineFields does not depends on order of modelPropertis
  • Length - when writing if Property value longer then defined in Length it is cut from the right to fit - valueTrim;
    zero 0 value means entire line and negative values means start from Right side
  • Format - Defaults per data type or group
  • Pad - Defaults per data category: { PadNumeric = ' ', PadNonNumeric = ' ' }
  • PadSide - Defaults per data category: { PadSideNumeric = PadSide.Left, PadSideNonNumeric = PadSide.Right }
  • DoTrim - Default is 'True' when text will be trimmed before casting
  • StructureTypeId - Default = 0, used when having multiple files with different structure or format for same data
  • NullPattern - Default = string.Empty, Pattern used to represent a null value

*_Format types:
-FormatNumberInteger Default = "0", *groupFormat:Int32,Int64
-FormatNumberDecimal Default = "0.00", *groupFormat:Decimal,Single,Double
("0;00" - Special custom Format that removes decimal separator: 123.45 -> 12345)
-FormatBoolean . . . . . . Default = "1; ;0" ("ValueForTrue; ValueForNull; ValueForFalse")
-FormatDateTime. . . . . .Default = "yyyyMMdd"
Custom format strings for Numeric and DateTime.

When need more then 1 file structure/format we can put multiple Attributes per Property with different StructureTypeId.
Next example shows 2 structures, second has one less Property and different PadNumeric: '0' instead of ' '(space).
To change DefaultConfig per StructureType, model should implement IFixedWidth interface with SetDefaultConfig() func.

publicenumConfigType{Alpha,Beta}publicclassInvoiceItem:IFixedWidth{publicDefaultConfigGetDefaultConfig(intStructureTypeId){vardefaultConfig=newDefaultConfig();switch((ConfigType)StructureTypeId){caseConfigType.Alpha:// config remains initial defaultbreak;caseConfigType.Beta:defaultConfig.PadNumeric='0';break;}returndefaultConfig;}[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=1,Length=3)][FixedWidthLineField(StructureTypeId=(int)ConfigType.Beta,Start=1,Length=4)]publicintNumber{get;set;}[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=4,Length=1)]publicstringNumberedBullet{get;set;}=".";[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=5,Length=30)][FixedWidthLineField(StructureTypeId=(int)ConfigType.Beta,Start=5,Length=30)]publicstringDescription{get;set;}//... Others Properties}

Beta Structure:

No | Description | Qty | Price | Amount |
0001Laptop Dell xps13 0000010000000821.000000000821.00
0002Monitor Asus 32'' 0000020000000478.000000000956.00

Calling the methods:

var linesProvider = new FixedWidthLinesProvider<InvoiceItem>();
List<InvoiceItem> itemsA = linesProvider.Parse(dataLinesA, (int)ConfigType.Alpha);
List<InvoiceItem> itemsB = linesProvider.Parse(dataLinesB, (int)ConfigType.Alpha);

PARSE method for all use cases can also optionally have third parameter List<string> errorLog which when sent as Empty list(not null) will be loaded with list of Exceptions if any were to happen during parsing and casting operations. When method is called without this param, which remains null, in that case first error with throw Exception and procedure will be stopped.

Full Examples are in Tests of the project.

2. Data in FileFields

Second usage is when one data record is in different rows at defined positions;
Record per File (Fixed/Relative-Height), E.g.:

SoftysTech LCC
__________________________________________________________________
Invoice Date: 2018-10-30 Buyer: SysCompanik
INVOICE no. 0169/18
No | Description | Qty | Price | Amount |
...
...
------------------------------------------------------------------
1,299.00 Date: 2018-10-31 Financial Manager
John Doe

For parsing/writing [FixedWidthFileField] attributes are used, that have additional parameter:

  • Line - in which we define line number where the value is (Negative values represent row number from bottom)

For type FileField param. Length not required, if not set means value goes till end of row(trimmed), and Start has default = 1.

publicclassInvoice{[FixedWidthFileField(Line=1)]publicstringCompanyName{get;set;}[FixedWidthFileField(Line=4,Start=15,Length=19,Format="yyyy-MM-dd")]publicDateTimeDate{get;set;}[FixedWidthFileField(Line=4,Start=43)]publicstringBuyerName{get;set;}[FixedWidthFileField(Line=6,Start=37)]publicstringInvoiceNumber{get;set;}[FixedWidthFileField(Line=-4,Length=66,Format="0,000.00")]publicdecimalAmountTotal{get;set;}[FixedWidthFileField(Line=-2,Start=7,Length=10,Format="yyyy-MM-dd")]publicDateTimeDateCreated{get;set;}[FixedWidthFileField(Line=-2,Start=17,Length=50,PadSide=PadSide.Left)]publicstringSignatoryTitle{get;set;}// Line Negative - counted from bottom [FixedWidthFileField(Line=-1,Length=66,PadSide=PadSide.Left)]publicstringSignatureName{get;set;}}

Usage:

publicInvoiceParseFieldsFromFile(newList<string> fileLines){
invoice invoice =newFixedWidthFileProvider<Invoice>().Parse(fileLines);returninvoice;}publicList<string>WriteFieldsToFile(Invoice){List<string>templateLines=GetDataFormTemplate();varfileProvider=newFixedWidthFileProvider<Invoice>(){Content=templateLines};fileProvider.UpdateContent(invoice);invoice.UpdateContent();returninvoice.Content;}

DataFormTemplate looks like this:

{CompanyName}
__________________________________________________________________
Invoice Date: {InvoiceDate} Buyer: {BuyerName}
INVOICE no. NNNN/YY
No | Description | Qty | Price | Amount |
...
...
------------------------------------------------------------------
0.00 Date: {DateCreated} {SignatoryTitle}
{SignatureName}

In situation where many same type properties have Format different from default one, instead of setting custom format individually for each Property, it is possible to override DefaultConfig for certain data types/groups in that class:

publicclassInvoice:IFixedWidth{publicDefaultConfigGetDefaultConfig(intStructureTypeId){returnnewDefaultConfig{FormatDateTime="yyyy-MM-dd"};}[FixedWidthFileField(Line=1)]publicstringCompanyName{get;set;}// Format set on class with FormatDateTime - not required on each Attribute of DateTime Property[FixedWidthFileField(Line=4,Start=15,Length=19/*, Format = "yyyy-MM-dd"*/)]publicDateTimeDate{get;set;}/* ... Other Properties */}

If we need to changed DefaultConfig(Format) for multiple models then we could override entire Provider to keep it DRY.

-Combining both previous usages we can make complex file structures like invoiceFull.
-In adddition we could also have grouped line with say header per segment in which case we can use multiple Line Models and then perfilter lines based on category, example invoiceGrouped.

When there is some special situation where some Fields can not be configured with existing options, then we can make additional custom parsing prior or/and after calling the method - PRE & POST Processing.

3. Data in CustomFileFields

Third use case (Parsing only) is when one data field is relative to some text position.

SoftysTech LCC company
__________________________________________________________________
Date generated: 30.06.2020.
Q REPORT no. 11/20** 1569ccACTN.162-677-169-796
...
Revenue:
1,234.55
...
xx
publicclassReport{[CustomFileField(EndsWith=" company")]publicstringCompanyName{get;set;}[CustomFileField(StartsWith="Date generated: ",Format="d.M.yyyy.")]publicDateTimeDate{get;set;}[CustomFileField(Contains="Q REPORT no. ",RemoveText="**")]publicstringNumber{get;set;}[CustomFileField(Contains="ACTN.",Length=-15)]publicstringAccount{get;set;}[CustomFileField(StartsWith="Revenue:",Offset=1)]publicdecimalRevenue{get;set;}}

For parsing [CustomFileField] attributes are used, with additional params:

  • StartsWith, EndsWith, Contains - finds lines with first occurance of search criteria
  • Offset - moves found line up(is positive) or down(negative value) for defined number of rows
  • RemoveText - to clear custom substring from text value before additional casting
  • RemoveStartsWith, RemoveEndsWith, RemoveContains - defaults are 'True' meaning that search string is also cleared

Code Structure:

FixedWidthBaseProvider { ParseData(); WriteData() }
|
|---FixedWidthLinesProvider : FWBaseProvider { Parse(); Write() }
|---FixedWidthFileProvider : FWBaseProvider { Parse(); UpdateContent() }
|---CustomFileProvider : FWBaseProvider { Parse() }

About

Reading & Writing fixed-width data (files) with defined field position in line or file(relative height).

Resources

Code of conduct

Contributing

Stars

38 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

FixedWidthParserWriter

Library (C# .NET) for Parsing(Reading) & Writing fixed-width/flat data files (.txt, others).
Uses FastMember instead of slower Reflection.
It is targeting NetStandard 2.0 so it can be used on project targeting NetCore(2.0+) or NetFramework(4.6.1+).
Config base on POCO class/object model with Attributes.

There are 2 main types of usage that are explained in the following segments:
1. LineFields
2. FileFields and
3. CustomFileFields (additional third type or 2.b)
All are simple to use and easily configured with Attributes.

Logo:

NuGet

Installation

Available on NuGet latest version.
Package manager console command for installation: Install-Package FixedWidthParserWriter

Support

If you find this project useful you can mark it by leaving a Github Star
And even with community license, if you want help development, you can make a DONATION:
Buy Me A Coffee _ or _ Button
Want to Contact for Development & Consulting: www.codis.tech (Quality Assurance)

Contributing

Please read CONTRIBUTING for details on code of conduct, and the process for submitting pull requests.
When opening issues do write detailed explanation of the problem or feature with reproducible example.

**Also take a look into others packages:
Open source (MIT or cFOSS) authored .Net libraries (@Infopedia.io personal blog post)

.Net libraryDescription
1EFCore.BulkExtensionsEF Core Bulk CRUD Ops (Flagship Lib)
2EFCore.UtilExtensionsEF Core Custom Annotations and AuditInfo
3EFCore.FluentApiToAnnotationConverting FluentApi configuration to Annotations
4*FixedWidthParserWriterReading & Writing fixed-width/flat data files
5CsCodeGeneratorC# code generation based on Classes and elements
6CsCodeExampleExamples of C# code in form of a simple tutorial

CONFIG

FixedWidthConfig options (provides configuration for the Provider): defaultValue

intStructureTypeId:0	...............................// for multiple Attributes structureDictionary<string,FixedWidthAttribute> DynamicSettings	// Attributes defined at runtimebool LogAndSkipErrors:false .......................... // when True parsing exception skippedList<string> ErrorsLog :newList<string>() ...........// output field, logs ErrorMessagesList<string> WarningsLog :new List<string>() .........// output field, List for Warnings

*-Special feature is 'DYNAMIC Settings' with which Attributes values can be defined at runtime, for all usage types.
Data is forwarded using Dict with PropertyName and new independent Attribute with parameter values:
Dictionary<string, FixedWidthAttribute> dynamicSettings.
It can be set for all needed Properties when having no Attributes, or just add/override some specific.
And if need to exclude ones that has regular Atribute then set it with Null.
Sample in test LineParserTest.

WarningsLog filled when Writing string is cut to fit into defined position.

1. Data in LineFields

First is regular flat data file;
Record per Line (Fixed-Width), for example:

No | Description | Qty | Price | Amount |
1.Laptop Dell xps13 1 821.00 821.00
2.Monitor Asus 32'' 2 478.00 956.00

For parsing/writing we make a model which Properties have [FixedWidthLineField] Attribute:

publicclassInvoiceItem{[FixedWidthLineField(Start=1,Length=3)]publicintNumber{get;set;}[FixedWidthLineField(Start=4,Length=1)]publicstringNumberedBullet{get;set;}=".";[FixedWidthLineField(Start=5,Length=30)]publicstringDescription{get;set;}[FixedWidthLineField(Start=35,Length=6)]publicintQuantity{get;set;}[FixedWidthLineField(Start=41,Length=13)]publicdecimalPrice{get;set;}[FixedWidthLineField(Start=54,Length=13)]publicdecimalAmount=>Quantity*Price;}

Then we can call it like this:

// dataLines stripped of headerpublicList<Invoiceitem>ParseFieldsFromLines(newList<string> dataLines){
List<InvoiceItem> invoiceItems =newFixedWidthLinesProvider<InvoiceItem>().Parse(dataLines);returninvoiceItems;}publicList<string>WriteFieldsToLines(List<InvoiceItem>invoiceItems){List<string>dataLines=newFixedWidthLinesProvider<InvoiceItem>().Write(invoiceItems);returndataLines;}

[FixedWidthLineField] has following parameters that can be configured for each Property:

  • Start - required for LineType so that order of lineFields does not depends on order of modelPropertis
  • Length - when writing if Property value longer then defined in Length it is cut from the right to fit - valueTrim;
    zero 0 value means entire line and negative values means start from Right side
  • Format - Defaults per data type or group
  • Pad - Defaults per data category: { PadNumeric = ' ', PadNonNumeric = ' ' }
  • PadSide - Defaults per data category: { PadSideNumeric = PadSide.Left, PadSideNonNumeric = PadSide.Right }
  • DoTrim - Default is 'True' when text will be trimmed before casting
  • StructureTypeId - Default = 0, used when having multiple files with different structure or format for same data
  • NullPattern - Default = string.Empty, Pattern used to represent a null value

*_Format types:
-FormatNumberInteger Default = "0", *groupFormat:Int32,Int64
-FormatNumberDecimal Default = "0.00", *groupFormat:Decimal,Single,Double
("0;00" - Special custom Format that removes decimal separator: 123.45 -> 12345)
-FormatBoolean . . . . . . Default = "1; ;0" ("ValueForTrue; ValueForNull; ValueForFalse")
-FormatDateTime. . . . . .Default = "yyyyMMdd"
Custom format strings for Numeric and DateTime.

When need more then 1 file structure/format we can put multiple Attributes per Property with different StructureTypeId.
Next example shows 2 structures, second has one less Property and different PadNumeric: '0' instead of ' '(space).
To change DefaultConfig per StructureType, model should implement IFixedWidth interface with SetDefaultConfig() func.

publicenumConfigType{Alpha,Beta}publicclassInvoiceItem:IFixedWidth{publicDefaultConfigGetDefaultConfig(intStructureTypeId){vardefaultConfig=newDefaultConfig();switch((ConfigType)StructureTypeId){caseConfigType.Alpha:// config remains initial defaultbreak;caseConfigType.Beta:defaultConfig.PadNumeric='0';break;}returndefaultConfig;}[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=1,Length=3)][FixedWidthLineField(StructureTypeId=(int)ConfigType.Beta,Start=1,Length=4)]publicintNumber{get;set;}[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=4,Length=1)]publicstringNumberedBullet{get;set;}=".";[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=5,Length=30)][FixedWidthLineField(StructureTypeId=(int)ConfigType.Beta,Start=5,Length=30)]publicstringDescription{get;set;}//... Others Properties}

Beta Structure:

No | Description | Qty | Price | Amount |
0001Laptop Dell xps13 0000010000000821.000000000821.00
0002Monitor Asus 32'' 0000020000000478.000000000956.00

Calling the methods:

var linesProvider = new FixedWidthLinesProvider<InvoiceItem>();
List<InvoiceItem> itemsA = linesProvider.Parse(dataLinesA, (int)ConfigType.Alpha);
List<InvoiceItem> itemsB = linesProvider.Parse(dataLinesB, (int)ConfigType.Alpha);

PARSE method for all use cases can also optionally have third parameter List<string> errorLog which when sent as Empty list(not null) will be loaded with list of Exceptions if any were to happen during parsing and casting operations. When method is called without this param, which remains null, in that case first error with throw Exception and procedure will be stopped.

Full Examples are in Tests of the project.

2. Data in FileFields

Second usage is when one data record is in different rows at defined positions;
Record per File (Fixed/Relative-Height), E.g.:

SoftysTech LCC
__________________________________________________________________
Invoice Date: 2018-10-30 Buyer: SysCompanik
INVOICE no. 0169/18
No | Description | Qty | Price | Amount |
...
...
------------------------------------------------------------------
1,299.00 Date: 2018-10-31 Financial Manager
John Doe

For parsing/writing [FixedWidthFileField] attributes are used, that have additional parameter:

  • Line - in which we define line number where the value is (Negative values represent row number from bottom)

For type FileField param. Length not required, if not set means value goes till end of row(trimmed), and Start has default = 1.

publicclassInvoice{[FixedWidthFileField(Line=1)]publicstringCompanyName{get;set;}[FixedWidthFileField(Line=4,Start=15,Length=19,Format="yyyy-MM-dd")]publicDateTimeDate{get;set;}[FixedWidthFileField(Line=4,Start=43)]publicstringBuyerName{get;set;}[FixedWidthFileField(Line=6,Start=37)]publicstringInvoiceNumber{get;set;}[FixedWidthFileField(Line=-4,Length=66,Format="0,000.00")]publicdecimalAmountTotal{get;set;}[FixedWidthFileField(Line=-2,Start=7,Length=10,Format="yyyy-MM-dd")]publicDateTimeDateCreated{get;set;}[FixedWidthFileField(Line=-2,Start=17,Length=50,PadSide=PadSide.Left)]publicstringSignatoryTitle{get;set;}// Line Negative - counted from bottom [FixedWidthFileField(Line=-1,Length=66,PadSide=PadSide.Left)]publicstringSignatureName{get;set;}}

Usage:

publicInvoiceParseFieldsFromFile(newList<string> fileLines){
invoice invoice =newFixedWidthFileProvider<Invoice>().Parse(fileLines);returninvoice;}publicList<string>WriteFieldsToFile(Invoice){List<string>templateLines=GetDataFormTemplate();varfileProvider=newFixedWidthFileProvider<Invoice>(){Content=templateLines};fileProvider.UpdateContent(invoice);invoice.UpdateContent();returninvoice.Content;}

DataFormTemplate looks like this:

{CompanyName}
__________________________________________________________________
Invoice Date: {InvoiceDate} Buyer: {BuyerName}
INVOICE no. NNNN/YY
No | Description | Qty | Price | Amount |
...
...
------------------------------------------------------------------
0.00 Date: {DateCreated} {SignatoryTitle}
{SignatureName}

In situation where many same type properties have Format different from default one, instead of setting custom format individually for each Property, it is possible to override DefaultConfig for certain data types/groups in that class:

publicclassInvoice:IFixedWidth{publicDefaultConfigGetDefaultConfig(intStructureTypeId){returnnewDefaultConfig{FormatDateTime="yyyy-MM-dd"};}[FixedWidthFileField(Line=1)]publicstringCompanyName{get;set;}// Format set on class with FormatDateTime - not required on each Attribute of DateTime Property[FixedWidthFileField(Line=4,Start=15,Length=19/*, Format = "yyyy-MM-dd"*/)]publicDateTimeDate{get;set;}/* ... Other Properties */}

If we need to changed DefaultConfig(Format) for multiple models then we could override entire Provider to keep it DRY.

-Combining both previous usages we can make complex file structures like invoiceFull.
-In adddition we could also have grouped line with say header per segment in which case we can use multiple Line Models and then perfilter lines based on category, example invoiceGrouped.

When there is some special situation where some Fields can not be configured with existing options, then we can make additional custom parsing prior or/and after calling the method - PRE & POST Processing.

3. Data in CustomFileFields

Third use case (Parsing only) is when one data field is relative to some text position.

SoftysTech LCC company
__________________________________________________________________
Date generated: 30.06.2020.
Q REPORT no. 11/20** 1569ccACTN.162-677-169-796
...
Revenue:
1,234.55
...
xx
publicclassReport{[CustomFileField(EndsWith=" company")]publicstringCompanyName{get;set;}[CustomFileField(StartsWith="Date generated: ",Format="d.M.yyyy.")]publicDateTimeDate{get;set;}[CustomFileField(Contains="Q REPORT no. ",RemoveText="**")]publicstringNumber{get;set;}[CustomFileField(Contains="ACTN.",Length=-15)]publicstringAccount{get;set;}[CustomFileField(StartsWith="Revenue:",Offset=1)]publicdecimalRevenue{get;set;}}

For parsing [CustomFileField] attributes are used, with additional params:

  • StartsWith, EndsWith, Contains - finds lines with first occurance of search criteria
  • Offset - moves found line up(is positive) or down(negative value) for defined number of rows
  • RemoveText - to clear custom substring from text value before additional casting
  • RemoveStartsWith, RemoveEndsWith, RemoveContains - defaults are 'True' meaning that search string is also cleared

Code Structure:

FixedWidthBaseProvider { ParseData(); WriteData() }
|
|---FixedWidthLinesProvider : FWBaseProvider { Parse(); Write() }
|---FixedWidthFileProvider : FWBaseProvider { Parse(); UpdateContent() }
|---CustomFileProvider : FWBaseProvider { Parse() }

About

Reading & Writing fixed-width data (files) with defined field position in line or file(relative height).

Resources

Code of conduct

Contributing

Stars

38 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

FixedWidthParserWriter

Library (C# .NET) for Parsing(Reading) & Writing fixed-width/flat data files (.txt, others).
Uses FastMember instead of slower Reflection.
It is targeting NetStandard 2.0 so it can be used on project targeting NetCore(2.0+) or NetFramework(4.6.1+).
Config base on POCO class/object model with Attributes.

There are 2 main types of usage that are explained in the following segments:
1. LineFields
2. FileFields and
3. CustomFileFields (additional third type or 2.b)
All are simple to use and easily configured with Attributes.

Logo:

NuGet

Installation

Available on NuGet latest version.
Package manager console command for installation: Install-Package FixedWidthParserWriter

Support

If you find this project useful you can mark it by leaving a Github Star
And even with community license, if you want help development, you can make a DONATION:
Buy Me A Coffee _ or _ Button
Want to Contact for Development & Consulting: www.codis.tech (Quality Assurance)

Contributing

Please read CONTRIBUTING for details on code of conduct, and the process for submitting pull requests.
When opening issues do write detailed explanation of the problem or feature with reproducible example.

**Also take a look into others packages:
Open source (MIT or cFOSS) authored .Net libraries (@Infopedia.io personal blog post)

.Net libraryDescription
1EFCore.BulkExtensionsEF Core Bulk CRUD Ops (Flagship Lib)
2EFCore.UtilExtensionsEF Core Custom Annotations and AuditInfo
3EFCore.FluentApiToAnnotationConverting FluentApi configuration to Annotations
4*FixedWidthParserWriterReading & Writing fixed-width/flat data files
5CsCodeGeneratorC# code generation based on Classes and elements
6CsCodeExampleExamples of C# code in form of a simple tutorial

CONFIG

FixedWidthConfig options (provides configuration for the Provider): defaultValue

intStructureTypeId:0	...............................// for multiple Attributes structureDictionary<string,FixedWidthAttribute> DynamicSettings	// Attributes defined at runtimebool LogAndSkipErrors:false .......................... // when True parsing exception skippedList<string> ErrorsLog :newList<string>() ...........// output field, logs ErrorMessagesList<string> WarningsLog :new List<string>() .........// output field, List for Warnings

*-Special feature is 'DYNAMIC Settings' with which Attributes values can be defined at runtime, for all usage types.
Data is forwarded using Dict with PropertyName and new independent Attribute with parameter values:
Dictionary<string, FixedWidthAttribute> dynamicSettings.
It can be set for all needed Properties when having no Attributes, or just add/override some specific.
And if need to exclude ones that has regular Atribute then set it with Null.
Sample in test LineParserTest.

WarningsLog filled when Writing string is cut to fit into defined position.

1. Data in LineFields

First is regular flat data file;
Record per Line (Fixed-Width), for example:

No | Description | Qty | Price | Amount |
1.Laptop Dell xps13 1 821.00 821.00
2.Monitor Asus 32'' 2 478.00 956.00

For parsing/writing we make a model which Properties have [FixedWidthLineField] Attribute:

publicclassInvoiceItem{[FixedWidthLineField(Start=1,Length=3)]publicintNumber{get;set;}[FixedWidthLineField(Start=4,Length=1)]publicstringNumberedBullet{get;set;}=".";[FixedWidthLineField(Start=5,Length=30)]publicstringDescription{get;set;}[FixedWidthLineField(Start=35,Length=6)]publicintQuantity{get;set;}[FixedWidthLineField(Start=41,Length=13)]publicdecimalPrice{get;set;}[FixedWidthLineField(Start=54,Length=13)]publicdecimalAmount=>Quantity*Price;}

Then we can call it like this:

// dataLines stripped of headerpublicList<Invoiceitem>ParseFieldsFromLines(newList<string> dataLines){
List<InvoiceItem> invoiceItems =newFixedWidthLinesProvider<InvoiceItem>().Parse(dataLines);returninvoiceItems;}publicList<string>WriteFieldsToLines(List<InvoiceItem>invoiceItems){List<string>dataLines=newFixedWidthLinesProvider<InvoiceItem>().Write(invoiceItems);returndataLines;}

[FixedWidthLineField] has following parameters that can be configured for each Property:

  • Start - required for LineType so that order of lineFields does not depends on order of modelPropertis
  • Length - when writing if Property value longer then defined in Length it is cut from the right to fit - valueTrim;
    zero 0 value means entire line and negative values means start from Right side
  • Format - Defaults per data type or group
  • Pad - Defaults per data category: { PadNumeric = ' ', PadNonNumeric = ' ' }
  • PadSide - Defaults per data category: { PadSideNumeric = PadSide.Left, PadSideNonNumeric = PadSide.Right }
  • DoTrim - Default is 'True' when text will be trimmed before casting
  • StructureTypeId - Default = 0, used when having multiple files with different structure or format for same data
  • NullPattern - Default = string.Empty, Pattern used to represent a null value

*_Format types:
-FormatNumberInteger Default = "0", *groupFormat:Int32,Int64
-FormatNumberDecimal Default = "0.00", *groupFormat:Decimal,Single,Double
("0;00" - Special custom Format that removes decimal separator: 123.45 -> 12345)
-FormatBoolean . . . . . . Default = "1; ;0" ("ValueForTrue; ValueForNull; ValueForFalse")
-FormatDateTime. . . . . .Default = "yyyyMMdd"
Custom format strings for Numeric and DateTime.

When need more then 1 file structure/format we can put multiple Attributes per Property with different StructureTypeId.
Next example shows 2 structures, second has one less Property and different PadNumeric: '0' instead of ' '(space).
To change DefaultConfig per StructureType, model should implement IFixedWidth interface with SetDefaultConfig() func.

publicenumConfigType{Alpha,Beta}publicclassInvoiceItem:IFixedWidth{publicDefaultConfigGetDefaultConfig(intStructureTypeId){vardefaultConfig=newDefaultConfig();switch((ConfigType)StructureTypeId){caseConfigType.Alpha:// config remains initial defaultbreak;caseConfigType.Beta:defaultConfig.PadNumeric='0';break;}returndefaultConfig;}[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=1,Length=3)][FixedWidthLineField(StructureTypeId=(int)ConfigType.Beta,Start=1,Length=4)]publicintNumber{get;set;}[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=4,Length=1)]publicstringNumberedBullet{get;set;}=".";[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=5,Length=30)][FixedWidthLineField(StructureTypeId=(int)ConfigType.Beta,Start=5,Length=30)]publicstringDescription{get;set;}//... Others Properties}

Beta Structure:

No | Description | Qty | Price | Amount |
0001Laptop Dell xps13 0000010000000821.000000000821.00
0002Monitor Asus 32'' 0000020000000478.000000000956.00

Calling the methods:

var linesProvider = new FixedWidthLinesProvider<InvoiceItem>();
List<InvoiceItem> itemsA = linesProvider.Parse(dataLinesA, (int)ConfigType.Alpha);
List<InvoiceItem> itemsB = linesProvider.Parse(dataLinesB, (int)ConfigType.Alpha);

PARSE method for all use cases can also optionally have third parameter List<string> errorLog which when sent as Empty list(not null) will be loaded with list of Exceptions if any were to happen during parsing and casting operations. When method is called without this param, which remains null, in that case first error with throw Exception and procedure will be stopped.

Full Examples are in Tests of the project.

2. Data in FileFields

Second usage is when one data record is in different rows at defined positions;
Record per File (Fixed/Relative-Height), E.g.:

SoftysTech LCC
__________________________________________________________________
Invoice Date: 2018-10-30 Buyer: SysCompanik
INVOICE no. 0169/18
No | Description | Qty | Price | Amount |
...
...
------------------------------------------------------------------
1,299.00 Date: 2018-10-31 Financial Manager
John Doe

For parsing/writing [FixedWidthFileField] attributes are used, that have additional parameter:

  • Line - in which we define line number where the value is (Negative values represent row number from bottom)

For type FileField param. Length not required, if not set means value goes till end of row(trimmed), and Start has default = 1.

publicclassInvoice{[FixedWidthFileField(Line=1)]publicstringCompanyName{get;set;}[FixedWidthFileField(Line=4,Start=15,Length=19,Format="yyyy-MM-dd")]publicDateTimeDate{get;set;}[FixedWidthFileField(Line=4,Start=43)]publicstringBuyerName{get;set;}[FixedWidthFileField(Line=6,Start=37)]publicstringInvoiceNumber{get;set;}[FixedWidthFileField(Line=-4,Length=66,Format="0,000.00")]publicdecimalAmountTotal{get;set;}[FixedWidthFileField(Line=-2,Start=7,Length=10,Format="yyyy-MM-dd")]publicDateTimeDateCreated{get;set;}[FixedWidthFileField(Line=-2,Start=17,Length=50,PadSide=PadSide.Left)]publicstringSignatoryTitle{get;set;}// Line Negative - counted from bottom [FixedWidthFileField(Line=-1,Length=66,PadSide=PadSide.Left)]publicstringSignatureName{get;set;}}

Usage:

publicInvoiceParseFieldsFromFile(newList<string> fileLines){
invoice invoice =newFixedWidthFileProvider<Invoice>().Parse(fileLines);returninvoice;}publicList<string>WriteFieldsToFile(Invoice){List<string>templateLines=GetDataFormTemplate();varfileProvider=newFixedWidthFileProvider<Invoice>(){Content=templateLines};fileProvider.UpdateContent(invoice);invoice.UpdateContent();returninvoice.Content;}

DataFormTemplate looks like this:

{CompanyName}
__________________________________________________________________
Invoice Date: {InvoiceDate} Buyer: {BuyerName}
INVOICE no. NNNN/YY
No | Description | Qty | Price | Amount |
...
...
------------------------------------------------------------------
0.00 Date: {DateCreated} {SignatoryTitle}
{SignatureName}

In situation where many same type properties have Format different from default one, instead of setting custom format individually for each Property, it is possible to override DefaultConfig for certain data types/groups in that class:

publicclassInvoice:IFixedWidth{publicDefaultConfigGetDefaultConfig(intStructureTypeId){returnnewDefaultConfig{FormatDateTime="yyyy-MM-dd"};}[FixedWidthFileField(Line=1)]publicstringCompanyName{get;set;}// Format set on class with FormatDateTime - not required on each Attribute of DateTime Property[FixedWidthFileField(Line=4,Start=15,Length=19/*, Format = "yyyy-MM-dd"*/)]publicDateTimeDate{get;set;}/* ... Other Properties */}

If we need to changed DefaultConfig(Format) for multiple models then we could override entire Provider to keep it DRY.

-Combining both previous usages we can make complex file structures like invoiceFull.
-In adddition we could also have grouped line with say header per segment in which case we can use multiple Line Models and then perfilter lines based on category, example invoiceGrouped.

When there is some special situation where some Fields can not be configured with existing options, then we can make additional custom parsing prior or/and after calling the method - PRE & POST Processing.

3. Data in CustomFileFields

Third use case (Parsing only) is when one data field is relative to some text position.

SoftysTech LCC company
__________________________________________________________________
Date generated: 30.06.2020.
Q REPORT no. 11/20** 1569ccACTN.162-677-169-796
...
Revenue:
1,234.55
...
xx
publicclassReport{[CustomFileField(EndsWith=" company")]publicstringCompanyName{get;set;}[CustomFileField(StartsWith="Date generated: ",Format="d.M.yyyy.")]publicDateTimeDate{get;set;}[CustomFileField(Contains="Q REPORT no. ",RemoveText="**")]publicstringNumber{get;set;}[CustomFileField(Contains="ACTN.",Length=-15)]publicstringAccount{get;set;}[CustomFileField(StartsWith="Revenue:",Offset=1)]publicdecimalRevenue{get;set;}}

For parsing [CustomFileField] attributes are used, with additional params:

  • StartsWith, EndsWith, Contains - finds lines with first occurance of search criteria
  • Offset - moves found line up(is positive) or down(negative value) for defined number of rows
  • RemoveText - to clear custom substring from text value before additional casting
  • RemoveStartsWith, RemoveEndsWith, RemoveContains - defaults are 'True' meaning that search string is also cleared

Code Structure:

FixedWidthBaseProvider { ParseData(); WriteData() }
|
|---FixedWidthLinesProvider : FWBaseProvider { Parse(); Write() }
|---FixedWidthFileProvider : FWBaseProvider { Parse(); UpdateContent() }
|---CustomFileProvider : FWBaseProvider { Parse() }

About

Reading & Writing fixed-width data (files) with defined field position in line or file(relative height).

Resources

Code of conduct

Contributing

Stars

38 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

FixedWidthParserWriter

Library (C# .NET) for Parsing(Reading) & Writing fixed-width/flat data files (.txt, others).
Uses FastMember instead of slower Reflection.
It is targeting NetStandard 2.0 so it can be used on project targeting NetCore(2.0+) or NetFramework(4.6.1+).
Config base on POCO class/object model with Attributes.

There are 2 main types of usage that are explained in the following segments:
1. LineFields
2. FileFields and
3. CustomFileFields (additional third type or 2.b)
All are simple to use and easily configured with Attributes.

Logo:

NuGet

Installation

Available on NuGet latest version.
Package manager console command for installation: Install-Package FixedWidthParserWriter

Support

If you find this project useful you can mark it by leaving a Github Star
And even with community license, if you want help development, you can make a DONATION:
Buy Me A Coffee _ or _ Button
Want to Contact for Development & Consulting: www.codis.tech (Quality Assurance)

Contributing

Please read CONTRIBUTING for details on code of conduct, and the process for submitting pull requests.
When opening issues do write detailed explanation of the problem or feature with reproducible example.

**Also take a look into others packages:
Open source (MIT or cFOSS) authored .Net libraries (@Infopedia.io personal blog post)

.Net libraryDescription
1EFCore.BulkExtensionsEF Core Bulk CRUD Ops (Flagship Lib)
2EFCore.UtilExtensionsEF Core Custom Annotations and AuditInfo
3EFCore.FluentApiToAnnotationConverting FluentApi configuration to Annotations
4*FixedWidthParserWriterReading & Writing fixed-width/flat data files
5CsCodeGeneratorC# code generation based on Classes and elements
6CsCodeExampleExamples of C# code in form of a simple tutorial

CONFIG

FixedWidthConfig options (provides configuration for the Provider): defaultValue

intStructureTypeId:0	...............................// for multiple Attributes structureDictionary<string,FixedWidthAttribute> DynamicSettings	// Attributes defined at runtimebool LogAndSkipErrors:false .......................... // when True parsing exception skippedList<string> ErrorsLog :newList<string>() ...........// output field, logs ErrorMessagesList<string> WarningsLog :new List<string>() .........// output field, List for Warnings

*-Special feature is 'DYNAMIC Settings' with which Attributes values can be defined at runtime, for all usage types.
Data is forwarded using Dict with PropertyName and new independent Attribute with parameter values:
Dictionary<string, FixedWidthAttribute> dynamicSettings.
It can be set for all needed Properties when having no Attributes, or just add/override some specific.
And if need to exclude ones that has regular Atribute then set it with Null.
Sample in test LineParserTest.

WarningsLog filled when Writing string is cut to fit into defined position.

1. Data in LineFields

First is regular flat data file;
Record per Line (Fixed-Width), for example:

No | Description | Qty | Price | Amount |
1.Laptop Dell xps13 1 821.00 821.00
2.Monitor Asus 32'' 2 478.00 956.00

For parsing/writing we make a model which Properties have [FixedWidthLineField] Attribute:

publicclassInvoiceItem{[FixedWidthLineField(Start=1,Length=3)]publicintNumber{get;set;}[FixedWidthLineField(Start=4,Length=1)]publicstringNumberedBullet{get;set;}=".";[FixedWidthLineField(Start=5,Length=30)]publicstringDescription{get;set;}[FixedWidthLineField(Start=35,Length=6)]publicintQuantity{get;set;}[FixedWidthLineField(Start=41,Length=13)]publicdecimalPrice{get;set;}[FixedWidthLineField(Start=54,Length=13)]publicdecimalAmount=>Quantity*Price;}

Then we can call it like this:

// dataLines stripped of headerpublicList<Invoiceitem>ParseFieldsFromLines(newList<string> dataLines){
List<InvoiceItem> invoiceItems =newFixedWidthLinesProvider<InvoiceItem>().Parse(dataLines);returninvoiceItems;}publicList<string>WriteFieldsToLines(List<InvoiceItem>invoiceItems){List<string>dataLines=newFixedWidthLinesProvider<InvoiceItem>().Write(invoiceItems);returndataLines;}

[FixedWidthLineField] has following parameters that can be configured for each Property:

  • Start - required for LineType so that order of lineFields does not depends on order of modelPropertis
  • Length - when writing if Property value longer then defined in Length it is cut from the right to fit - valueTrim;
    zero 0 value means entire line and negative values means start from Right side
  • Format - Defaults per data type or group
  • Pad - Defaults per data category: { PadNumeric = ' ', PadNonNumeric = ' ' }
  • PadSide - Defaults per data category: { PadSideNumeric = PadSide.Left, PadSideNonNumeric = PadSide.Right }
  • DoTrim - Default is 'True' when text will be trimmed before casting
  • StructureTypeId - Default = 0, used when having multiple files with different structure or format for same data
  • NullPattern - Default = string.Empty, Pattern used to represent a null value

*_Format types:
-FormatNumberInteger Default = "0", *groupFormat:Int32,Int64
-FormatNumberDecimal Default = "0.00", *groupFormat:Decimal,Single,Double
("0;00" - Special custom Format that removes decimal separator: 123.45 -> 12345)
-FormatBoolean . . . . . . Default = "1; ;0" ("ValueForTrue; ValueForNull; ValueForFalse")
-FormatDateTime. . . . . .Default = "yyyyMMdd"
Custom format strings for Numeric and DateTime.

When need more then 1 file structure/format we can put multiple Attributes per Property with different StructureTypeId.
Next example shows 2 structures, second has one less Property and different PadNumeric: '0' instead of ' '(space).
To change DefaultConfig per StructureType, model should implement IFixedWidth interface with SetDefaultConfig() func.

publicenumConfigType{Alpha,Beta}publicclassInvoiceItem:IFixedWidth{publicDefaultConfigGetDefaultConfig(intStructureTypeId){vardefaultConfig=newDefaultConfig();switch((ConfigType)StructureTypeId){caseConfigType.Alpha:// config remains initial defaultbreak;caseConfigType.Beta:defaultConfig.PadNumeric='0';break;}returndefaultConfig;}[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=1,Length=3)][FixedWidthLineField(StructureTypeId=(int)ConfigType.Beta,Start=1,Length=4)]publicintNumber{get;set;}[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=4,Length=1)]publicstringNumberedBullet{get;set;}=".";[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=5,Length=30)][FixedWidthLineField(StructureTypeId=(int)ConfigType.Beta,Start=5,Length=30)]publicstringDescription{get;set;}//... Others Properties}

Beta Structure:

No | Description | Qty | Price | Amount |
0001Laptop Dell xps13 0000010000000821.000000000821.00
0002Monitor Asus 32'' 0000020000000478.000000000956.00

Calling the methods:

var linesProvider = new FixedWidthLinesProvider<InvoiceItem>();
List<InvoiceItem> itemsA = linesProvider.Parse(dataLinesA, (int)ConfigType.Alpha);
List<InvoiceItem> itemsB = linesProvider.Parse(dataLinesB, (int)ConfigType.Alpha);

PARSE method for all use cases can also optionally have third parameter List<string> errorLog which when sent as Empty list(not null) will be loaded with list of Exceptions if any were to happen during parsing and casting operations. When method is called without this param, which remains null, in that case first error with throw Exception and procedure will be stopped.

Full Examples are in Tests of the project.

2. Data in FileFields

Second usage is when one data record is in different rows at defined positions;
Record per File (Fixed/Relative-Height), E.g.:

SoftysTech LCC
__________________________________________________________________
Invoice Date: 2018-10-30 Buyer: SysCompanik
INVOICE no. 0169/18
No | Description | Qty | Price | Amount |
...
...
------------------------------------------------------------------
1,299.00 Date: 2018-10-31 Financial Manager
John Doe

For parsing/writing [FixedWidthFileField] attributes are used, that have additional parameter:

  • Line - in which we define line number where the value is (Negative values represent row number from bottom)

For type FileField param. Length not required, if not set means value goes till end of row(trimmed), and Start has default = 1.

publicclassInvoice{[FixedWidthFileField(Line=1)]publicstringCompanyName{get;set;}[FixedWidthFileField(Line=4,Start=15,Length=19,Format="yyyy-MM-dd")]publicDateTimeDate{get;set;}[FixedWidthFileField(Line=4,Start=43)]publicstringBuyerName{get;set;}[FixedWidthFileField(Line=6,Start=37)]publicstringInvoiceNumber{get;set;}[FixedWidthFileField(Line=-4,Length=66,Format="0,000.00")]publicdecimalAmountTotal{get;set;}[FixedWidthFileField(Line=-2,Start=7,Length=10,Format="yyyy-MM-dd")]publicDateTimeDateCreated{get;set;}[FixedWidthFileField(Line=-2,Start=17,Length=50,PadSide=PadSide.Left)]publicstringSignatoryTitle{get;set;}// Line Negative - counted from bottom [FixedWidthFileField(Line=-1,Length=66,PadSide=PadSide.Left)]publicstringSignatureName{get;set;}}

Usage:

publicInvoiceParseFieldsFromFile(newList<string> fileLines){
invoice invoice =newFixedWidthFileProvider<Invoice>().Parse(fileLines);returninvoice;}publicList<string>WriteFieldsToFile(Invoice){List<string>templateLines=GetDataFormTemplate();varfileProvider=newFixedWidthFileProvider<Invoice>(){Content=templateLines};fileProvider.UpdateContent(invoice);invoice.UpdateContent();returninvoice.Content;}

DataFormTemplate looks like this:

{CompanyName}
__________________________________________________________________
Invoice Date: {InvoiceDate} Buyer: {BuyerName}
INVOICE no. NNNN/YY
No | Description | Qty | Price | Amount |
...
...
------------------------------------------------------------------
0.00 Date: {DateCreated} {SignatoryTitle}
{SignatureName}

In situation where many same type properties have Format different from default one, instead of setting custom format individually for each Property, it is possible to override DefaultConfig for certain data types/groups in that class:

publicclassInvoice:IFixedWidth{publicDefaultConfigGetDefaultConfig(intStructureTypeId){returnnewDefaultConfig{FormatDateTime="yyyy-MM-dd"};}[FixedWidthFileField(Line=1)]publicstringCompanyName{get;set;}// Format set on class with FormatDateTime - not required on each Attribute of DateTime Property[FixedWidthFileField(Line=4,Start=15,Length=19/*, Format = "yyyy-MM-dd"*/)]publicDateTimeDate{get;set;}/* ... Other Properties */}

If we need to changed DefaultConfig(Format) for multiple models then we could override entire Provider to keep it DRY.

-Combining both previous usages we can make complex file structures like invoiceFull.
-In adddition we could also have grouped line with say header per segment in which case we can use multiple Line Models and then perfilter lines based on category, example invoiceGrouped.

When there is some special situation where some Fields can not be configured with existing options, then we can make additional custom parsing prior or/and after calling the method - PRE & POST Processing.

3. Data in CustomFileFields

Third use case (Parsing only) is when one data field is relative to some text position.

SoftysTech LCC company
__________________________________________________________________
Date generated: 30.06.2020.
Q REPORT no. 11/20** 1569ccACTN.162-677-169-796
...
Revenue:
1,234.55
...
xx
publicclassReport{[CustomFileField(EndsWith=" company")]publicstringCompanyName{get;set;}[CustomFileField(StartsWith="Date generated: ",Format="d.M.yyyy.")]publicDateTimeDate{get;set;}[CustomFileField(Contains="Q REPORT no. ",RemoveText="**")]publicstringNumber{get;set;}[CustomFileField(Contains="ACTN.",Length=-15)]publicstringAccount{get;set;}[CustomFileField(StartsWith="Revenue:",Offset=1)]publicdecimalRevenue{get;set;}}

For parsing [CustomFileField] attributes are used, with additional params:

  • StartsWith, EndsWith, Contains - finds lines with first occurance of search criteria
  • Offset - moves found line up(is positive) or down(negative value) for defined number of rows
  • RemoveText - to clear custom substring from text value before additional casting
  • RemoveStartsWith, RemoveEndsWith, RemoveContains - defaults are 'True' meaning that search string is also cleared

Code Structure:

FixedWidthBaseProvider { ParseData(); WriteData() }
|
|---FixedWidthLinesProvider : FWBaseProvider { Parse(); Write() }
|---FixedWidthFileProvider : FWBaseProvider { Parse(); UpdateContent() }
|---CustomFileProvider : FWBaseProvider { Parse() }

About

Reading & Writing fixed-width data (files) with defined field position in line or file(relative height).

Resources

Code of conduct

Contributing

Stars

38 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

FixedWidthParserWriter

Library (C# .NET) for Parsing(Reading) & Writing fixed-width/flat data files (.txt, others).
Uses FastMember instead of slower Reflection.
It is targeting NetStandard 2.0 so it can be used on project targeting NetCore(2.0+) or NetFramework(4.6.1+).
Config base on POCO class/object model with Attributes.

There are 2 main types of usage that are explained in the following segments:
1. LineFields
2. FileFields and
3. CustomFileFields (additional third type or 2.b)
All are simple to use and easily configured with Attributes.

Logo:

NuGet

Installation

Available on NuGet latest version.
Package manager console command for installation: Install-Package FixedWidthParserWriter

Support

If you find this project useful you can mark it by leaving a Github Star
And even with community license, if you want help development, you can make a DONATION:
Buy Me A Coffee _ or _ Button
Want to Contact for Development & Consulting: www.codis.tech (Quality Assurance)

Contributing

Please read CONTRIBUTING for details on code of conduct, and the process for submitting pull requests.
When opening issues do write detailed explanation of the problem or feature with reproducible example.

**Also take a look into others packages:
Open source (MIT or cFOSS) authored .Net libraries (@Infopedia.io personal blog post)

.Net libraryDescription
1EFCore.BulkExtensionsEF Core Bulk CRUD Ops (Flagship Lib)
2EFCore.UtilExtensionsEF Core Custom Annotations and AuditInfo
3EFCore.FluentApiToAnnotationConverting FluentApi configuration to Annotations
4*FixedWidthParserWriterReading & Writing fixed-width/flat data files
5CsCodeGeneratorC# code generation based on Classes and elements
6CsCodeExampleExamples of C# code in form of a simple tutorial

CONFIG

FixedWidthConfig options (provides configuration for the Provider): defaultValue

intStructureTypeId:0	...............................// for multiple Attributes structureDictionary<string,FixedWidthAttribute> DynamicSettings	// Attributes defined at runtimebool LogAndSkipErrors:false .......................... // when True parsing exception skippedList<string> ErrorsLog :newList<string>() ...........// output field, logs ErrorMessagesList<string> WarningsLog :new List<string>() .........// output field, List for Warnings

*-Special feature is 'DYNAMIC Settings' with which Attributes values can be defined at runtime, for all usage types.
Data is forwarded using Dict with PropertyName and new independent Attribute with parameter values:
Dictionary<string, FixedWidthAttribute> dynamicSettings.
It can be set for all needed Properties when having no Attributes, or just add/override some specific.
And if need to exclude ones that has regular Atribute then set it with Null.
Sample in test LineParserTest.

WarningsLog filled when Writing string is cut to fit into defined position.

1. Data in LineFields

First is regular flat data file;
Record per Line (Fixed-Width), for example:

No | Description | Qty | Price | Amount |
1.Laptop Dell xps13 1 821.00 821.00
2.Monitor Asus 32'' 2 478.00 956.00

For parsing/writing we make a model which Properties have [FixedWidthLineField] Attribute:

publicclassInvoiceItem{[FixedWidthLineField(Start=1,Length=3)]publicintNumber{get;set;}[FixedWidthLineField(Start=4,Length=1)]publicstringNumberedBullet{get;set;}=".";[FixedWidthLineField(Start=5,Length=30)]publicstringDescription{get;set;}[FixedWidthLineField(Start=35,Length=6)]publicintQuantity{get;set;}[FixedWidthLineField(Start=41,Length=13)]publicdecimalPrice{get;set;}[FixedWidthLineField(Start=54,Length=13)]publicdecimalAmount=>Quantity*Price;}

Then we can call it like this:

// dataLines stripped of headerpublicList<Invoiceitem>ParseFieldsFromLines(newList<string> dataLines){
List<InvoiceItem> invoiceItems =newFixedWidthLinesProvider<InvoiceItem>().Parse(dataLines);returninvoiceItems;}publicList<string>WriteFieldsToLines(List<InvoiceItem>invoiceItems){List<string>dataLines=newFixedWidthLinesProvider<InvoiceItem>().Write(invoiceItems);returndataLines;}

[FixedWidthLineField] has following parameters that can be configured for each Property:

  • Start - required for LineType so that order of lineFields does not depends on order of modelPropertis
  • Length - when writing if Property value longer then defined in Length it is cut from the right to fit - valueTrim;
    zero 0 value means entire line and negative values means start from Right side
  • Format - Defaults per data type or group
  • Pad - Defaults per data category: { PadNumeric = ' ', PadNonNumeric = ' ' }
  • PadSide - Defaults per data category: { PadSideNumeric = PadSide.Left, PadSideNonNumeric = PadSide.Right }
  • DoTrim - Default is 'True' when text will be trimmed before casting
  • StructureTypeId - Default = 0, used when having multiple files with different structure or format for same data
  • NullPattern - Default = string.Empty, Pattern used to represent a null value

*_Format types:
-FormatNumberInteger Default = "0", *groupFormat:Int32,Int64
-FormatNumberDecimal Default = "0.00", *groupFormat:Decimal,Single,Double
("0;00" - Special custom Format that removes decimal separator: 123.45 -> 12345)
-FormatBoolean . . . . . . Default = "1; ;0" ("ValueForTrue; ValueForNull; ValueForFalse")
-FormatDateTime. . . . . .Default = "yyyyMMdd"
Custom format strings for Numeric and DateTime.

When need more then 1 file structure/format we can put multiple Attributes per Property with different StructureTypeId.
Next example shows 2 structures, second has one less Property and different PadNumeric: '0' instead of ' '(space).
To change DefaultConfig per StructureType, model should implement IFixedWidth interface with SetDefaultConfig() func.

publicenumConfigType{Alpha,Beta}publicclassInvoiceItem:IFixedWidth{publicDefaultConfigGetDefaultConfig(intStructureTypeId){vardefaultConfig=newDefaultConfig();switch((ConfigType)StructureTypeId){caseConfigType.Alpha:// config remains initial defaultbreak;caseConfigType.Beta:defaultConfig.PadNumeric='0';break;}returndefaultConfig;}[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=1,Length=3)][FixedWidthLineField(StructureTypeId=(int)ConfigType.Beta,Start=1,Length=4)]publicintNumber{get;set;}[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=4,Length=1)]publicstringNumberedBullet{get;set;}=".";[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=5,Length=30)][FixedWidthLineField(StructureTypeId=(int)ConfigType.Beta,Start=5,Length=30)]publicstringDescription{get;set;}//... Others Properties}

Beta Structure:

No | Description | Qty | Price | Amount |
0001Laptop Dell xps13 0000010000000821.000000000821.00
0002Monitor Asus 32'' 0000020000000478.000000000956.00

Calling the methods:

var linesProvider = new FixedWidthLinesProvider<InvoiceItem>();
List<InvoiceItem> itemsA = linesProvider.Parse(dataLinesA, (int)ConfigType.Alpha);
List<InvoiceItem> itemsB = linesProvider.Parse(dataLinesB, (int)ConfigType.Alpha);

PARSE method for all use cases can also optionally have third parameter List<string> errorLog which when sent as Empty list(not null) will be loaded with list of Exceptions if any were to happen during parsing and casting operations. When method is called without this param, which remains null, in that case first error with throw Exception and procedure will be stopped.

Full Examples are in Tests of the project.

2. Data in FileFields

Second usage is when one data record is in different rows at defined positions;
Record per File (Fixed/Relative-Height), E.g.:

SoftysTech LCC
__________________________________________________________________
Invoice Date: 2018-10-30 Buyer: SysCompanik
INVOICE no. 0169/18
No | Description | Qty | Price | Amount |
...
...
------------------------------------------------------------------
1,299.00 Date: 2018-10-31 Financial Manager
John Doe

For parsing/writing [FixedWidthFileField] attributes are used, that have additional parameter:

  • Line - in which we define line number where the value is (Negative values represent row number from bottom)

For type FileField param. Length not required, if not set means value goes till end of row(trimmed), and Start has default = 1.

publicclassInvoice{[FixedWidthFileField(Line=1)]publicstringCompanyName{get;set;}[FixedWidthFileField(Line=4,Start=15,Length=19,Format="yyyy-MM-dd")]publicDateTimeDate{get;set;}[FixedWidthFileField(Line=4,Start=43)]publicstringBuyerName{get;set;}[FixedWidthFileField(Line=6,Start=37)]publicstringInvoiceNumber{get;set;}[FixedWidthFileField(Line=-4,Length=66,Format="0,000.00")]publicdecimalAmountTotal{get;set;}[FixedWidthFileField(Line=-2,Start=7,Length=10,Format="yyyy-MM-dd")]publicDateTimeDateCreated{get;set;}[FixedWidthFileField(Line=-2,Start=17,Length=50,PadSide=PadSide.Left)]publicstringSignatoryTitle{get;set;}// Line Negative - counted from bottom [FixedWidthFileField(Line=-1,Length=66,PadSide=PadSide.Left)]publicstringSignatureName{get;set;}}

Usage:

publicInvoiceParseFieldsFromFile(newList<string> fileLines){
invoice invoice =newFixedWidthFileProvider<Invoice>().Parse(fileLines);returninvoice;}publicList<string>WriteFieldsToFile(Invoice){List<string>templateLines=GetDataFormTemplate();varfileProvider=newFixedWidthFileProvider<Invoice>(){Content=templateLines};fileProvider.UpdateContent(invoice);invoice.UpdateContent();returninvoice.Content;}

DataFormTemplate looks like this:

{CompanyName}
__________________________________________________________________
Invoice Date: {InvoiceDate} Buyer: {BuyerName}
INVOICE no. NNNN/YY
No | Description | Qty | Price | Amount |
...
...
------------------------------------------------------------------
0.00 Date: {DateCreated} {SignatoryTitle}
{SignatureName}

In situation where many same type properties have Format different from default one, instead of setting custom format individually for each Property, it is possible to override DefaultConfig for certain data types/groups in that class:

publicclassInvoice:IFixedWidth{publicDefaultConfigGetDefaultConfig(intStructureTypeId){returnnewDefaultConfig{FormatDateTime="yyyy-MM-dd"};}[FixedWidthFileField(Line=1)]publicstringCompanyName{get;set;}// Format set on class with FormatDateTime - not required on each Attribute of DateTime Property[FixedWidthFileField(Line=4,Start=15,Length=19/*, Format = "yyyy-MM-dd"*/)]publicDateTimeDate{get;set;}/* ... Other Properties */}

If we need to changed DefaultConfig(Format) for multiple models then we could override entire Provider to keep it DRY.

-Combining both previous usages we can make complex file structures like invoiceFull.
-In adddition we could also have grouped line with say header per segment in which case we can use multiple Line Models and then perfilter lines based on category, example invoiceGrouped.

When there is some special situation where some Fields can not be configured with existing options, then we can make additional custom parsing prior or/and after calling the method - PRE & POST Processing.

3. Data in CustomFileFields

Third use case (Parsing only) is when one data field is relative to some text position.

SoftysTech LCC company
__________________________________________________________________
Date generated: 30.06.2020.
Q REPORT no. 11/20** 1569ccACTN.162-677-169-796
...
Revenue:
1,234.55
...
xx
publicclassReport{[CustomFileField(EndsWith=" company")]publicstringCompanyName{get;set;}[CustomFileField(StartsWith="Date generated: ",Format="d.M.yyyy.")]publicDateTimeDate{get;set;}[CustomFileField(Contains="Q REPORT no. ",RemoveText="**")]publicstringNumber{get;set;}[CustomFileField(Contains="ACTN.",Length=-15)]publicstringAccount{get;set;}[CustomFileField(StartsWith="Revenue:",Offset=1)]publicdecimalRevenue{get;set;}}

For parsing [CustomFileField] attributes are used, with additional params:

  • StartsWith, EndsWith, Contains - finds lines with first occurance of search criteria
  • Offset - moves found line up(is positive) or down(negative value) for defined number of rows
  • RemoveText - to clear custom substring from text value before additional casting
  • RemoveStartsWith, RemoveEndsWith, RemoveContains - defaults are 'True' meaning that search string is also cleared

Code Structure:

FixedWidthBaseProvider { ParseData(); WriteData() }
|
|---FixedWidthLinesProvider : FWBaseProvider { Parse(); Write() }
|---FixedWidthFileProvider : FWBaseProvider { Parse(); UpdateContent() }
|---CustomFileProvider : FWBaseProvider { Parse() }

About

Reading & Writing fixed-width data (files) with defined field position in line or file(relative height).

Resources

Code of conduct

Contributing

Stars

38 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

FixedWidthParserWriter

Library (C# .NET) for Parsing(Reading) & Writing fixed-width/flat data files (.txt, others).
Uses FastMember instead of slower Reflection.
It is targeting NetStandard 2.0 so it can be used on project targeting NetCore(2.0+) or NetFramework(4.6.1+).
Config base on POCO class/object model with Attributes.

There are 2 main types of usage that are explained in the following segments:
1. LineFields
2. FileFields and
3. CustomFileFields (additional third type or 2.b)
All are simple to use and easily configured with Attributes.

Logo:

NuGet

Installation

Available on NuGet latest version.
Package manager console command for installation: Install-Package FixedWidthParserWriter

Support

If you find this project useful you can mark it by leaving a Github Star
And even with community license, if you want help development, you can make a DONATION:
Buy Me A Coffee _ or _ Button
Want to Contact for Development & Consulting: www.codis.tech (Quality Assurance)

Contributing

Please read CONTRIBUTING for details on code of conduct, and the process for submitting pull requests.
When opening issues do write detailed explanation of the problem or feature with reproducible example.

**Also take a look into others packages:
Open source (MIT or cFOSS) authored .Net libraries (@Infopedia.io personal blog post)

.Net libraryDescription
1EFCore.BulkExtensionsEF Core Bulk CRUD Ops (Flagship Lib)
2EFCore.UtilExtensionsEF Core Custom Annotations and AuditInfo
3EFCore.FluentApiToAnnotationConverting FluentApi configuration to Annotations
4*FixedWidthParserWriterReading & Writing fixed-width/flat data files
5CsCodeGeneratorC# code generation based on Classes and elements
6CsCodeExampleExamples of C# code in form of a simple tutorial

CONFIG

FixedWidthConfig options (provides configuration for the Provider): defaultValue

intStructureTypeId:0	...............................// for multiple Attributes structureDictionary<string,FixedWidthAttribute> DynamicSettings	// Attributes defined at runtimebool LogAndSkipErrors:false .......................... // when True parsing exception skippedList<string> ErrorsLog :newList<string>() ...........// output field, logs ErrorMessagesList<string> WarningsLog :new List<string>() .........// output field, List for Warnings

*-Special feature is 'DYNAMIC Settings' with which Attributes values can be defined at runtime, for all usage types.
Data is forwarded using Dict with PropertyName and new independent Attribute with parameter values:
Dictionary<string, FixedWidthAttribute> dynamicSettings.
It can be set for all needed Properties when having no Attributes, or just add/override some specific.
And if need to exclude ones that has regular Atribute then set it with Null.
Sample in test LineParserTest.

WarningsLog filled when Writing string is cut to fit into defined position.

1. Data in LineFields

First is regular flat data file;
Record per Line (Fixed-Width), for example:

No | Description | Qty | Price | Amount |
1.Laptop Dell xps13 1 821.00 821.00
2.Monitor Asus 32'' 2 478.00 956.00

For parsing/writing we make a model which Properties have [FixedWidthLineField] Attribute:

publicclassInvoiceItem{[FixedWidthLineField(Start=1,Length=3)]publicintNumber{get;set;}[FixedWidthLineField(Start=4,Length=1)]publicstringNumberedBullet{get;set;}=".";[FixedWidthLineField(Start=5,Length=30)]publicstringDescription{get;set;}[FixedWidthLineField(Start=35,Length=6)]publicintQuantity{get;set;}[FixedWidthLineField(Start=41,Length=13)]publicdecimalPrice{get;set;}[FixedWidthLineField(Start=54,Length=13)]publicdecimalAmount=>Quantity*Price;}

Then we can call it like this:

// dataLines stripped of headerpublicList<Invoiceitem>ParseFieldsFromLines(newList<string> dataLines){
List<InvoiceItem> invoiceItems =newFixedWidthLinesProvider<InvoiceItem>().Parse(dataLines);returninvoiceItems;}publicList<string>WriteFieldsToLines(List<InvoiceItem>invoiceItems){List<string>dataLines=newFixedWidthLinesProvider<InvoiceItem>().Write(invoiceItems);returndataLines;}

[FixedWidthLineField] has following parameters that can be configured for each Property:

  • Start - required for LineType so that order of lineFields does not depends on order of modelPropertis
  • Length - when writing if Property value longer then defined in Length it is cut from the right to fit - valueTrim;
    zero 0 value means entire line and negative values means start from Right side
  • Format - Defaults per data type or group
  • Pad - Defaults per data category: { PadNumeric = ' ', PadNonNumeric = ' ' }
  • PadSide - Defaults per data category: { PadSideNumeric = PadSide.Left, PadSideNonNumeric = PadSide.Right }
  • DoTrim - Default is 'True' when text will be trimmed before casting
  • StructureTypeId - Default = 0, used when having multiple files with different structure or format for same data
  • NullPattern - Default = string.Empty, Pattern used to represent a null value

*_Format types:
-FormatNumberInteger Default = "0", *groupFormat:Int32,Int64
-FormatNumberDecimal Default = "0.00", *groupFormat:Decimal,Single,Double
("0;00" - Special custom Format that removes decimal separator: 123.45 -> 12345)
-FormatBoolean . . . . . . Default = "1; ;0" ("ValueForTrue; ValueForNull; ValueForFalse")
-FormatDateTime. . . . . .Default = "yyyyMMdd"
Custom format strings for Numeric and DateTime.

When need more then 1 file structure/format we can put multiple Attributes per Property with different StructureTypeId.
Next example shows 2 structures, second has one less Property and different PadNumeric: '0' instead of ' '(space).
To change DefaultConfig per StructureType, model should implement IFixedWidth interface with SetDefaultConfig() func.

publicenumConfigType{Alpha,Beta}publicclassInvoiceItem:IFixedWidth{publicDefaultConfigGetDefaultConfig(intStructureTypeId){vardefaultConfig=newDefaultConfig();switch((ConfigType)StructureTypeId){caseConfigType.Alpha:// config remains initial defaultbreak;caseConfigType.Beta:defaultConfig.PadNumeric='0';break;}returndefaultConfig;}[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=1,Length=3)][FixedWidthLineField(StructureTypeId=(int)ConfigType.Beta,Start=1,Length=4)]publicintNumber{get;set;}[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=4,Length=1)]publicstringNumberedBullet{get;set;}=".";[FixedWidthLineField(StructureTypeId=(int)ConfigType.Alpha,Start=5,Length=30)][FixedWidthLineField(StructureTypeId=(int)ConfigType.Beta,Start=5,Length=30)]publicstringDescription{get;set;}//... Others Properties}

Beta Structure:

No | Description | Qty | Price | Amount |
0001Laptop Dell xps13 0000010000000821.000000000821.00
0002Monitor Asus 32'' 0000020000000478.000000000956.00

Calling the methods:

var linesProvider = new FixedWidthLinesProvider<InvoiceItem>();
List<InvoiceItem> itemsA = linesProvider.Parse(dataLinesA, (int)ConfigType.Alpha);
List<InvoiceItem> itemsB = linesProvider.Parse(dataLinesB, (int)ConfigType.Alpha);

PARSE method for all use cases can also optionally have third parameter List<string> errorLog which when sent as Empty list(not null) will be loaded with list of Exceptions if any were to happen during parsing and casting operations. When method is called without this param, which remains null, in that case first error with throw Exception and procedure will be stopped.

Full Examples are in Tests of the project.

2. Data in FileFields

Second usage is when one data record is in different rows at defined positions;
Record per File (Fixed/Relative-Height), E.g.:

SoftysTech LCC
__________________________________________________________________
Invoice Date: 2018-10-30 Buyer: SysCompanik
INVOICE no. 0169/18
No | Description | Qty | Price | Amount |
...
...
------------------------------------------------------------------
1,299.00 Date: 2018-10-31 Financial Manager
John Doe

For parsing/writing [FixedWidthFileField] attributes are used, that have additional parameter:

  • Line - in which we define line number where the value is (Negative values represent row number from bottom)

For type FileField param. Length not required, if not set means value goes till end of row(trimmed), and Start has default = 1.

publicclassInvoice{[FixedWidthFileField(Line=1)]publicstringCompanyName{get;set;}[FixedWidthFileField(Line=4,Start=15,Length=19,Format="yyyy-MM-dd")]publicDateTimeDate{get;set;}[FixedWidthFileField(Line=4,Start=43)]publicstringBuyerName{get;set;}[FixedWidthFileField(Line=6,Start=37)]publicstringInvoiceNumber{get;set;}[FixedWidthFileField(Line=-4,Length=66,Format="0,000.00")]publicdecimalAmountTotal{get;set;}[FixedWidthFileField(Line=-2,Start=7,Length=10,Format="yyyy-MM-dd")]publicDateTimeDateCreated{get;set;}[FixedWidthFileField(Line=-2,Start=17,Length=50,PadSide=PadSide.Left)]publicstringSignatoryTitle{get;set;}// Line Negative - counted from bottom [FixedWidthFileField(Line=-1,Length=66,PadSide=PadSide.Left)]publicstringSignatureName{get;set;}}

Usage:

publicInvoiceParseFieldsFromFile(newList<string> fileLines){
invoice invoice =newFixedWidthFileProvider<Invoice>().Parse(fileLines);returninvoice;}publicList<string>WriteFieldsToFile(Invoice){List<string>templateLines=GetDataFormTemplate();varfileProvider=newFixedWidthFileProvider<Invoice>(){Content=templateLines};fileProvider.UpdateContent(invoice);invoice.UpdateContent();returninvoice.Content;}

DataFormTemplate looks like this:

{CompanyName}
__________________________________________________________________
Invoice Date: {InvoiceDate} Buyer: {BuyerName}
INVOICE no. NNNN/YY
No | Description | Qty | Price | Amount |
...
...
------------------------------------------------------------------
0.00 Date: {DateCreated} {SignatoryTitle}
{SignatureName}

In situation where many same type properties have Format different from default one, instead of setting custom format individually for each Property, it is possible to override DefaultConfig for certain data types/groups in that class:

publicclassInvoice:IFixedWidth{publicDefaultConfigGetDefaultConfig(intStructureTypeId){returnnewDefaultConfig{FormatDateTime="yyyy-MM-dd"};}[FixedWidthFileField(Line=1)]publicstringCompanyName{get;set;}// Format set on class with FormatDateTime - not required on each Attribute of DateTime Property[FixedWidthFileField(Line=4,Start=15,Length=19/*, Format = "yyyy-MM-dd"*/)]publicDateTimeDate{get;set;}/* ... Other Properties */}

If we need to changed DefaultConfig(Format) for multiple models then we could override entire Provider to keep it DRY.

-Combining both previous usages we can make complex file structures like invoiceFull.
-In adddition we could also have grouped line with say header per segment in which case we can use multiple Line Models and then perfilter lines based on category, example invoiceGrouped.

When there is some special situation where some Fields can not be configured with existing options, then we can make additional custom parsing prior or/and after calling the method - PRE & POST Processing.

3. Data in CustomFileFields

Third use case (Parsing only) is when one data field is relative to some text position.

SoftysTech LCC company
__________________________________________________________________
Date generated: 30.06.2020.
Q REPORT no. 11/20** 1569ccACTN.162-677-169-796
...
Revenue:
1,234.55
...
xx
publicclassReport{[CustomFileField(EndsWith=" company")]publicstringCompanyName{get;set;}[CustomFileField(StartsWith="Date generated: ",Format="d.M.yyyy.")]publicDateTimeDate{get;set;}[CustomFileField(Contains="Q REPORT no. ",RemoveText="**")]publicstringNumber{get;set;}[CustomFileField(Contains="ACTN.",Length=-15)]publicstringAccount{get;set;}[CustomFileField(StartsWith="Revenue:",Offset=1)]publicdecimalRevenue{get;set;}}

For parsing [CustomFileField] attributes are used, with additional params:

  • StartsWith, EndsWith, Contains - finds lines with first occurance of search criteria
  • Offset - moves found line up(is positive) or down(negative value) for defined number of rows
  • RemoveText - to clear custom substring from text value before additional casting
  • RemoveStartsWith, RemoveEndsWith, RemoveContains - defaults are 'True' meaning that search string is also cleared

Code Structure:

FixedWidthBaseProvider { ParseData(); WriteData() }
|
|---FixedWidthLinesProvider : FWBaseProvider { Parse(); Write() }
|---FixedWidthFileProvider : FWBaseProvider { Parse(); UpdateContent() }
|---CustomFileProvider : FWBaseProvider { Parse() }

About

Reading & Writing fixed-width data (files) with defined field position in line or file(relative height).

Resources

Code of conduct

Contributing

Stars

38 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages