{"id":40544584,"url":"https://github.com/tinyhubs/properties","last_synced_at":"2026-01-20T23:35:23.234Z","repository":{"id":57483226,"uuid":"87710716","full_name":"tinyhubs/properties","owner":"tinyhubs","description":"properties file reader \u0026 writer for go","archived":false,"fork":false,"pushed_at":"2017-06-17T12:28:28.000Z","size":28,"stargazers_count":8,"open_issues_count":0,"forks_count":5,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-01-14T15:41:53.923Z","etag":null,"topics":["java","properties","read","write"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tinyhubs.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2017-04-09T13:32:19.000Z","updated_at":"2022-05-11T09:05:53.000Z","dependencies_parsed_at":"2022-08-28T09:11:24.142Z","dependency_job_id":null,"html_url":"https://github.com/tinyhubs/properties","commit_stats":null,"previous_names":["libbylg/properties"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tinyhubs/properties","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tinyhubs%2Fproperties","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tinyhubs%2Fproperties/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tinyhubs%2Fproperties/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tinyhubs%2Fproperties/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tinyhubs","download_url":"https://codeload.github.com/tinyhubs/properties/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tinyhubs%2Fproperties/sbom","scorecard":{"id":887534,"data":{"date":"2025-08-11","repo":{"name":"github.com/tinyhubs/properties","commit":"483af3ecca52e90bd4dbcef6bc6c325de5d3bce0"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: BSD 3-Clause \"New\" or \"Revised\" License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-24T10:33:38.877Z","repository_id":57483226,"created_at":"2025-08-24T10:33:38.877Z","updated_at":"2025-08-24T10:33:38.877Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28618803,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T22:24:05.405Z","status":"ssl_error","status_checked_at":"2026-01-20T22:20:31.342Z","response_time":117,"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":["java","properties","read","write"],"created_at":"2026-01-20T23:35:22.583Z","updated_at":"2026-01-20T23:35:23.229Z","avatar_url":"https://github.com/tinyhubs.png","language":"Go","readme":"# Overview\n\n[![Build Status](https://travis-ci.org/tinyhubs/properties.svg?branch=master)](https://travis-ci.org/tinyhubs/properties)\n[![GoDoc](https://godoc.org/github.com/tinyhubs/properties?status.svg)](https://godoc.org/github.com/tinyhubs/properties)\n[![Language](https://img.shields.io/badge/language-go-lightgrey.svg)](https://github.com/tinyhubs/properties)\n[![License](https://img.shields.io/badge/license-New%20BSD-yellow.svg?style=flat)](LICENSE)\n[![codecov](https://codecov.io/gh/tinyhubs/properties/branch/master/graph/badge.svg)](https://codecov.io/gh/tinyhubs/properties)\n[![goreport](https://www.goreportcard.com/badge/github.com/tinyhubs/properties)](https://www.goreportcard.com/report/github.com/tinyhubs/properties)\n\n`*.properties`文件是java里面很常见的配置文件。这里是一个go语言版的*.properties文件读处理库。本库支持properties文件的读取、修改、回写操作。也支持向properties文件中的属性追加、删除注释操作。\n\n## go-properties文件格式定义\n\n为了使得properties文件的识别更加简单快速，go的properties的文件格式和java的properties文件并不是等价的。它将java里面一些很少用到的格式特性都去掉了。\n\ngolang版本的properties文件的格式定义如下：\n\n- 一行如果第一个非空白字符是`#`或者`!`，那么整行将被识别为注释行，注释行将被解析器忽略。\n\n    比如，下面三行都会被解析器忽略(第三行是空白行)：\n    ```\n    #  这是注释行\n    !  这也是注释行\n        \n    ```\n    \n- 每个配置项都是单行的key-value对，**不支持跨行**，key和value以`=`或者`:`分隔。\n\n    比如，下面其实是三个配置项----`UserName=Fabirc \\`、`Boch=`、`Contry=US`：\n    ```\n    UserName = Fabirc \\\n    Boch\n    Contry=US\n    ```\n    \n- key和value都是区分大小写；\n\n    比如，下面其实是三个不同的配置项：\n    ```\n    SizeRange=1-20\n    sizerange=1-20\n    SIZERANGE=1-20\n    ```\n    \n- 一行的第一个`=`即使key与value的分隔字符，所以key中不会出现`=`，但value部分可以出现`=`；\n\n    比如，下面这个第一个行的key为`expr`,value是`A-B=C`；而第二行的key是一个`\"\"`(空字符串)，value是`Hello`。当然第二种情况并没有实际意义:\n    ```\n    expr=A-B=C\n    =Hello\n    ```\n    \n    \n- key和value的前后的空白都将被忽略，但key和value中间的空白会被原样保留；\n\n    比如，下面这三个配置项的值都是`1-20`：\n    ```\n      SizeRange-1=1-20\n    SizeRange-2 =  1-20   \n       SizeRange-3  =  1-20  \n    ```\n- properties文件只支持**`UTF-8`**字符集，所以value中可以直接输入中文，遇到中文字符不必像java那样使用`\\uxxxx`转义，直接用中文字面文字即可；\n\n    比如，下面这个配置项的key为`地址`，value为`深圳`。\n    ```\n      地址=深圳\n    ```\n    \n- 当value为空时，等号可忽略；\n\n    所以，下面三个配置项的值都是空字符串(第一个等号后面有个)：\n    ```\n    Address-1=  \n    Address-2=\n    Address-3\n    ```\n\n## 接口定义\n\n#### 属性文档\n\n一个properties文档由一个`properties.PropertiesDocument`对象来表示。一个properties文档由多个key-value形式的属性组成。每个属性还可以追加一行或者多行注释。\n\n#### 加载属性文档\n\n`properties.Load` 从io流生成一个`properties.PropertiesDocument`对象。\n\n```go\nfile, err := os.Open(\"test1.properties\")\nif nil != err {\n    return\n}\n\ndoc, err := properties.Load(file)\nif nil != err {\n    t.Error(\"加载失败\")\n    return\n}\n\nfmt.Println(doc.String(\"key\"))\n```\n\n\n#### 创建一个新的属性文档对象\n\n`properties.New` 直接创建一个新的属性文档对象,常用于属性创建文档文件的场景下。我们随后可以通过`properties.Save`函数将属性文档写入到文件或者输出流。\n\n```go\ndoc := properties.New()\ndoc.Set(\"a\", \"aaa\")\ndoc.Comment(\"a\", \"This is a comment for a\")\n\nbuf := bytes.NewBufferString(\"\")\nproperties.Save(doc, buf)\n```\n\n\n#### 将属性回写到文件或者流\n\n`properties.Save` 可用于将一个文档回写到指定的writer中去。 \n\n```go\nbuf := bytes.NewBufferString(\"\")\nproperties.Save(doc, buf)\n```\n\n#### 属性值的读取\n\n- **通用读取能力**\n\nPropertiesDocument对象的`Get`方法提供了一个基本的元素读取能力：\n\n```go\nfunc (p PropertiesDocument) Get(key string) (value string, exist bool)\n```\n\n`Get`函数会返回两个参数，当对应的key在PropertiesDocument文档中存在时，会返回该key对应的value，且exist的值将为true；如果不存在，exist的值将是false。\n\n我们经常利用`Get`来探测，某个指定key的属性是否在属性文件中定义了。\n\n- **读取并转换**\n读取属性然后转成对应的数据类型是个很常见的任务，所以PropertiesDocument为最常用的几种类型提供了方便的读取并转换的函数。  \n  * `String()` 读取一个字符串型的属性，如果不存在默认返回`\"\"`\n  * `Int()` 读取一个属性并转换为`int64`类型，如果key对应的属性不存在，或者转换失败，返回值为0\n  * `Uint()` 和`Int()`函数类似，只是返回的数据类型为uint64\n  * `Float()` 也是和`Int()`函数类似，但返回值为float64\n  * `Bool()` 同与`String`类似，只是返回值是`bool`类型的且缺省值是`false`。`Bool`函数会将`1`, `t`, `T`, `true`, `TRUE`, `True`识别为`true`，将`0`, `f`, `F`, `false`, `FALSE`, `False`识别为`false`。\n  * `Object` 这个函数提供了一个数据映射能力，可以将找到的value映射为任何类型。\n\n- **指定读取的缺省值**\n前面的`String()`、`Int()`等函数在key不存在或者抓换失败的场景下，默认会返回零值。但零值往往不能满足我们的诉求，我们经常需要自己指定这些场景下的返回值。`StringDefault`，`IntDefault`、`FloatDefault`、`BoolDefault`、`ObjectDefault` 这几个函数的返回值和前面不带`Default`后缀的函数的行为类似，只是当配置项不存在时或者数据格式错误时，会直接返回参数中的`def`(缺省值)。\n\n\n#### 属性的增删改\n\n- **增加或者修改属性**\n\n`Set()`函数用于修改指定的key的属性的值。如果指定的key的属性不存在，那么自动创建一个。\n\n本库并没有提供按类型设置属性值的功能，前面描述的`Set()`函数只接受字符串类型的属性值作为输入。主要原因是数据的转换方式非常多，没有一种普适 的数据转换方法。所以，对于非字符串类型的值的设置需要自行转换成string类型。\n\n```go\ndoc.Set(\"key\", \"value\")\n```\n\n- **处理属性不存在的场景**\n\n当属性不存在时，`Set()`函数会新建一个属性值，这种工作方式通常是很有用的。但是，有时候我们不希望`Set()`的这种自作聪明的行为。此时，我们可以通过`Get()`方法判断一些以确定是否需调用`Set()`。\n\n```go\n_, exist := doc.Get(\"key\")\nif !exist {\n    return errors.New(\"Key is not exist\")\n}\n\ndoc.Set(\"key\", \"New-Value\")\n```\n\n- **删除属性**\n\n`Del()`函数用于删除指定key的属性。它会返回一个bool值，用于表示当前的key的属性是否存在。\n\n```go\nexist := doc.Del(\"key\")\n```\n\n\n#### 操作注释\n\n在本库中，注释是绑定到属性的。位于属性的key-value定义前面，且与属性之间没有空白行的多行注释，我们会判定这些注释是属于该属性的，比如：\n```properties\n # Comment1\n # Comment2\n \n # Comment3\n # Comment4\n mykey=myvalue\n```\n\n上面的Comment3和Comment4是mykey属性的注释，但是Comment1和Comment2却不是。\n\nPropertiesDocument的`Comment()`函数用于为属性指定一些注释。而`Uncomment()`函数用于删除指定的key的注释。\n\nPropertiesDocument的`Comment()`函数允许一次性指定多行注释，而`Uncomment()`用于一次性删除一个指定的key的所有的注释。\n\n\n#### 文档对象枚举\n\nPropertiesDocument的`Accept()`和`Foreach()`函数都是用来对文档对象进行枚举的，但是`Foreach()`专用于对属性进行遍历。而`Accept()`可以通过对属性和注释进行遍历。\n\n实际上，`Save()`函数就是利用`Accept()`函数来实现的：\n\n```go\nfunc Save(doc *PropertiesDocument, writer io.Writer) {\n    doc.Accept(func(typo byte, value string, key string) bool {\n        switch typo {\n        case '#', '!', ' ':\n            {\n                fmt.Fprintln(writer, value)\n            }\n        case '=', ':':\n            {\n                fmt.Fprintf(writer, \"%s%c%s\\n\", key, typo, value)\n            }\n        }\n        return true\n    })\n}\n```\n\n`Accept()`函数的回调函数里面有个`typo`参数，这个参数决定了当前这条记录是注释还是一个有效的属性。`typo`的取值可能有下面一些：\n- `'#'` 表示当前的value是`#`开头的注释\n- `'!'` 表示当前的value是`!`开头的注释\n- `' '` 表示当前的value是个空行或者空白行\n- `'='` 表示当前的value是个以`=`分隔的属性\n- `':'` 表示当前的value是个以`:`分隔的属性\n\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftinyhubs%2Fproperties","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftinyhubs%2Fproperties","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftinyhubs%2Fproperties/lists"}