{"id":15111263,"url":"https://github.com/guiabolso/fixed-length-file-handler","last_synced_at":"2025-10-23T04:31:17.123Z","repository":{"id":38217418,"uuid":"220034685","full_name":"GuiaBolso/fixed-length-file-handler","owner":"GuiaBolso","description":"Handlers for Fixed Length files in a beautiful Kotlin DSL","archived":false,"fork":false,"pushed_at":"2023-02-02T20:04:19.000Z","size":98,"stargazers_count":15,"open_issues_count":4,"forks_count":4,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-01-30T17:24:54.080Z","etag":null,"topics":["dsl","file","fixed-length","fixed-length-format","fixed-length-records","fixed-width","kotlin","parser","parsing","parsing-library"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GuiaBolso.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-11-06T15:56:23.000Z","updated_at":"2024-12-09T19:55:25.000Z","dependencies_parsed_at":"2023-02-18T00:01:10.372Z","dependency_job_id":null,"html_url":"https://github.com/GuiaBolso/fixed-length-file-handler","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GuiaBolso%2Ffixed-length-file-handler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GuiaBolso%2Ffixed-length-file-handler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GuiaBolso%2Ffixed-length-file-handler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GuiaBolso%2Ffixed-length-file-handler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GuiaBolso","download_url":"https://codeload.github.com/GuiaBolso/fixed-length-file-handler/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":237780107,"owners_count":19365129,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dsl","file","fixed-length","fixed-length-format","fixed-length-records","fixed-width","kotlin","parser","parsing","parsing-library"],"created_at":"2024-09-26T00:02:51.498Z","updated_at":"2025-10-23T04:31:16.733Z","avatar_url":"https://github.com/GuiaBolso.png","language":"Kotlin","readme":"# Fixed Length File Handler\n\n[![Build](https://github.com/GuiaBolso/fixed-length-file-handler/actions/workflows/build.yml/badge.svg)](https://github.com/GuiaBolso/fixed-length-file-handler/actions/workflows/build.yml)\n[![GitHub](https://img.shields.io/github/license/GuiaBolso/fixed-length-file-handler)](https://github.com/GuiaBolso/fixed-length-file-handler/blob/master/LICENSE)\n![Maven Central](https://img.shields.io/maven-central/v/br.com.guiabolso/FixedLengthFileHandler)\n\n\n## Introduction\nWhen processing data from some systems (mainly legacy ones), it's usual to have Fixed Length Files, which are files that contain lines which content is split using a specific length for each field of a record.\n\nThis kind of files are sometimes tricky to handle as many times there is a spaghetti of string manipulations and padding, and character counting and... Well, many things to take care of.\n\nThis library comes to the rescue of programmers dealing with fixed length files. It enables you to simply define how your records are structured and it will handle these records for you in a nice Kotlin DSL for further processing.\n\n## Using with Gradle\n\nImport it into your dependencies:\n\n```kotlin\ndependencies {\n    implementation(\"br.com.guiabolso:FixedLengthFileHandler:{version}\")\n}\n```\n\n## Basic Usage\n\nThe basic usage assumes that you're reading a file with a single type of record.\n\nGiven a Fixed-Length File:\n\n\n#### Definition\n\n| Field | Type | Initial Position | Final Position Exclusive | \n| ----- | ---- | ---------------- | ------------------------ |\n| UserName | String | 0 | 30 |\n| User Document | Int | 30 | 39 |\n| User Registry Date | LocalDate | 39 | 49 | \n\n#### File\n\n```\nFirstUsername                 1234567892019-02-09\nSecondAndLongerUsername       9876543212018-03-10\nThirdUsernameWithShorterDoc   0000001232017-04-11\n```\n\nWe can parse it with the `fixedLengthFileParser` DSL:\n\n```kotlin\ndata class MyUserRecord(val username: String, val userDoc: Int, val registryDate: LocalDate)\n\nval fileInputStream: InputStream = getFileInputStream()\n\nfixedLengthFileParser\u003cMyUserRecord\u003e(fileInputStream) {\n    MyUserRecord(\n        field(0, 30, Padding.PaddingRight(' ')),\n        field(30, 39, Padding.PaddingLeft('0')),\n        field(39, 49)\n    )    \n}\n```\n\nThe library is prepared to handle `Left Padding` and `Right Padding`. It's also prepared to handle many of Kotlin/Java types.\n\n## Closing the file stream\n\n**Attention** - You're responsible for closing the stream after processing the sequence, so be sure to close it!\n\n## Default parsing\n\nThis library is prepared to handle some of the most usual Kotlin/Java types. More types may be added if they're required. The default types are:\n\n- String\n- Int\n- UInt\n- Double\n- Long\n- ULong\n- Char\n- Boolean (Case insensitive)\n- LocalDate (Using default DateTimeFormatter)\n- LocalTime (Using default DateTimeFormatter)\n- LocalDateTime (Using default DateTimeFormatter)\n- BigDecimal\n- Enum types\n\n## Decimal Parsing\n\nThere might be scenarios where the default parsing of a decimal (Double / BigDecimal) isn't enough, and you need to declare a special scale, for example `4299` might represent `42.99` (scale of 2, initially undeclared in the String).\n\nFor this particular case, you can use `decimalField` instead of `field`:\n\n```kotlin\ndecimalField(from = 0, toExclusive = 0, scale = 3, padding = NoPadding)\n```\n\n\n## Custom parsing\n\nThere might be times where the default types are not enough, and you need a custom parser for a given record.\n\nFor example: You know that a specific number contains a currency, and the last two digits are used for the cents.\n\nThis library is prepared to handle cases where you need custom parsing for a String, by modifying the `field` invocation:\n\n```kotlin\n\n// Parsing the field 0000520099 to 5200.99 \n\nfield(15, 25, Padding.PaddingLeft('0')) { str: String -\u003e StringBuilder(str).insert(str.length - 2, \".\").toString().toBigDecimal() }\n``` \n\n## Advanced Usage\n\nFor an unknown reason, many Fixed-Length file providers use the same file for more than one record, denoting a specific bit for record identification, so there's a possibility that this happens:\n\n```\n1 FirstUserName       123.12\n1 SecondUserName      002.50\n2 123456789     2019-02-09UserDocs\n2 000812347     2018-03-08AnotherUserDocs\n```\n\nIn this cases, the software must look at the first `char` to determine the record type. This situation is usually what leads to a spaghetti string manipulation. We can solve it by using this library's \"advanced\" options:\n\n```kotlin\ndata class FirstRecordType(username: String, userMoney: BigDecimal)\ndata class SecondRecordType(userCode: Int, registerDate: LocalDate, docs: String)\n\nfixedLengthFileParser\u003cAny\u003e(fileInputStream) {\n    withRecord({ line -\u003e line[0] == '1' }) {\n        FirstRecordType(\n            field(2, 22, Padding.PaddingRight(' ')),\n            field(22, 28, Padding.PaddingLeft('0'))\n        )\n    }\n    \n    withRecord( { line -\u003e line[0] == '2' }) {\n        SecondRecordType(\n            field(2, 15, Padding.PaddingRight(' ')),\n            field(15, 25),\n            field(25, 40, Padding.PaddingRight(' '))\n        )\n    }\n}\n```\n\n## Features\n\n- The file is streamed into a sequence of values, and is never loaded in its entirety to the memory. You should expect this to have a good performance over a very big file.\n- The Kotlin DSL makes it easier to define the file parsing in a single point, and the sequence processing can be done anywhere\n\n## Changelog\n\nCheck the complete changelog [here](./CHANGELOG.md)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguiabolso%2Ffixed-length-file-handler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fguiabolso%2Ffixed-length-file-handler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguiabolso%2Ffixed-length-file-handler/lists"}