{"id":44954029,"url":"https://github.com/g0ddest/fixedlength","last_synced_at":"2026-02-18T11:16:48.706Z","repository":{"id":43379996,"uuid":"324994594","full_name":"g0ddest/fixedlength","owner":"g0ddest","description":"Fast simple zero-dependency Java library to parse fixed length files","archived":false,"fork":false,"pushed_at":"2026-02-09T19:48:33.000Z","size":158,"stargazers_count":22,"open_issues_count":1,"forks_count":8,"subscribers_count":5,"default_branch":"master","last_synced_at":"2026-02-09T22:35:57.669Z","etag":null,"topics":["fixed-length","fixed-length-fields","fixed-length-format","hacktoberfest","hacktoberfest-accepted","java","no-dependencies"],"latest_commit_sha":null,"homepage":"","language":"Java","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/g0ddest.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"g0ddest"}},"created_at":"2020-12-28T11:32:07.000Z","updated_at":"2026-02-09T19:48:36.000Z","dependencies_parsed_at":"2022-08-24T14:36:43.380Z","dependency_job_id":"c624b905-c575-4a89-a8c1-6e41cbb599d1","html_url":"https://github.com/g0ddest/fixedlength","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/g0ddest/fixedlength","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/g0ddest%2Ffixedlength","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/g0ddest%2Ffixedlength/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/g0ddest%2Ffixedlength/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/g0ddest%2Ffixedlength/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/g0ddest","download_url":"https://codeload.github.com/g0ddest/fixedlength/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/g0ddest%2Ffixedlength/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29577068,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-18T08:38:15.585Z","status":"ssl_error","status_checked_at":"2026-02-18T08:38:14.917Z","response_time":162,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["fixed-length","fixed-length-fields","fixed-length-format","hacktoberfest","hacktoberfest-accepted","java","no-dependencies"],"created_at":"2026-02-18T11:16:47.914Z","updated_at":"2026-02-18T11:16:48.700Z","avatar_url":"https://github.com/g0ddest.png","language":"Java","funding_links":["https://github.com/sponsors/g0ddest"],"categories":[],"sub_categories":[],"readme":" # Fixed Length handler for Java\n \n[![Maven Central](https://img.shields.io/maven-central/v/name.velikodniy.vitaliy/fixedlength)](https://search.maven.org/artifact/name.velikodniy.vitaliy/fixedlength)\n[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=g0ddest_fixedlength\u0026metric=coverage)](https://sonarcloud.io/summary/new_code?id=g0ddest_fixedlength)\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=g0ddest_fixedlength\u0026metric=alert_status)](https://sonarcloud.io/summary/new_code?id=g0ddest_fixedlength)\n[![javadoc](https://javadoc.io/badge2/name.velikodniy.vitaliy/fixedlength/javadoc.svg)](https://javadoc.io/doc/name.velikodniy.vitaliy/fixedlength)\n\nThis is fast simple zero-dependency library for Java 8+ that aims to parse fixed length (files with entities placed on fixed place in every line) files.\n\nLibrary was inspired by [Fixed Length File Handler](https://github.com/GuiaBolso/fixed-length-file-handler) and [fixedformat4j](https://github.com/jeyben/fixedformat4j).\n\nOne of its advantages is support mixed line types.\n\nIt works with `InputStream` so it is more memory efficient than store all file in memory. This is big \nadvantage when working with big files.  \n\n## Download\n\nThis library is published to Maven Central and to Github packages, so you'll need to configure that in your repositories:\n\nJust ensure that you have \n\n```groovy\nrepositories {\n    mavenCentral()\n}\n```\n\nor optionally if you want you can get the package from the Github packages\n\nGradle:\n```groovy\nrepositories {\n    mavenCentral()\n    maven {\n        url \"https://maven.pkg.github.com/g0ddest/fixedlength\"\n        credentials {\n             username = project.findProperty(\"gpr.user\") ?: System.getenv(\"USERNAME\")\n             password = project.findProperty(\"gpr.key\") ?: System.getenv(\"TOKEN\")\n        }\n    }\n}\n```\n(you need to add property with your username and github token, or put them into system envs).\n\nAnd then configure dependency:\n\nMaven:\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003ename.velikodniy.vitaliy\u003c/groupId\u003e\n  \u003cartifactId\u003efixedlength\u003c/artifactId\u003e\n  \u003cversion\u003e0.14\u003c/version\u003e\n  \u003ctype\u003epom\u003c/type\u003e\n\u003c/dependency\u003e\n```\n\nGradle:\n```groovy\nimplementation 'name.velikodniy.vitaliy:fixedlength:0.14'\n```\n\nIvy:\n```xml\n\u003cdependency org='name.velikodniy.vitaliy' name='fixedlength' rev='0.14'\u003e\n  \u003cartifact name='fixedlength' ext='pom' \u003e\u003c/artifact\u003e\n\u003c/dependency\u003e\n```\n\n## Usage\n### Basic usage\nFor example, you can transform this lines to 2 different kind of objects:\n\n```\nEmplJoe1      Smith     Developer 07500010012009\nCatSnowball  20200103\nEmplJoe3      Smith     Developer \n```\n\nIt's usual when processing data in some legacy systems.\n\nYou just need to write class with field structure and annotate each field that you want to connect with your file.\n\nTo parse this simple file\n\n```\nJoe1      Smith     \nJoe3      Smith     \n```\n\nyou need just write down this class (annotated fields also could be pulled from annotated classes):\n\n```java\npublic class Employee {\n    @FixedField(offset = 1, length = 10, align = Align.LEFT)\n    public String firstName;\n\n    @FixedField(offset = 10, length = 10, align = Align.LEFT)\n    public String lastName;\n}\n```\n\nand run parser:\n\n```java\nList\u003cObject\u003e parse = new FixedLength()\n    .registerLineType(Employee.class)\n    .parse(fileStream);\n```\n\n### Mixed line types\nIf there are few line types in your file and they starts with different string you can register different line types.\n\nTo do this you should add annotation to your class:\n\n```java\n@FixedLine(startsWith = \"Empl\")\n```\n\nSo you can parse this file:\n\n```\nEmplJoe1      Smith     \nCatSnowball  \nEmplJoe3      Smith     \n```\n\nwith these files:\n\n```java\n@FixedLine(startsWith = \"Empl\")\npublic class EmployeeMixed {\n\n    @FixedField(offset = 5, length = 10, align = Align.LEFT)\n    public String firstName;\n\n    @FixedField(offset = 15, length = 10, align = Align.LEFT)\n    public String lastName;\n}\n```\n\n(fields could be final as well).\n\n```java\n@FixedLine(startsWith = \"Cat\")\npublic class CatMixed {\n\n    @FixedField(offset = 4, length = 10, align = Align.LEFT)\n    public String name;\n\n    @FixedField(offset = 14, length = 8, format = \"yyyyMMdd\")\n    public LocalDate birthDate;\n\n}\n```\n\nand run parser like that:\n\n```java\nList\u003cObject\u003e parse = new FixedLength()\n    .registerLineType(EmployeeMixed.class)\n    .registerLineType(CatMixed.class)\n    .parse(fileStream);\n```\n\n### Custom formatters\nIf you need to use a custom class or type in parser you can add your own formatter like this:\n\n```java\npublic class StringFormatter extends Formatter\u003cString\u003e {\n    @Override\n    public String asObject(String string, FixedField field) {\n        return string;\n    }\n}\n```\n\nand register it with `registerFormatter` method on `FixedLength` instance.\n\n### Annotation parameters\nThere are all fields in `FixedField` annotation:\n* `offset` —  position on which this fields starts. Line starts with offset 1.\n* `length` — length of the field\n* `align` — on which side the content is justified. It works with padding.\n* `padding` — based on align trimming filler symbols. For example `\" 1\"` becomes `\"1\"`.\n* `format` — parameters that goes to formatter. For example, it can be date format.\n* `divide` — for number fields you can automatically divide the value on 10^n where n is value of this parameter.\n* `ignore` — the parser will ignore the field content if it matches the given regular expression. For example, `\"0{8}\"` will ignore `\"00000000\"`\n* `allowEmptyStrings` — the parser will keep empty strings instead of replacing them with `null`\n* `fallbackStringForNullValue` — when formatting an object back to a fixed length string, the formatter will replace a `null` value for this field with the given fallback string. If the fallback string is shorter than the field length, it will be padded according to the specified alignment and padding character.\n\n### Generics support\n\nYou can also use generics to cast parsed object to desired class.\nIt is more convenient if you have file with one entity type.\n\n```java\nList\u003cEmployee\u003e parse = new FixedLength\u003cEmployee\u003e()\n                .registerLineType(Employee.class);\n```\n\n### Ignoring errors\n\nIf there is errors on your line format there are two modes that you could skip these errors if you want to:\n\n* `skipErroneousLines` — line with error will not be added to result.\n* `skipErroneousFields` — fields with errors will be `null`.\n\nIn both cases warnings will be raised in logs.\n\nBy default, exception will be raised for entire process.\n\n### Splitting lines\n\nIn the case if you have 2 different records in one line and there is a split index you can add a method in your entity that should return index of the next record and mark it with annotation `SplitLineAfter`.\n\nFor example record\n\n```\nHEADERMy Title  26        EmplJoe1      Smith     Developer 07500010012009\n```\n\nNumber 26 indicates index of the next record.\n\nYou can describe it with entity:\n\n```java\n@FixedLine(startsWith = \"HEADER\")\npublic class HeaderSplit {\n    @FixedField(offset = 7, length = 10)\n    public String title;\n    @FixedField(offset = 17, length = 2)\n    public int headerLength;\n\n    @SplitLineAfter\n    public int getSplitIndex() {\n        return headerLength;\n    }\n}\n```\n\n### Custom rules for mixed lines\n\nThere is a `startsWith` parameter for easy-to-use identifying the class to deserialize, but sometimes it is not enough. So there is a `predicate` parameter in `FixedLine` annotation where you should pass your own custom rule as predicate. Just implement `Predicate\u003cString\u003e` and pass pointer to class in annotation.\n\n```java\n@FixedLine(predicate = EmployeePositionPredicate.class)\n```\n\nThis class will be initialized just once and cached. \n\n### Handling empty fields during formatting\n\nWhen formatting an object back to a fixed length string, you can control how empty (null) fields are handled using the `fallbackStringForNullValue` parameter in the `FixedField` annotation. If a field's value is `null`, the formatter will replace it with the specified fallback string. If the fallback string is shorter than the defined field length, it will be padded according to the specified alignment and padding character.\n\nLet's say we have a class defined as follows:\n```java\npublic class Employee {\n    @FixedField(offset = 1, length = 10, align = Align.LEFT)\n    public String firstName;\n\n    @FixedField(offset = 10, length = 10, align = Align.LEFT)\n    public String lastName;\n\n    @FixedField(offset = 20, length = 10, align = Align.LEFT)\n    public String role;\n\n    @FixedField(offset = 20, length = 10, align = Align.LEFT, ignore = \"0{8}\")\n    public LocalDate joinDate;\n}\n```\n\nWhen parsing the following lines, there will be two `null` values for the 2nd line: `lastName` and `joinDate`:\n```\nJoe1      Smith     Developer 12122009\nJoe3                Tester    00000000\n```\n\nHowever, when formatting the resulting object back to a fixed length string, the `null` values for these columns would not be included, leading to a string like this:\n```\nJoe3      Tester\n```\n\nAs this is most likely not what we want, we can specify a fallback string for each of the columns as follows:\n```java\npublic class Employee {\n    @FixedField(offset = 1, length = 10, align = Align.LEFT, fallbackStringForNullValue = \" \")\n    public String firstName;\n\n    @FixedField(offset = 10, length = 10, align = Align.LEFT, fallbackStringForNullValue = \" \")\n    public String lastName;\n\n    @FixedField(offset = 20, length = 10, align = Align.LEFT, fallbackStringForNullValue = \" \")\n    public String role;\n\n    @FixedField(offset = 20, length = 10, align = Align.LEFT, ignore = \"0{8}\", fallbackStringForNullValue = \"00000000\")\n    public LocalDate joinDate;\n}\n```\n\nWhen formatting the object back into a fixed length string, the resulting string will not look as expected (note how the fallback-strings will be auto-padded, if needed):\n```\nJoe3                Tester    00000000\n```\n\n## Java records support\n\nThere is an experimental support of Java 14+ records with no breaking of Java 8 support.\n\nJust annotate record's constructor as follows:\n\n```java\nrecord Employee (\n    @FixedField(offset = 1, length = 10, align = Align.LEFT)\n    String firstName,\n\n    @FixedField(offset = 10, length = 10, align = Align.LEFT)\n    String lastName\n){}\n```\n\nand it works the same way as annotated class.\n\n## Benchmark\n\nThere is a benchmark, you can run it with `gradle jmh` command. Also, you can change running parameters of it in file `src/jmh/java/name/velikodniy/vitaliy/fixedlength/benchmark/BenchmarkRunner.java`. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fg0ddest%2Ffixedlength","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fg0ddest%2Ffixedlength","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fg0ddest%2Ffixedlength/lists"}