1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-02-04 12:52:28 +08:00

Compare commits

...

27 Commits

Author SHA1 Message Date
dudaodong
0f9683a764 release v1.4.5 2024-12-04 10:42:55 +08:00
dudaodong
4595a94b4c update doc 2024-12-04 10:40:18 +08:00
dudaodong
e1e15883e9 fix: MaxInt was added only in 1.17 use instead 2024-11-27 15:09:23 +08:00
dudaodong
b4e7977feb release v1.4.4 2024-10-09 15:16:22 +08:00
dudaodong
1d3585b22f fix: fix bug of Comma, issue #248 2024-09-29 11:52:01 +08:00
dudaodong
2145808268 feat: add new functions in datetime and strutils 2024-09-09 14:33:15 +08:00
dudaodong
cb7ff904d9 doc: update doc 2024-09-04 10:21:46 +08:00
dudaodong
87c8799e9e doc: update doc 2024-09-04 10:20:34 +08:00
dudaodong
c9a8c70ee6 feat: merge some new functions from v2 branch 2024-09-04 10:13:36 +08:00
dudaodong
ab2e5a3137 feat: add new functions 2024-09-03 20:25:06 +08:00
Chao Cai
b942b7a074 fix:convertor struct to map by struct ptr (#221)
Co-authored-by: emrysechobygo <emrysechobygo@hotmail.com>
2024-05-25 20:05:38 +08:00
dudaodong
d910af24e0 release v1.4.3 2024-03-05 14:53:54 +08:00
dudaodong
5fc21330da feat: add new functions to mathutil package 2024-03-05 10:41:07 +08:00
dudaodong
928e3a390d feat: merge some new functions from rc branch 2024-03-04 16:36:20 +08:00
dudaodong
48519aba2b feat: add MapToStruct 2024-02-23 10:29:46 +08:00
dudaodong
97ea636e9e feat: add WriteMapsToCsv 2024-02-06 17:17:17 +08:00
dudaodong
f82f49a4c2 feat: add SubInBetween 2024-02-06 17:13:00 +08:00
dudaodong
0fc8733915 feat: add SubInBetween 2024-02-06 17:09:36 +08:00
dudaodong
44022c5861 refactor: refact Comma function 2024-02-06 17:09:08 +08:00
dudaodong
ec0222c5b1 refactor: refact Comma function 2024-02-06 17:06:06 +08:00
dudaodong
76b82a2fa1 release v1.4.2 2023-10-07 11:25:36 +08:00
dudaodong
d9dc2993b3 update readme file 2023-10-07 11:25:00 +08:00
dudaodong
ae42a9fdff Merge branch 'v1' of github.com:duke-git/lancet into v1 2023-09-24 19:19:30 +08:00
dudaodong
f37e55d9f1 feat: add ReadFile 2023-09-24 19:19:09 +08:00
dudaodong
d920a51988 add tempdir 2023-09-18 10:08:19 +08:00
dudaodong
561c42a24e feat: add rsa encrypt 2023-09-17 16:21:40 +08:00
dudaodong
12d74489d5 update doc 2023-09-17 16:12:12 +08:00
51 changed files with 5913 additions and 527 deletions

5
.gitignore vendored
View File

@@ -5,6 +5,9 @@ cryptor/*.txt
fileutil/*.txt
fileutil/*.zip
fileutil/*.link
fileutil/tempdir
fileutil/unzip/*
slice/testdata/*
cryptor/*.pem
cryptor/*.pem
docs/node_modules
docs/.vitepress

View File

@@ -4,7 +4,7 @@
<br/>
![Go version](https://img.shields.io/badge/go-v1.16-9cf)
[![Release](https://img.shields.io/badge/release-1.4.1-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-1.4.5-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com//duke-git/lancet?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet)](https://goreportcard.com/report/github.com/duke-git/lancet)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
@@ -96,6 +96,7 @@ import "github.com/duke-git/lancet/convertor"
- [ToJson](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ToJson)
- [ToString](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ToString)
- [StructToMap](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#StructToMap)
- [MapToStruct](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#MapToStruct)
- [EncodeByte](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#EncodeByte)
- [DecodeByte](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#DecodeByte)
- [DeepClone](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#DeepClone)
@@ -103,6 +104,11 @@ import "github.com/duke-git/lancet/convertor"
- [ToInterface](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ToInterface)
- [Utf8ToGbk](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#Utf8ToGbk)
- [GbkToUtf8](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#GbkToUtf8)
- [ToStdBase64](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ToStdBase64)
- [ToUrlBase64](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ToUrlBase64)
- [ToRawStdBase64](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ToRawStdBase64)
- [ToRawUrlBase64](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ToRawUrlBase64)
- [ToBigInt](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ToBigInt)
### 3. Cryptor package is for data encryption and decryption.
@@ -154,7 +160,9 @@ import "github.com/duke-git/lancet/cryptor"
- [GenerateRsaKey](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#GenerateRsaKey)
- [RsaEncrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#RsaEncrypt)
- [RsaDecrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#RsaDecrypt)
- [GenerateRsaKeyPair](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#GenerateRsaKeyPair)
- [RsaEncryptOAEP](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#RsaEncryptOAEP)
- [RsaDecryptOAEP](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#RsaDecryptOAEP)
### 4. Datetime package supports date and time format and compare.
```go
@@ -203,6 +211,9 @@ import "github.com/duke-git/lancet/datetime"
- [TimestampMilli](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#TimestampMilli)
- [TimestampMicro](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#TimestampMicro)
- [TimestampNano](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#TimestampNano)
- [TrackFuncTime](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#TrackFuncTime)
- [DaysBetween](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#DaysBetween)
- [GenerateDatetimesBetween](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#GenerateDatetimesBetween)
### 5. Fileutil package implements some basic functions for file operations.
@@ -235,8 +246,10 @@ import "github.com/duke-git/lancet/fileutil"
- [Sha](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#Sha)
- [ReadCsvFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#ReadCsvFile)
- [WriteCsvFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#WriteCsvFile)
- [WriteMapsToCsv](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#WriteMapsToCsv)
- [WriteStringToFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#WriteStringToFile)
- [WriteBytesToFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#WriteBytesToFile)
- [ReadFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#ReadFile)
### 6. Formatter contains some functions for data formatting.
@@ -267,6 +280,8 @@ import "github.com/duke-git/lancet/function"
- [Curry](https://github.com/duke-git/lancet/blob/v1/docs/function.md#Curry)
- [Compose](https://github.com/duke-git/lancet/blob/v1/docs/function.md#Compose)
- [Debounced](https://github.com/duke-git/lancet/blob/v1/docs/function.md#Debounced)
- [Debounce](https://github.com/duke-git/lancet/blob/v1/docs/function.md#Debounce)
- [Throttle](https://github.com/duke-git/lancet/blob/v1/docs/function.md#Throttle)
- [Delay](https://github.com/duke-git/lancet/blob/v1/docs/function.md#Delay)
- [Pipeline](https://github.com/duke-git/lancet/blob/v1/docs/function.md#Pipeline)
- [Schedule](https://github.com/duke-git/lancet/blob/v1/docs/function.md#Schedule)
@@ -287,6 +302,10 @@ import "github.com/duke-git/lancet/mathutil"
- [RoundToFloat](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#RoundToFloat)
- [RoundToString](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#RoundToString)
- [TruncRound](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#TruncRound)
- [CeilToFloat](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#CeilToFloat)
- [CeilToString](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#CeilToString)
- [FloorToFloat](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#FloorToFloat)
- [FloorToString](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#FloorToString)
- [AngleToRadian](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#AngleToRadian)
- [RadianToAngle](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#RadianToAngle)
- [PointDistance](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#PointDistance)
@@ -333,9 +352,15 @@ import "github.com/duke-git/lancet/random"
#### Function list:
- [RandBool](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandBool)
- [RandBoolSlice](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandBoolSlice)
- [RandBytes](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandBytes)
- [RandInt](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandInt)
- [RandIntSlice](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandIntSlice)
- [RandString](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandString)
- [RandStringSlice](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandStringSlice)
- [RandFloat](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandFloat)
- [RandFloats](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandFloats)
- [RandUpper](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandUpper)
- [RandLower](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandLower)
- [RandNumeral](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandNumeral)
@@ -451,6 +476,16 @@ import "github.com/duke-git/lancet/strutil"
- [SplitAndTrim](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#SplitAndTrim)
- [HideString](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#HideString)
- [RemoveWhiteSpace](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#RemoveWhiteSpace)
- [SubInBetween](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#SubInBetween)
- [HammingDistance](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#HammingDistance)
- [Concat](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#Concat)
- [Ellipsis](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#Ellipsis)
- [Shuffle](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#Shuffle)
- [Rotate](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#Rotate)
- [TemplateReplace](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#TemplateReplace)
- [RegexMatchAllGroups](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#RegexMatchAllGroups)
### 14. System package contain some functions about os, runtime, shell command.
```go
@@ -510,6 +545,16 @@ import "github.com/duke-git/lancet/validator"
- [IsGBK](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsGBK)
- [IsASCII](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsASCII)
- [IsPrintable](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsPrintable)
- [IsBin](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsBin)
- [IsHex](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsHex)
- [IsBase64URL](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsBase64URL)
- [IsJWT](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsJWT)
- [IsVisa](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsVisa)
- [IsMasterCard](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsMasterCard)
- [IsAmericanExpress](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsAmericanExpress)
- [IsUnionPay](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsUnionPay)
- [IsChinaUnionPay](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsChinaUnionPay)
## How to Contribute

View File

@@ -4,7 +4,7 @@
<br/>
![Go version](https://img.shields.io/badge/go-v1.16-9cf)
[![Release](https://img.shields.io/badge/release-1.4.1-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-1.4.5-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com//duke-git/lancet?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet)](https://goreportcard.com/report/github.com/duke-git/lancet)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
@@ -95,6 +95,7 @@ import "github.com/duke-git/lancet/convertor"
- [ToJson](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ToJson)
- [ToString](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ToString)
- [StructToMap](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#StructToMap)
- [MapToStruct](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#MapToStruct)
- [EncodeByte](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#EncodeByte)
- [DecodeByte](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#DecodeByte)
- [DeepClone](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#DeepClone)
@@ -102,6 +103,13 @@ import "github.com/duke-git/lancet/convertor"
- [ToInterface](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ToInterface)
- [Utf8ToGbk](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#Utf8ToGbk)
- [GbkToUtf8](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#GbkToUtf8)
- [ToStdBase64](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ToStdBase64)
- [ToUrlBase64](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ToUrlBase64)
- [ToRawStdBase64](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ToRawStdBase64)
- [ToRawUrlBase64](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ToRawUrlBase64)
- [ToBigInt](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ToBigInt)
### 3. cryptor 加密包支持数据加密和解密,获取 md5hash 值。支持 base64, md5, hmac, aes, des, rsa。
```go
@@ -153,6 +161,10 @@ md#HmacSha256WithBase64)
- [GenerateRsaKey](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#GenerateRsaKey)
- [RsaEncrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#RsaEncrypt)
- [RsaDecrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#RsaDecrypt)
- [GenerateRsaKeyPair](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#GenerateRsaKeyPair)
- [RsaEncryptOAEP](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#RsaEncryptOAEP)
- [RsaDecryptOAEP](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#RsaDecryptOAEP)
### 4. datetime 日期时间处理包,格式化日期,比较日期。
@@ -202,6 +214,9 @@ import "github.com/duke-git/lancet/datetime"
- [TimestampMilli](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#TimestampMilli)
- [TimestampMicro](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#TimestampMicro)
- [TimestampNano](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#TimestampNano)
- [TrackFuncTime](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#TrackFuncTime)
- [DaysBetween](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#DaysBetween)
- [GenerateDatetimesBetween](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#GenerateDatetimesBetween)
### 5. fileutil 包支持文件基本操作。
@@ -234,8 +249,10 @@ import "github.com/duke-git/lancet/fileutil"
- [Sha](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#Sha)
- [ReadCsvFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#ReadCsvFile)
- [WriteCsvFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#WriteCsvFile)
- [WriteMapsToCsv](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#WriteMapsToCsv)
- [WriteStringToFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#WriteStringToFile)
- [WriteBytesToFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#WriteBytesToFile)
- [ReadFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#ReadFile)
### 6. formatter 格式化器包含一些数据格式化处理方法。
@@ -266,6 +283,8 @@ import "github.com/duke-git/lancet/function"
- [Curry](https://github.com/duke-git/lancet/blob/v1/docs/function_zh-CN.md#Curry)
- [Compose](https://github.com/duke-git/lancet/blob/v1/docs/function_zh-CN.md#Compose)
- [Debounced](https://github.com/duke-git/lancet/blob/v1/docs/function_zh-CN.md#Debounced)
- [Debounce](https://github.com/duke-git/lancet/blob/v1/docs/function_zh-CN.md#Debounce)
- [Throttle](https://github.com/duke-git/lancet/blob/v1/docs/function_zh-CN.md#Throttle)
- [Delay](https://github.com/duke-git/lancet/blob/v1/docs/function_zh-CN.md#Delay)
- [Pipeline](https://github.com/duke-git/lancet/blob/v1/docs/function_zh-CN.md#Pipeline)
- [Schedule](https://github.com/duke-git/lancet/blob/v1/docs/function_zh-CN.md#Schedule)
@@ -286,6 +305,10 @@ import "github.com/duke-git/lancet/mathutil"
- [RoundToFloat](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#RoundToFloat)
- [RoundToString](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#RoundToString)
- [TruncRound](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#TruncRound)
- [CeilToFloat](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#CeilToFloat)
- [CeilToString](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#CeilToString)
- [FloorToFloat](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#FloorToFloat)
- [FloorToString](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#FloorToString)
- [AngleToRadian](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#AngleToRadian)
- [RadianToAngle](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#RadianToAngle)
- [PointDistance](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#PointDistance)
@@ -331,9 +354,15 @@ import "github.com/duke-git/lancet/random"
#### 函数列表:
- [RandBool](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandBool)
- [RandBoolSlice](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandBoolSlice)
- [RandBytes](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandBytes)
- [RandInt](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandInt)
- [RandIntSlice](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandIntSlice)
- [RandString](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandString)
- [RandStringSlice](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandStringSlice)
- [RandFloat](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandFloat)
- [RandFloats](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandFloats)
- [RandUpper](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandUpper)
- [RandLower](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandLower)
- [RandNumeral](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandNumeral)
@@ -449,6 +478,14 @@ import "github.com/duke-git/lancet/strutil"
- [SplitAndTrim](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#SplitAndTrim)
- [HideString](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#HideString)
- [RemoveWhiteSpace](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#RemoveWhiteSpace)
- [SubInBetween](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#SubInBetween)
- [HammingDistance](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#HammingDistance)
- [Concat](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#Concat)
- [Ellipsis](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#Ellipsis)
- [Shuffle](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#Shuffle)
- [Rotate](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#Rotate)
- [TemplateReplace](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#TemplateReplace)
- [RegexMatchAllGroups](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#RegexMatchAllGroups)
### 14. system 包含 os, runtime, shell command 相关函数。
@@ -509,6 +546,15 @@ import "github.com/duke-git/lancet/validator"
- [IsGBK](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsGBK)
- [IsASCII](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsASCII)
- [IsPrintable](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsPrintable)
- [IsBin](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsBin)
- [IsHex](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsHex)
- [IsBase64URL](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsBase64URL)
- [IsJWT](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsJWT)
- [IsVisa](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsVisa)
- [IsMasterCard](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsMasterCard)
- [IsAmericanExpress](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsAmericanExpress)
- [IsUnionPay](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsUnionPay)
- [IsChinaUnionPay](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsChinaUnionPay)
## 如何贡献代码

View File

@@ -3,6 +3,7 @@ package compare
import (
"bytes"
"encoding/json"
"math/big"
"reflect"
"time"
@@ -155,169 +156,129 @@ func compareBasicValue(operator string, leftValue, rightValue interface{}) bool
}
switch leftVal := leftValue.(type) {
case json.Number:
if left, err := leftVal.Float64(); err == nil {
switch rightVal := rightValue.(type) {
case json.Number:
if right, err := rightVal.Float64(); err == nil {
switch operator {
case equal:
if left == right {
return true
}
case lessThan:
if left < right {
return true
}
case greaterThan:
if left > right {
return true
}
case lessOrEqual:
if left <= right {
return true
}
case greaterOrEqual:
if left >= right {
return true
}
}
}
case float32, float64, int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
right, err := convertor.ToFloat(rightValue)
if err != nil {
return false
}
switch operator {
case equal:
if left == right {
return true
}
case lessThan:
if left < right {
return true
}
case greaterThan:
if left > right {
return true
}
case lessOrEqual:
if left <= right {
return true
}
case greaterOrEqual:
if left >= right {
return true
}
}
}
case int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
left, err := convertor.ToBigInt(leftValue)
if err != nil {
return false
}
case float32, float64, int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
right, err := convertor.ToBigInt(rightValue)
if err != nil {
return false
}
return compareBigInt(operator, left, right)
case float32, float64:
left, err := convertor.ToFloat(leftValue)
if err != nil {
return false
}
switch rightVal := rightValue.(type) {
case json.Number:
if right, err := rightVal.Float64(); err == nil {
switch operator {
case equal:
if left == right {
return true
}
case lessThan:
if left < right {
return true
}
case greaterThan:
if left > right {
return true
}
case lessOrEqual:
if left <= right {
return true
}
case greaterOrEqual:
if left >= right {
return true
}
}
}
case float32, float64, int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
right, err := convertor.ToFloat(rightValue)
if err != nil {
return false
}
switch operator {
case equal:
if left == right {
return true
}
case lessThan:
if left < right {
return true
}
case greaterThan:
if left > right {
return true
}
case lessOrEqual:
if left <= right {
return true
}
case greaterOrEqual:
if left >= right {
return true
}
}
right, err := convertor.ToFloat(rightValue)
if err != nil {
return false
}
return compareFloats(operator, left, right)
case string:
left := leftVal
switch right := rightValue.(type) {
case string:
switch operator {
case equal:
if left == right {
return true
}
case lessThan:
if left < right {
return true
}
case greaterThan:
if left > right {
return true
}
case lessOrEqual:
if left <= right {
return true
}
case greaterOrEqual:
if left >= right {
return true
}
}
return compareStrings(operator, left, right)
}
case bool:
left := leftVal
switch right := rightValue.(type) {
case bool:
switch operator {
case equal:
if left == right {
return true
}
}
return compareBools(operator, left, right)
}
case json.Number:
if left, err := leftVal.Float64(); err == nil {
switch rightVal := rightValue.(type) {
case json.Number:
if right, err := rightVal.Float64(); err == nil {
return compareFloats(operator, left, right)
}
case float32, float64:
right, err := convertor.ToFloat(rightValue)
if err != nil {
return false
}
return compareFloats(operator, left, right)
case int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
right, err := convertor.ToBigInt(rightValue)
if err != nil {
return false
}
left, err := convertor.ToBigInt(left)
return compareBigInt(operator, left, right)
}
}
}
return false
}
// compareBigInt compares two big.Int values based on the operator
func compareBigInt(operator string, left, right *big.Int) bool {
switch operator {
case equal:
return left.Cmp(right) == 0
case lessThan:
return left.Cmp(right) < 0
case greaterThan:
return left.Cmp(right) > 0
case lessOrEqual:
return left.Cmp(right) <= 0
case greaterOrEqual:
return left.Cmp(right) >= 0
}
return false
}
// compareFloats compares two float64 values based on the operator
func compareFloats(operator string, left, right float64) bool {
switch operator {
case equal:
return left == right
case lessThan:
return left < right
case greaterThan:
return left > right
case lessOrEqual:
return left <= right
case greaterOrEqual:
return left >= right
}
return false
}
// compareStrings compares two string values based on the operator
func compareStrings(operator string, left, right string) bool {
switch operator {
case equal:
return left == right
case lessThan:
return left < right
case greaterThan:
return left > right
case lessOrEqual:
return left <= right
case greaterOrEqual:
return left >= right
}
return false
}
// compareBools compares two boolean values based on the operator
func compareBools(operator string, left, right bool) bool {
switch operator {
case equal:
return left == right
}
return false
}

View File

@@ -1,6 +1,7 @@
package compare
import (
"encoding/json"
"testing"
"time"
@@ -70,18 +71,28 @@ func TestEqualValue(t *testing.T) {
}
func TestLessThan(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestLessThan")
assert.Equal(true, LessThan(1, 2))
assert.Equal(true, LessThan(1.1, 2.2))
assert.Equal(true, LessThan("a", "b"))
tests := []struct {
left interface{}
right interface{}
want bool
}{
{1, 2, true},
{1.1, 2.2, true},
{"a", "b", true},
{time.Now(), time.Now().Add(time.Second), true},
{[]byte("hello1"), []byte("hello2"), true},
{json.Number("123"), json.Number("124"), true},
{645680099112988673, 645680099112988675, true},
{1, 1, false},
{1, int64(1), false},
}
time1 := time.Now()
time2 := time1.Add(time.Second)
assert.Equal(true, LessThan(time1, time2))
assert.Equal(false, LessThan(1, 1))
assert.Equal(false, LessThan(1, int64(1)))
for _, tt := range tests {
assert.Equal(tt.want, LessThan(tt.left, tt.right))
}
}
func TestGreaterThan(t *testing.T) {

View File

@@ -6,6 +6,7 @@ package convertor
import (
"bytes"
"encoding/base64"
"encoding/binary"
"encoding/gob"
"encoding/json"
@@ -13,6 +14,7 @@ import (
"fmt"
"io"
"math"
"math/big"
"reflect"
"regexp"
"strconv"
@@ -196,6 +198,7 @@ func StructToMap(value interface{}) (map[string]interface{}, error) {
if t.Kind() == reflect.Ptr {
t = t.Elem()
v = v.Elem()
}
if t.Kind() != reflect.Struct {
return nil, fmt.Errorf("data type %T not support, shuld be struct or pointer to struct", value)
@@ -294,48 +297,6 @@ func DeepClone(src interface{}) interface{} {
return result.Interface()
}
// // CopyProperties copies each field from the source into the destination. It recursively copies struct pointers and interfaces that contain struct pointers.
// func CopyProperties(dst, src interface{}) (err error) {
// defer func() {
// if e := recover(); e != nil {
// err = errors.New(fmt.Sprintf("%v", e))
// }
// }()
// dstType, dstValue := reflect.TypeOf(dst), reflect.ValueOf(dst)
// srcType, srcValue := reflect.TypeOf(src), reflect.ValueOf(src)
// if dstType.Kind() != reflect.Ptr || dstType.Elem().Kind() != reflect.Struct {
// return errors.New("CopyProperties: param dst should be struct pointer")
// }
// if srcType.Kind() == reflect.Ptr {
// srcType, srcValue = srcType.Elem(), srcValue.Elem()
// }
// if srcType.Kind() != reflect.Struct {
// return errors.New("CopyProperties: param src should be a struct or struct pointer")
// }
// dstType, dstValue = dstType.Elem(), dstValue.Elem()
// propertyNums := dstType.NumField()
// for i := 0; i < propertyNums; i++ {
// property := dstType.Field(i)
// propertyValue := srcValue.FieldByName(property.Name)
// if !propertyValue.IsValid() || property.Type != propertyValue.Type() {
// continue
// }
// if dstValue.Field(i).CanSet() {
// dstValue.Field(i).Set(propertyValue)
// }
// }
// return nil
// }
// CopyProperties copies each field from the source into the destination. It recursively copies struct pointers and interfaces that contain struct pointers.
// use json.Marshal/Unmarshal, so json tag should be set for fields of dst and src struct.
func CopyProperties(dst, src interface{}) error {
@@ -404,3 +365,132 @@ func GbkToUtf8(bs []byte) ([]byte, error) {
b, err := io.ReadAll(r)
return b, err
}
// MapToStruct converts map to struct
func MapToStruct(m map[string]interface{}, structObj interface{}) error {
for k, v := range m {
err := setStructField(structObj, k, v)
if err != nil {
return err
}
}
return nil
}
// ToStdBase64 convert data to standard base64 encoding.
func ToStdBase64(value interface{}) string {
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
return ""
}
switch value.(type) {
case []byte:
return base64.StdEncoding.EncodeToString(value.([]byte))
case string:
return base64.StdEncoding.EncodeToString([]byte(value.(string)))
case error:
return base64.StdEncoding.EncodeToString([]byte(value.(error).Error()))
default:
marshal, err := json.Marshal(value)
if err != nil {
return ""
}
return base64.StdEncoding.EncodeToString(marshal)
}
}
// ToUrlBase64 convert data to URL base64 encoding.
func ToUrlBase64(value interface{}) string {
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
return ""
}
switch value.(type) {
case []byte:
return base64.URLEncoding.EncodeToString(value.([]byte))
case string:
return base64.URLEncoding.EncodeToString([]byte(value.(string)))
case error:
return base64.URLEncoding.EncodeToString([]byte(value.(error).Error()))
default:
marshal, err := json.Marshal(value)
if err != nil {
return ""
}
return base64.URLEncoding.EncodeToString(marshal)
}
}
// ToRawStdBase64 convert data to raw standard base64 encoding.
func ToRawStdBase64(value interface{}) string {
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
return ""
}
switch value.(type) {
case []byte:
return base64.RawStdEncoding.EncodeToString(value.([]byte))
case string:
return base64.RawStdEncoding.EncodeToString([]byte(value.(string)))
case error:
return base64.RawStdEncoding.EncodeToString([]byte(value.(error).Error()))
default:
marshal, err := json.Marshal(value)
if err != nil {
return ""
}
return base64.RawStdEncoding.EncodeToString(marshal)
}
}
// ToRawUrlBase64 convert data to raw URL base64 encoding.
func ToRawUrlBase64(value interface{}) string {
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
return ""
}
switch value.(type) {
case []byte:
return base64.RawURLEncoding.EncodeToString(value.([]byte))
case string:
return base64.RawURLEncoding.EncodeToString([]byte(value.(string)))
case error:
return base64.RawURLEncoding.EncodeToString([]byte(value.(error).Error()))
default:
marshal, err := json.Marshal(value)
if err != nil {
return ""
}
return base64.RawURLEncoding.EncodeToString(marshal)
}
}
// ToBigInt converts an integer of any supported type (int, int64, uint64, etc.) to *big.Int
// Play: todo
func ToBigInt(v interface{}) (*big.Int, error) {
result := new(big.Int)
switch v := (v).(type) {
case int:
result.SetInt64(int64(v))
case int8:
result.SetInt64(int64(v))
case int16:
result.SetInt64(int64(v))
case int32:
result.SetInt64(int64(v))
case int64:
result.SetInt64(v)
case uint:
result.SetUint64(uint64(v))
case uint8:
result.SetUint64(uint64(v))
case uint16:
result.SetUint64(uint64(v))
case uint32:
result.SetUint64(uint64(v))
case uint64:
result.SetUint64(v)
default:
return nil, fmt.Errorf("unsupported type: %T", v)
}
return result, nil
}

View File

@@ -4,7 +4,10 @@
// Package convertor implements some functions to convert data.
package convertor
import "reflect"
import (
"fmt"
"reflect"
)
type cloner struct {
ptrs map[reflect.Type]map[uintptr]reflect.Value
@@ -214,3 +217,70 @@ func (c *cloner) cloneStruct(v reflect.Value) reflect.Value {
return clonedStruct
}
func setStructField(structObj interface{}, fieldName string, fieldValue interface{}) error {
structVal := reflect.ValueOf(structObj).Elem()
fName := getFieldNameByJsonTag(structObj, fieldName)
if fName == "" {
return fmt.Errorf("Struct field json tag don't match map key : %s in obj", fieldName)
}
fieldVal := structVal.FieldByName(fName)
if !fieldVal.IsValid() {
return fmt.Errorf("No such field: %s in obj", fieldName)
}
if !fieldVal.CanSet() {
return fmt.Errorf("Cannot set %s field value", fieldName)
}
val := reflect.ValueOf(fieldValue)
if fieldVal.Type() != val.Type() {
if val.CanConvert(fieldVal.Type()) {
fieldVal.Set(val.Convert(fieldVal.Type()))
return nil
}
if m, ok := fieldValue.(map[string]interface{}); ok {
if fieldVal.Kind() == reflect.Struct {
return MapToStruct(m, fieldVal.Addr().Interface())
}
if fieldVal.Kind() == reflect.Ptr && fieldVal.Type().Elem().Kind() == reflect.Struct {
if fieldVal.IsNil() {
fieldVal.Set(reflect.New(fieldVal.Type().Elem()))
}
return MapToStruct(m, fieldVal.Interface())
}
}
return fmt.Errorf("Map value type don't match struct field type")
}
fieldVal.Set(val)
return nil
}
func getFieldNameByJsonTag(structObj interface{}, jsonTag string) string {
s := reflect.TypeOf(structObj).Elem()
for i := 0; i < s.NumField(); i++ {
field := s.Field(i)
tag := field.Tag
name := tag.Get("json")
if name == jsonTag {
return field.Name
}
}
return ""
}

View File

@@ -1,10 +1,15 @@
package convertor
import (
"encoding/base64"
"errors"
"fmt"
"io"
"math/big"
"reflect"
"testing"
"unicode/utf8"
"unsafe"
"github.com/duke-git/lancet/internal"
"github.com/duke-git/lancet/validator"
@@ -152,13 +157,17 @@ func TestStructToMap(t *testing.T) {
Name string `json:"name"`
age int
}
p := People{
p := &People{
"test",
100,
}
pm, _ := StructToMap(p)
var expected = map[string]interface{}{"name": "test"}
data, _ := StructToMap(p)
var expected = map[string]interface{}{
"name": "test",
}
assert.Equal(expected, pm)
assert.Equal(expected, data)
}
func TestColorHexToRGB(t *testing.T) {
@@ -363,3 +372,396 @@ func TestGbkToUtf8(t *testing.T) {
assert.Equal(true, utf8.Valid(utf8Data))
assert.Equal("hello", string(utf8Data))
}
func TestMapToStruct(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestMapToStruct")
type Address struct {
Street string `json:"street"`
Number int `json:"number"`
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Phone string `json:"phone"`
Addr *Address `json:"address"`
}
m := map[string]interface{}{
"name": "Nothin",
"age": 28,
"phone": "123456789",
"address": map[string]interface{}{
"street": "test",
"number": 1,
},
}
var p Person
err := MapToStruct(m, &p)
assert.IsNil(err)
assert.Equal(m["name"], p.Name)
assert.Equal(m["age"], p.Age)
assert.Equal(m["phone"], p.Phone)
assert.Equal("test", p.Addr.Street)
assert.Equal(1, p.Addr.Number)
}
func TestToStdBase64(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToStdBase64")
r1 := ToStdBase64("abc")
d1, _ := base64.StdEncoding.DecodeString(r1)
assert.Equal("abc", string(d1))
r2 := ToStdBase64([]byte("abc"))
d2, _ := base64.StdEncoding.DecodeString(r2)
assert.Equal("abc", string(d2))
r3 := ToStdBase64(123)
d3, _ := base64.StdEncoding.DecodeString(r3)
assert.Equal("123", string(d3))
r4 := ToStdBase64(11.11)
d4, _ := base64.StdEncoding.DecodeString(r4)
assert.Equal("11.11", string(d4))
r5 := ToStdBase64(map[string]interface{}{"name": "duke", "quantity": 1})
d5, _ := base64.StdEncoding.DecodeString(r5)
assert.Equal("{\"name\":\"duke\",\"quantity\":1}", string(d5))
r6 := ToStdBase64([]int64{7, 5, 9, 4, 23})
d6, _ := base64.StdEncoding.DecodeString(r6)
assert.Equal("[7,5,9,4,23]", string(d6))
r7 := ToStdBase64([]string{"7", "5", "9", "4", "23"})
d7, _ := base64.StdEncoding.DecodeString(r7)
assert.Equal("[\"7\",\"5\",\"9\",\"4\",\"23\"]", string(d7))
r8 := ToStdBase64(nil)
d8, _ := base64.StdEncoding.DecodeString(r8)
assert.Equal("", string(d8))
ch := make(chan int, 3)
ch <- 1
ch <- 2
r9 := ToStdBase64(ch)
d9, _ := base64.StdEncoding.DecodeString(r9)
assert.Equal("", string(d9))
r10 := ToStdBase64(io.EOF)
d10, _ := base64.StdEncoding.DecodeString(r10)
assert.Equal("EOF", string(d10))
r11 := ToStdBase64(errors.New("test"))
d11, _ := base64.StdEncoding.DecodeString(r11)
assert.Equal("test", string(d11))
typedNil := (*int)(nil)
r12 := ToStdBase64(typedNil)
d12, _ := base64.StdEncoding.DecodeString(r12)
assert.Equal("", string(d12))
type nilInterface interface {
}
var nI nilInterface = nil
d13, _ := base64.StdEncoding.DecodeString(ToStdBase64(nI))
assert.Equal("", string(d13))
var p unsafe.Pointer
d14, _ := base64.StdEncoding.DecodeString(ToStdBase64(p))
assert.Equal("", string(d14))
}
func TestToUrlBase64(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToUrlBase64")
r1 := ToUrlBase64("abc")
d1, _ := base64.URLEncoding.DecodeString(r1)
assert.Equal("abc", string(d1))
r2 := ToUrlBase64([]byte("abc"))
d2, _ := base64.URLEncoding.DecodeString(r2)
assert.Equal("abc", string(d2))
r3 := ToUrlBase64(123)
d3, _ := base64.URLEncoding.DecodeString(r3)
assert.Equal("123", string(d3))
r4 := ToUrlBase64(11.11)
d4, _ := base64.URLEncoding.DecodeString(r4)
assert.Equal("11.11", string(d4))
r5 := ToUrlBase64(map[string]interface{}{"name": "duke", "quantity": 1})
d5, _ := base64.URLEncoding.DecodeString(r5)
assert.Equal("{\"name\":\"duke\",\"quantity\":1}", string(d5))
r6 := ToUrlBase64([]int64{7, 5, 9, 4, 23})
d6, _ := base64.URLEncoding.DecodeString(r6)
assert.Equal("[7,5,9,4,23]", string(d6))
r7 := ToUrlBase64([]string{"7", "5", "9", "4", "23"})
d7, _ := base64.URLEncoding.DecodeString(r7)
assert.Equal("[\"7\",\"5\",\"9\",\"4\",\"23\"]", string(d7))
r8 := ToUrlBase64(nil)
d8, _ := base64.URLEncoding.DecodeString(r8)
assert.Equal("", string(d8))
ch := make(chan int, 3)
ch <- 1
ch <- 2
r9 := ToUrlBase64(ch)
d9, _ := base64.URLEncoding.DecodeString(r9)
assert.Equal("", string(d9))
r10 := ToUrlBase64(io.EOF)
d10, _ := base64.URLEncoding.DecodeString(r10)
assert.Equal("EOF", string(d10))
r11 := ToUrlBase64(errors.New("test"))
d11, _ := base64.URLEncoding.DecodeString(r11)
assert.Equal("test", string(d11))
typedNil := (*int)(nil)
r12 := ToUrlBase64(typedNil)
d12, _ := base64.URLEncoding.DecodeString(r12)
assert.Equal("", string(d12))
type nilInterface interface {
}
var nI nilInterface = nil
d13, _ := base64.URLEncoding.DecodeString(ToUrlBase64(nI))
assert.Equal("", string(d13))
var p unsafe.Pointer
d14, _ := base64.URLEncoding.DecodeString(ToUrlBase64(p))
assert.Equal("", string(d14))
r15 := ToUrlBase64("4+3/4?=")
d15, _ := base64.URLEncoding.DecodeString(r15)
assert.Equal("4+3/4?=", string(d15))
}
func TestToRawStdBase64(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToRawStdBase64")
r1 := ToRawStdBase64("abc")
d1, _ := base64.RawStdEncoding.DecodeString(r1)
assert.Equal("abc", string(d1))
r2 := ToRawStdBase64([]byte("abc"))
d2, _ := base64.RawStdEncoding.DecodeString(r2)
assert.Equal("abc", string(d2))
r3 := ToRawStdBase64(123)
d3, _ := base64.RawStdEncoding.DecodeString(r3)
assert.Equal("123", string(d3))
r4 := ToRawStdBase64(11.11)
d4, _ := base64.RawStdEncoding.DecodeString(r4)
assert.Equal("11.11", string(d4))
r5 := ToRawStdBase64(map[string]interface{}{"name": "duke", "quantity": 1})
d5, _ := base64.RawStdEncoding.DecodeString(r5)
assert.Equal("{\"name\":\"duke\",\"quantity\":1}", string(d5))
r6 := ToRawStdBase64([]int64{7, 5, 9, 4, 23})
d6, _ := base64.RawStdEncoding.DecodeString(r6)
assert.Equal("[7,5,9,4,23]", string(d6))
r7 := ToRawStdBase64([]string{"7", "5", "9", "4", "23"})
d7, _ := base64.RawStdEncoding.DecodeString(r7)
assert.Equal("[\"7\",\"5\",\"9\",\"4\",\"23\"]", string(d7))
r8 := ToRawStdBase64(nil)
d8, _ := base64.RawStdEncoding.DecodeString(r8)
assert.Equal("", string(d8))
ch := make(chan int, 3)
ch <- 1
ch <- 2
r9 := ToRawStdBase64(ch)
d9, _ := base64.RawStdEncoding.DecodeString(r9)
assert.Equal("", string(d9))
r10 := ToRawStdBase64(io.EOF)
d10, _ := base64.RawStdEncoding.DecodeString(r10)
assert.Equal("EOF", string(d10))
r11 := ToRawStdBase64(errors.New("test"))
d11, _ := base64.RawStdEncoding.DecodeString(r11)
assert.Equal("test", string(d11))
typedNil := (*int)(nil)
r12 := ToRawStdBase64(typedNil)
d12, _ := base64.RawStdEncoding.DecodeString(r12)
assert.Equal("", string(d12))
type nilInterface interface {
}
var nI nilInterface = nil
d13, _ := base64.RawStdEncoding.DecodeString(ToRawStdBase64(nI))
assert.Equal("", string(d13))
var p unsafe.Pointer
d14, _ := base64.RawStdEncoding.DecodeString(ToRawStdBase64(p))
assert.Equal("", string(d14))
}
func TestToRawUrlBase64(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToRawUrlBase64")
r1 := ToRawUrlBase64("abc")
d1, _ := base64.RawURLEncoding.DecodeString(r1)
assert.Equal("abc", string(d1))
r2 := ToRawUrlBase64([]byte("abc"))
d2, _ := base64.RawURLEncoding.DecodeString(r2)
assert.Equal("abc", string(d2))
r3 := ToRawUrlBase64(123)
d3, _ := base64.RawURLEncoding.DecodeString(r3)
assert.Equal("123", string(d3))
r4 := ToRawUrlBase64(11.11)
d4, _ := base64.RawURLEncoding.DecodeString(r4)
assert.Equal("11.11", string(d4))
r5 := ToRawUrlBase64(map[string]interface{}{"name": "duke", "quantity": 1})
d5, _ := base64.RawURLEncoding.DecodeString(r5)
assert.Equal("{\"name\":\"duke\",\"quantity\":1}", string(d5))
r6 := ToRawUrlBase64([]int64{7, 5, 9, 4, 23})
d6, _ := base64.RawURLEncoding.DecodeString(r6)
assert.Equal("[7,5,9,4,23]", string(d6))
r7 := ToRawUrlBase64([]string{"7", "5", "9", "4", "23"})
d7, _ := base64.RawURLEncoding.DecodeString(r7)
assert.Equal("[\"7\",\"5\",\"9\",\"4\",\"23\"]", string(d7))
r8 := ToRawUrlBase64(nil)
d8, _ := base64.RawURLEncoding.DecodeString(r8)
assert.Equal("", string(d8))
ch := make(chan int, 3)
ch <- 1
ch <- 2
r9 := ToRawUrlBase64(ch)
d9, _ := base64.RawURLEncoding.DecodeString(r9)
assert.Equal("", string(d9))
r10 := ToRawUrlBase64(io.EOF)
d10, _ := base64.RawURLEncoding.DecodeString(r10)
assert.Equal("EOF", string(d10))
r11 := ToRawUrlBase64(errors.New("test"))
d11, _ := base64.RawURLEncoding.DecodeString(r11)
assert.Equal("test", string(d11))
typedNil := (*int)(nil)
r12 := ToRawUrlBase64(typedNil)
d12, _ := base64.RawURLEncoding.DecodeString(r12)
assert.Equal("", string(d12))
type nilInterface interface {
}
var nI nilInterface = nil
d13, _ := base64.RawURLEncoding.DecodeString(ToRawUrlBase64(nI))
assert.Equal("", string(d13))
var p unsafe.Pointer
d14, _ := base64.RawURLEncoding.DecodeString(ToRawUrlBase64(p))
assert.Equal("", string(d14))
r15 := ToRawUrlBase64("4+3/4?=")
d15, _ := base64.RawURLEncoding.DecodeString(r15)
assert.Equal("4+3/4?=", string(d15))
}
func TestToBigInt(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToBigInt")
tests := []struct {
name string
input interface{}
want *big.Int
hasErr bool
}{
{
name: "int",
input: 42,
want: big.NewInt(42),
},
{
name: "int8",
input: int8(127),
want: big.NewInt(127),
},
{
name: "int16",
input: int16(32000),
want: big.NewInt(32000),
},
{
name: "int32",
input: int32(123456),
want: big.NewInt(123456),
},
{
name: "int64",
input: int64(987654321),
want: big.NewInt(987654321),
},
{
name: "uint",
input: uint(987654321),
want: big.NewInt(987654321),
},
{
name: "uint8",
input: uint8(255),
want: big.NewInt(255),
},
{
name: "uint16",
input: uint16(65535),
want: big.NewInt(65535),
},
{
name: "uint32",
input: uint32(4294967295),
want: big.NewInt(4294967295),
},
{
name: "uint64",
input: uint64(18446744073709551615),
want: new(big.Int).SetUint64(18446744073709551615),
},
{
name: "unsupported type",
input: 3.14, // Unsupported type
hasErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ToBigInt(tt.input)
if (err != nil) != tt.hasErr {
t.Errorf("ToBigInt() error = %v, hasErr %v", err, tt.hasErr)
return
}
assert.Equal(tt.want, got)
})
}
}

View File

@@ -13,6 +13,7 @@ import (
"crypto/des"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"io"
@@ -461,3 +462,29 @@ func RsaDecrypt(data []byte, privateKeyFileName string) []byte {
}
return plainText
}
// GenerateRsaKeyPair create rsa private and public key.
func GenerateRsaKeyPair(keySize int) (*rsa.PrivateKey, *rsa.PublicKey) {
privateKey, _ := rsa.GenerateKey(rand.Reader, keySize)
return privateKey, &privateKey.PublicKey
}
// RsaEncryptOAEP encrypts the given data with RSA-OAEP.
func RsaEncryptOAEP(data []byte, label []byte, key rsa.PublicKey) ([]byte, error) {
encryptedBytes, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, &key, data, label)
if err != nil {
return nil, err
}
return encryptedBytes, nil
}
// RsaDecryptOAEP decrypts the data with RSA-OAEP.
func RsaDecryptOAEP(ciphertext []byte, label []byte, key rsa.PrivateKey) ([]byte, error) {
decryptedBytes, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, &key, ciphertext, label)
if err != nil {
return nil, err
}
return decryptedBytes, nil
}

View File

@@ -128,3 +128,21 @@ func TestRsaEncrypt(t *testing.T) {
assert := internal.NewAssert(t, "TestRsaEncrypt")
assert.Equal(string(data), string(decrypted))
}
func TestRsaEncryptOAEP(t *testing.T) {
assert := internal.NewAssert(t, "TestRsaEncrypt")
t.Parallel()
pri, pub := GenerateRsaKeyPair(1024)
data := []byte("hello world")
label := []byte("123456")
encrypted, err := RsaEncryptOAEP(data, label, *pub)
assert.IsNil(err)
decrypted, err := RsaDecryptOAEP([]byte(encrypted), label, *pri)
assert.IsNil(err)
assert.Equal("hello world", string(decrypted))
}

View File

@@ -31,6 +31,7 @@ package datetime
import (
"fmt"
"runtime"
"strings"
"time"
)
@@ -321,3 +322,61 @@ func TimestampNano(timezone ...string) int64 {
return t.UnixNano()
}
// TrackFuncTime track the time of function execution.
// call it at top of the func like `defer TrackFuncTime(time.Now())()`
func TrackFuncTime(pre time.Time) func() {
callerName := getCallerName()
return func() {
elapsed := time.Since(pre)
fmt.Printf("Function %s execution time:\t %v", callerName, elapsed)
}
}
func getCallerName() string {
pc, _, _, ok := runtime.Caller(2)
if !ok {
return "Unknown"
}
fn := runtime.FuncForPC(pc)
if fn == nil {
return "Unknown"
}
fullName := fn.Name()
if lastDot := strings.LastIndex(fullName, "."); lastDot != -1 {
return fullName[lastDot+1:]
}
return fullName
}
// DaysBetween returns the number of days between two times.
func DaysBetween(start, end time.Time) int {
duration := end.Sub(start)
days := int(duration.Hours() / 24)
return days
}
// GenerateDatetimesBetween returns a slice of strings between two times.
// layout: the format of the datetime string
// interval: the interval between two datetimes
func GenerateDatetimesBetween(start, end time.Time, layout string, interval string) ([]string, error) {
var result []string
if start.After(end) {
start, end = end, start
}
duration, err := time.ParseDuration(interval)
if err != nil {
return nil, err
}
for current := start; !current.After(end); current = current.Add(duration) {
result = append(result, current.Format(layout))
}
return result, nil
}

View File

@@ -333,3 +333,114 @@ func TestTimestamp(t *testing.T) {
ts4 := TimestampNano()
t.Log(ts4)
}
func TestTrackFuncTime(t *testing.T) {
defer TrackFuncTime(time.Now())()
var n int
for i := 0; i < 5000000; i++ {
n++
}
}
func TestDaysBetween(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestDaysBetween")
tests := []struct {
start time.Time
end time.Time
expected int
}{
{
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC),
expected: 9,
},
{
start: time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
expected: -9,
},
{
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
expected: 0,
},
{
start: time.Date(2024, time.January, 1, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.December, 31, 0, 0, 0, 0, time.UTC),
expected: 365,
},
{
start: time.Date(2024, time.March, 1, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.March, 31, 0, 0, 0, 0, time.UTC),
expected: 30,
},
}
for _, tt := range tests {
result := DaysBetween(tt.start, tt.end)
assert.Equal(tt.expected, result)
}
}
func TestGenerateDatetimesBetween(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestGenerateDatetimesBetween")
tests := []struct {
start time.Time
end time.Time
layout string
interval string
expected []string
}{
{
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC),
layout: "2006-01-02 15:04:05",
interval: "30m",
expected: []string{
"2024-09-01 00:00:00",
"2024-09-01 00:30:00",
"2024-09-01 01:00:00",
"2024-09-01 01:30:00",
"2024-09-01 02:00:00",
},
},
{
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
layout: "2006-01-02 15:04:05",
interval: "1h",
expected: []string{"2024-09-01 00:00:00"},
},
{
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.September, 1, 3, 0, 0, 0, time.UTC),
layout: "2006-01-02 15:04:05",
interval: "2h",
expected: []string{
"2024-09-01 00:00:00",
"2024-09-01 02:00:00",
},
},
}
for _, tt := range tests {
result, err := GenerateDatetimesBetween(tt.start, tt.end, tt.layout, tt.interval)
assert.Equal(tt.expected, result)
assert.IsNil(err)
}
t.Run("Invalid interval", func(t *testing.T) {
_, err := GenerateDatetimesBetween(time.Now(), time.Now(), "2006-01-02 15:04:05", "invalid")
if err == nil {
t.Fatal("Expected error, got nil")
}
})
}

View File

@@ -42,7 +42,7 @@ import (
<b>Signature:</b>
```go
func Equal(left, right any) bool
func Equal(left, right interface{}) bool
```
<b>Example:</b>
@@ -91,7 +91,7 @@ func main() {
<b>Signature:</b>
```go
func EqualValue(left, right any) bool
func EqualValue(left, right interface{}) bool
```
<b>Example:</b>
@@ -130,7 +130,7 @@ func main() {
<b>Signature:</b>
```go
func LessThan(left, right any) bool
func LessThan(left, right interface{}) bool
```
<b>Example:</b>
@@ -179,7 +179,7 @@ func main() {
<b>Signature:</b>
```go
func GreaterThan(left, right any) bool
func GreaterThan(left, right interface{}) bool
```
<b>Example:</b>
@@ -231,7 +231,7 @@ func main() {
<b>Signature:</b>
```go
func LessOrEqual(left, right any) bool
func LessOrEqual(left, right interface{}) bool
```
<b>Example:</b>
@@ -280,7 +280,7 @@ func main() {
<b>Signature:</b>
```go
func GreaterOrEqual(left, right any) bool
func GreaterOrEqual(left, right interface{}) bool
```
<b>Example:</b>

View File

@@ -42,7 +42,7 @@ import (
<b>函数签名:</b>
```go
func Equal(left, right any) bool
func Equal(left, right interface{}) bool
```
<b>示例:</b>
@@ -91,7 +91,7 @@ func main() {
<b>函数签名:</b>
```go
func EqualValue(left, right any) bool
func EqualValue(left, right interface{}) bool
```
<b>示例:</b>
@@ -130,7 +130,7 @@ func main() {
<b>函数签名:</b>
```go
func LessThan(left, right any) bool
func LessThan(left, right interface{}) bool
```
<b>示例:</b>
@@ -179,7 +179,7 @@ func main() {
<b>函数签名:</b>
```go
func GreaterThan(left, right any) bool
func GreaterThan(left, right interface{}) bool
```
<b>示例:</b>
@@ -231,7 +231,7 @@ func main() {
<b>函数签名:</b>
```go
func LessOrEqual(left, right any) bool
func LessOrEqual(left, right interface{}) bool
```
<b>示例:</b>
@@ -280,7 +280,7 @@ func main() {
<b>函数签名:</b>
```go
func GreaterOrEqual(left, right any) bool
func GreaterOrEqual(left, right interface{}) bool
```
<b>示例:</b>

View File

@@ -33,6 +33,7 @@ import (
- [ToJson](#ToJson)
- [ToString](#ToString)
- [StructToMap](#StructToMap)
- [MapToStruct](#MapToStruct)
- [EncodeByte](#EncodeByte)
- [DecodeByte](#DecodeByte)
- [DeepClone](#DeepClone)
@@ -40,7 +41,11 @@ import (
- [ToInterface](#ToInterface)
- [Utf8ToGbk](#Utf8ToGbk)
- [GbkToUtf8](#GbkToUtf8)
- [ToStdBase64](#ToStdBase64)
- [ToUrlBase64](#ToUrlBase64)
- [ToRawStdBase64](#ToRawStdBase64)
- [ToRawUrlBase64](#ToRawUrlBase64)
- [ToBigInt](#ToBigInt)
<div STYLE="page-break-after: always;"></div>
@@ -356,7 +361,7 @@ func main() {
### <span id="StructToMap">StructToMap</span>
<p>Convert struct to map, only convert exported field, struct field tag `json` should be set.</p>
<p>Converts struct to map, only convert exported field, struct field tag `json` should be set.</p>
<b>Signature:</b>
@@ -389,6 +394,59 @@ func main() {
}
```
### <span id="MapToStruct">MapToStruct</span>
<p>Converts map to struct, only convert exported field, struct field tag `json` should be set.</p>
<b>Signature:</b>
```go
func MapToStruct(m map[string]interface{}, structObj interface{}) error
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/convertor"
)
func main() {
type Address struct {
Street string `json:"street"`
Number int `json:"number"`
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Phone string `json:"phone"`
Addr *Address `json:"address"`
}
m := map[string]interface{}{
"name": "Nothin",
"age": 28,
"phone": "123456789",
"address": map[string]interface{}{
"street": "test",
"number": 1,
},
}
var p Person
err := MapToStruct(m, &p)
if err != nil {
return
}
fmt.Printf("p.Addr.Street: %s", p.Addr.Street) //test
}
```
### <span id="EncodeByte">EncodeByte</span>
<p>Encode data to byte slice.</p>
@@ -396,7 +454,7 @@ func main() {
<b>Signature:</b>
```go
func EncodeByte(data any) ([]byte, error)
func EncodeByte(data interface{}) ([]byte, error)
```
<b>Example:</b>
@@ -422,7 +480,7 @@ func main() {
<b>Signature:</b>
```go
func DecodeByte(data []byte, target any) error
func DecodeByte(data []byte, target interface{}) error
```
<b>Example:</b>
@@ -450,7 +508,7 @@ func main() {
<b>Signature:</b>
```go
func DeepClone[T any](src T) T
func DeepClone[T interface{}](src T) T
```
<b>Example:</b>
@@ -686,4 +744,303 @@ func main() {
// true
// hello
}
```
### <span id="ToStdBase64">ToStdBase64</span>
<p>Convert a value to a string encoded in standard Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON-formatted string.</p>
<b>Signature:</b>
```go
func ToStdBase64(value interface{}) string
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/convertor"
)
func main() {
afterEncode := convertor.ToStdBase64(nil)
fmt.Println(afterEncode)
afterEncode = convertor.ToStdBase64("")
fmt.Println(afterEncode)
stringVal := "hello"
afterEncode = convertor.ToStdBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
afterEncode = convertor.ToStdBase64(byteSliceVal)
fmt.Println(afterEncode)
intVal := 123
afterEncode = convertor.ToStdBase64(intVal)
fmt.Println(afterEncode)
mapVal := map[string]interface{}{"a": "hi", "b": 2, "c": struct {
A string
B int
}{"hello", 3}}
afterEncode = convertor.ToStdBase64(mapVal)
fmt.Println(afterEncode)
floatVal := 123.456
afterEncode = convertor.ToStdBase64(floatVal)
fmt.Println(afterEncode)
boolVal := true
afterEncode = convertor.ToStdBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = convertor.ToStdBase64(errVal)
fmt.Println(afterEncode)
// Output:
//
//
// aGVsbG8=
// aGVsbG8=
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
// MTIzLjQ1Ng==
// dHJ1ZQ==
// ZXJy
}
```
### <span id="ToUrlBase64">ToUrlBase64</span>
<p>Convert a value to a string encoded in url Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON-formatted string.</p>
<b>Signature:</b>
```go
func ToUrlBase64(value interface{}) string
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/convertor"
)
func main() {
afterEncode := convertor.ToUrlBase64(nil)
fmt.Println(afterEncode)
stringVal := "hello"
afterEncode = convertor.ToUrlBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
afterEncode = convertor.ToUrlBase64(byteSliceVal)
fmt.Println(afterEncode)
intVal := 123
afterEncode = convertor.ToUrlBase64(intVal)
fmt.Println(afterEncode)
mapVal := map[string]interface{}{"a": "hi", "b": 2, "c": struct {
A string
B int
}{"hello", 3}}
afterEncode = convertor.ToUrlBase64(mapVal)
fmt.Println(afterEncode)
floatVal := 123.456
afterEncode = convertor.ToUrlBase64(floatVal)
fmt.Println(afterEncode)
boolVal := true
afterEncode = convertor.ToUrlBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = convertor.ToUrlBase64(errVal)
fmt.Println(afterEncode)
// Output:
//
// aGVsbG8=
// aGVsbG8=
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
// MTIzLjQ1Ng==
// dHJ1ZQ==
// ZXJy
}
```
### <span id="ToRawStdBase64">ToRawStdBase64</span>
<p>Convert a value to a string encoded in raw standard Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON-formatted string.</p>
<b>Signature:</b>
```go
func ToRawStdBase64(value interface{}) string
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/convertor"
)
func main() {
stringVal := "hello"
afterEncode = convertor.ToRawStdBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
afterEncode = convertor.ToRawStdBase64(byteSliceVal)
fmt.Println(afterEncode)
intVal := 123
afterEncode = convertor.ToRawStdBase64(intVal)
fmt.Println(afterEncode)
mapVal := map[string]interface{}{"a": "hi", "b": 2, "c": struct {
A string
B int
}{"hello", 3}}
afterEncode = convertor.ToRawStdBase64(mapVal)
fmt.Println(afterEncode)
floatVal := 123.456
afterEncode = convertor.ToRawStdBase64(floatVal)
fmt.Println(afterEncode)
boolVal := true
afterEncode = convertor.ToRawStdBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = convertor.ToRawStdBase64(errVal)
fmt.Println(afterEncode)
// Output:
// aGVsbG8
// aGVsbG8
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
// MTIzLjQ1Ng
// dHJ1ZQ
// ZXJy
}
```
### <span id="ToRawUrlBase64">ToRawUrlBase64</span>
<p> Convert a value to a string encoded in raw url Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON-formatted string.</p>
<b>Signature:</b>
```go
func ToRawUrlBase64(value interface{}) string
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/convertor"
)
func main() {
stringVal := "hello"
afterEncode = convertor.ToRawUrlBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
afterEncode = convertor.ToRawUrlBase64(byteSliceVal)
fmt.Println(afterEncode)
intVal := 123
afterEncode = convertor.ToRawUrlBase64(intVal)
fmt.Println(afterEncode)
mapVal := map[string]interface{}{"a": "hi", "b": 2, "c": struct {
A string
B int
}{"hello", 3}}
afterEncode = convertor.ToRawUrlBase64(mapVal)
fmt.Println(afterEncode)
floatVal := 123.456
afterEncode = convertor.ToRawUrlBase64(floatVal)
fmt.Println(afterEncode)
boolVal := true
afterEncode = convertor.ToRawStdBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = convertor.ToRawStdBase64(errVal)
fmt.Println(afterEncode)
// Output:
// aGVsbG8
// aGVsbG8
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
// MTIzLjQ1Ng
// dHJ1ZQ
// ZXJy
}
```
### <span id="ToBigInt">ToBigInt</span>
<p>Convert value to bigInt.</p>
<b>Signature:</b>
```go
func ToBigInt(v interface{}) (*big.Int, error)
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/convertor"
)
func main() {
n := 9876543210
bigInt, _ := convertor.ToBigInt(n)
fmt.Println(bigInt)
// Output:
// 9876543210
}
```

View File

@@ -33,6 +33,7 @@ import (
- [ToJson](#ToJson)
- [ToString](#ToString)
- [StructToMap](#StructToMap)
- [MapToStruct](#MapToStruct)
- [EncodeByte](#EncodeByte)
- [DecodeByte](#DecodeByte)
- [DeepClone](#DeepClone)
@@ -40,6 +41,11 @@ import (
- [ToInterface](#ToInterface)
- [Utf8ToGbk](#Utf8ToGbk)
- [GbkToUtf8](#GbkToUtf8)
- [ToStdBase64](#ToStdBase64)
- [ToUrlBase64](#ToUrlBase64)
- [ToRawStdBase64](#ToRawStdBase64)
- [ToRawUrlBase64](#ToRawUrlBase64)
- [ToBigInt](#ToBigInt)
<div STYLE="page-break-after: always;"></div>
@@ -388,6 +394,59 @@ func main() {
}
```
### <span id="MapToStruct">MapToStruct</span>
<p>将map转成structstruct中导出字段需要设置json tag标记</p>
<b>函数签名:</b>
```go
func MapToStruct(m map[string]interface{}, structObj interface{}) error
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/convertor"
)
func main() {
type Address struct {
Street string `json:"street"`
Number int `json:"number"`
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Phone string `json:"phone"`
Addr *Address `json:"address"`
}
m := map[string]interface{}{
"name": "Nothin",
"age": 28,
"phone": "123456789",
"address": map[string]interface{}{
"street": "test",
"number": 1,
},
}
var p Person
err := MapToStruct(m, &p)
if err != nil {
return
}
fmt.Printf("p.Addr.Street: %s", p.Addr.Street) //test
}
```
### <span id="EncodeByte">EncodeByte</span>
<p>将data编码成字节切片</p>
@@ -395,7 +454,7 @@ func main() {
<b>函数签名:</b>
```go
func EncodeByte(data any) ([]byte, error)
func EncodeByte(data interface{}) ([]byte, error)
```
<b>例子:</b>
@@ -421,7 +480,7 @@ func main() {
<b>函数签名:</b>
```go
func DecodeByte(data []byte, target any) error
func DecodeByte(data []byte, target interface{}) error
```
<b>例子:</b>
@@ -449,7 +508,7 @@ func main() {
<b>函数签名:</b>
```go
func DeepClone[T any](src T) T
func DeepClone[T interface{}](src T) T
```
<b>示例:</b>
@@ -686,4 +745,303 @@ func main() {
// true
// hello
}
```
### <span id="ToStdBase64">ToStdBase64</span>
<p>将值转换为StdBase64编码的字符串。error类型的数据也会把error的原因进行编码复杂的结构会转为JSON格式的字符串</p>
<b>函数签名:</b>
```go
func ToStdBase64(value interface{}) string
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/convertor"
)
func main() {
afterEncode := convertor.ToStdBase64(nil)
fmt.Println(afterEncode)
afterEncode = convertor.ToStdBase64("")
fmt.Println(afterEncode)
stringVal := "hello"
afterEncode = convertor.ToStdBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
afterEncode = convertor.ToStdBase64(byteSliceVal)
fmt.Println(afterEncode)
intVal := 123
afterEncode = convertor.ToStdBase64(intVal)
fmt.Println(afterEncode)
mapVal := map[string]interface{}{"a": "hi", "b": 2, "c": struct {
A string
B int
}{"hello", 3}}
afterEncode = convertor.ToStdBase64(mapVal)
fmt.Println(afterEncode)
floatVal := 123.456
afterEncode = convertor.ToStdBase64(floatVal)
fmt.Println(afterEncode)
boolVal := true
afterEncode = convertor.ToStdBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = convertor.ToStdBase64(errVal)
fmt.Println(afterEncode)
// Output:
//
//
// aGVsbG8=
// aGVsbG8=
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
// MTIzLjQ1Ng==
// dHJ1ZQ==
// ZXJy
}
```
### <span id="ToUrlBase64">ToUrlBase64</span>
<p>值转换为 ToUrlBase64 编码的字符串。error 类型的数据也会把 error 的原因进行编码,复杂的结构会转为 JSON 格式的字符串</p>
<b>函数签名:</b>
```go
func ToUrlBase64(value interface{}) string
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/convertor"
)
func main() {
afterEncode := convertor.ToUrlBase64(nil)
fmt.Println(afterEncode)
stringVal := "hello"
afterEncode = convertor.ToUrlBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
afterEncode = convertor.ToUrlBase64(byteSliceVal)
fmt.Println(afterEncode)
intVal := 123
afterEncode = convertor.ToUrlBase64(intVal)
fmt.Println(afterEncode)
mapVal := map[string]interface{}{"a": "hi", "b": 2, "c": struct {
A string
B int
}{"hello", 3}}
afterEncode = convertor.ToUrlBase64(mapVal)
fmt.Println(afterEncode)
floatVal := 123.456
afterEncode = convertor.ToUrlBase64(floatVal)
fmt.Println(afterEncode)
boolVal := true
afterEncode = convertor.ToUrlBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = convertor.ToUrlBase64(errVal)
fmt.Println(afterEncode)
// Output:
//
// aGVsbG8=
// aGVsbG8=
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
// MTIzLjQ1Ng==
// dHJ1ZQ==
// ZXJy
}
```
### <span id="ToRawStdBase64">ToRawStdBase64</span>
<p>值转换为 ToRawStdBase64 编码的字符串。error 类型的数据也会把 error 的原因进行编码,复杂的结构会转为 JSON 格式的字符串</p>
<b>函数签名:</b>
```go
func ToRawStdBase64(value interface{}) string
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/convertor"
)
func main() {
stringVal := "hello"
afterEncode = convertor.ToRawStdBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
afterEncode = convertor.ToRawStdBase64(byteSliceVal)
fmt.Println(afterEncode)
intVal := 123
afterEncode = convertor.ToRawStdBase64(intVal)
fmt.Println(afterEncode)
mapVal := map[string]interface{}{"a": "hi", "b": 2, "c": struct {
A string
B int
}{"hello", 3}}
afterEncode = convertor.ToRawStdBase64(mapVal)
fmt.Println(afterEncode)
floatVal := 123.456
afterEncode = convertor.ToRawStdBase64(floatVal)
fmt.Println(afterEncode)
boolVal := true
afterEncode = convertor.ToRawStdBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = convertor.ToRawStdBase64(errVal)
fmt.Println(afterEncode)
// Output:
// aGVsbG8
// aGVsbG8
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
// MTIzLjQ1Ng
// dHJ1ZQ
// ZXJy
}
```
### <span id="ToRawUrlBase64">ToRawUrlBase64</span>
<p>值转换为 ToRawUrlBase64 编码的字符串。error 类型的数据也会把 error 的原因进行编码,复杂的结构会转为 JSON 格式的字符串</p>
<b>函数签名:</b>
```go
func ToRawUrlBase64(value interface{}) string
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/convertor"
)
func main() {
stringVal := "hello"
afterEncode = convertor.ToRawUrlBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
afterEncode = convertor.ToRawUrlBase64(byteSliceVal)
fmt.Println(afterEncode)
intVal := 123
afterEncode = convertor.ToRawUrlBase64(intVal)
fmt.Println(afterEncode)
mapVal := map[string]interface{}{"a": "hi", "b": 2, "c": struct {
A string
B int
}{"hello", 3}}
afterEncode = convertor.ToRawUrlBase64(mapVal)
fmt.Println(afterEncode)
floatVal := 123.456
afterEncode = convertor.ToRawUrlBase64(floatVal)
fmt.Println(afterEncode)
boolVal := true
afterEncode = convertor.ToRawStdBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = convertor.ToRawStdBase64(errVal)
fmt.Println(afterEncode)
// Output:
// aGVsbG8
// aGVsbG8
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
// MTIzLjQ1Ng
// dHJ1ZQ
// ZXJy
}
```
### <span id="ToBigInt">ToBigInt</span>
<p>将整数值转换为bigInt。</p>
<b>函数签名:</b>
```go
func ToBigInt(v interface{}) (*big.Int, error)
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/convertor"
)
func main() {
n := 9876543210
bigInt, _ := convertor.ToBigInt(n)
fmt.Println(bigInt)
// Output:
// 9876543210
}
```

View File

@@ -65,6 +65,9 @@ import (
- [GenerateRsaKey](#GenerateRsaKey)
- [RsaEncrypt](#RsaEncrypt)
- [RsaDecrypt](#RsaDecrypt)
- [GenerateRsaKeyPair](#GenerateRsaKeyPair)
- [RsaEncryptOAEP](#RsaEncryptOAEP)
- [RsaDecryptOAEP](#RsaDecryptOAEP)
<div STYLE="page-break-after: always;"></div>
@@ -723,6 +726,7 @@ func main() {
fmt.Println(s) //3826f812255d8683f051ee97346d1359234d5dbd
}
```
### <span id="HmacSha1WithBase64">HmacSha1WithBase64</span>
<p>Return the hmac hash of string use sha1 with base64.</p>
@@ -1270,3 +1274,114 @@ func main() {
fmt.Println(string(decrypted)) //hello world
}
```
### <span id="GenerateRsaKeyPair">GenerateRsaKeyPair</span>
<p>Creates rsa private and public key.</p>
<b>Signature:</b>
```go
func GenerateRsaKeyPair(keySize int) (*rsa.PrivateKey, *rsa.PublicKey)
```
<b>Example:></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/cryptor"
)
func main() {
pri, pub := cryptor.GenerateRsaKeyPair(1024)
}
```
### <span id="RsaEncryptOAEP">RsaEncryptOAEP</span>
<p>Encrypts the given data with RSA-OAEP.</p>
<b>Signature:</b>
```go
func RsaEncryptOAEP(data []byte, label []byte, key rsa.PublicKey) ([]byte, error)
```
<b>Example:></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/cryptor"
)
func main() {
pri, pub := cryptor.GenerateRsaKeyPair(1024)
data := []byte("hello world")
label := []byte("123456")
encrypted, err := cryptor.RsaEncryptOAEP(data, label, *pub)
if err != nil {
return
}
decrypted, err := cryptor.RsaDecryptOAEP([]byte(encrypted), label, *pri)
if err != nil {
return
}
fmt.Println(string(decrypted))
// Output:
// hello world
}
```
### <span id="RsaDecryptOAEP">RsaDecryptOAEP</span>
<p>Decrypts the data with RSA-OAEP.</p>
<b>Signature:</b>
```go
func RsaDecryptOAEP(ciphertext []byte, label []byte, key rsa.PrivateKey) ([]byte, error)
```
<b>Example:></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/cryptor"
)
func main() {
pri, pub := cryptor.GenerateRsaKeyPair(1024)
data := []byte("hello world")
label := []byte("123456")
encrypted, err := cryptor.RsaEncryptOAEP(data, label, *pub)
if err != nil {
return
}
decrypted, err := cryptor.RsaDecryptOAEP([]byte(encrypted), label, *pri)
if err != nil {
return
}
fmt.Println(string(decrypted))
// Output:
// hello world
}
```

View File

@@ -64,6 +64,9 @@ import (
- [GenerateRsaKey](#GenerateRsaKey)
- [RsaEncrypt](#RsaEncrypt)
- [RsaDecrypt](#RsaDecrypt)
- [GenerateRsaKeyPair](#GenerateRsaKeyPair)
- [RsaEncryptOAEP](#RsaEncryptOAEP)
- [RsaDecryptOAEP](#RsaDecryptOAEP)
<div STYLE="page-break-after: always;"></div>
@@ -754,7 +757,6 @@ func main() {
}
```
### <span id="HmacSha256">HmacSha256</span>
<p>获取字符串sha256 hmac值。</p>
@@ -883,7 +885,6 @@ func main() {
}
```
### <span id="Md5String">Md5String</span>
<p>获取字符串md5值。</p>
@@ -1301,3 +1302,114 @@ func main() {
fmt.Println(string(decrypted)) //hello world
}
```
### <span id="GenerateRsaKeyPair">GenerateRsaKeyPair</span>
<p>创建rsa公钥私钥和key。</p>
<b>函数签名:</b>
```go
func GenerateRsaKeyPair(keySize int) (*rsa.PrivateKey, *rsa.PublicKey)
```
<b>示例:></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/cryptor"
)
func main() {
pri, pub := cryptor.GenerateRsaKeyPair(1024)
}
```
### <span id="RsaEncryptOAEP">RsaEncryptOAEP</span>
<p>rsa OAEP加密。</p>
<b>函数签名:</b>
```go
func RsaEncryptOAEP(data []byte, label []byte, key rsa.PublicKey) ([]byte, error)
```
<b>示例:></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/cryptor"
)
func main() {
pri, pub := cryptor.GenerateRsaKeyPair(1024)
data := []byte("hello world")
label := []byte("123456")
encrypted, err := cryptor.RsaEncryptOAEP(data, label, *pub)
if err != nil {
return
}
decrypted, err := cryptor.RsaDecryptOAEP([]byte(encrypted), label, *pri)
if err != nil {
return
}
fmt.Println(string(decrypted))
// Output:
// hello world
}
```
### <span id="RsaDecryptOAEP">RsaDecryptOAEP</span>
<p>rsa OAEP解密。</p>
<b>函数签名:</b>
```go
func RsaDecryptOAEP(ciphertext []byte, label []byte, key rsa.PrivateKey) ([]byte, error)
```
<b>示例:></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/cryptor"
)
func main() {
pri, pub := cryptor.GenerateRsaKeyPair(1024)
data := []byte("hello world")
label := []byte("123456")
encrypted, err := cryptor.RsaEncryptOAEP(data, label, *pub)
if err != nil {
return
}
decrypted, err := cryptor.RsaDecryptOAEP([]byte(encrypted), label, *pri)
if err != nil {
return
}
fmt.Println(string(decrypted))
// Output:
// hello world
}
```

View File

@@ -62,6 +62,9 @@ import (
- [TimestampMilli](#TimestampMilli)
- [TimestampMicro](#TimestampMicro)
- [TimestampNano](#TimestampNano)
- [TrackFuncTime](#TrackFuncTime)
- [DaysBetween](#DaysBetween)
- [GenerateDatetimesBetween](#GenerateDatetimesBetween)
<div STYLE="page-break-after: always;"></div>
@@ -1360,3 +1363,106 @@ func main() {
// 1690363051331788000
}
```
### <span id="TrackFuncTime">TrackFuncTime</span>
<p>Tracks function execution time.</p>
<b>Signature:</b>
```go
func TrackFuncTime(pre time.Time) func()
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datetime"
)
func main() {
defer datetime.TrackFuncTime(time.Now())()
var n int
for i := 0; i < 5000000; i++ {
n++
}
fmt.Println(1) // Function main execution time: 1.460287ms
}
```
### <span id="DaysBetween">DaysBetween</span>
<p>Returns the number of days between two times.</p>
<b>Signature:</b>
```go
func DaysBetween(start, end time.Time) int
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datetime"
)
func main() {
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC)
result := datetime.DaysBetween(start, end)
fmt.Println(result)
// Output:
// 9
}
```
### <span id="GenerateDatetimesBetween">GenerateDatetimesBetween</span>
<p>Returns a slice of strings between two times. `layout`: the format of the datetime string.`interval`: the interval between two datetimes.</p>
<b>Signature:</b>
```go
func GenerateDatetimesBetween(start, end time.Time, layout string, interval string) ([]string, error)
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datetime"
)
func main() {
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC)
layout := "2006-01-02 15:04:05"
interval := "1h"
result, err := datetime.GenerateDatetimesBetween(start, end, layout, interval)
fmt.Println(result)
fmt.Println(err)
// Output:
// [2024-09-01 00:00:00 2024-09-01 01:00:00 2024-09-01 02:00:00]
// <nil>
}
```

View File

@@ -62,6 +62,10 @@ import (
- [TimestampMilli](#TimestampMilli)
- [TimestampMicro](#TimestampMicro)
- [TimestampNano](#TimestampNano)
- [TrackFuncTime](#TrackFuncTime)
- [DaysBetween](#DaysBetween)
- [GenerateDatetimesBetween](#GenerateDatetimesBetween)
<div STYLE="page-break-after: always;"></div>
@@ -1279,4 +1283,108 @@ func main() {
// Output:
// 1690363051331788000
}
```
### <span id="TrackFuncTime">TrackFuncTime</span>
<p>返回两个日期之间的天数差。</p>
<b>函数签名:</b>
```go
func TrackFuncTime(pre time.Time) func()
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datetime"
)
func main() {
defer datetime.TrackFuncTime(time.Now())()
var n int
for i := 0; i < 5000000; i++ {
n++
}
// Output:
// Function main execution time: 1.608195ms
}
```
### <span id="DaysBetween">DaysBetween</span>
<p>返回两个日期之间的天数差。</p>
<b>函数签名:</b>
```go
func DaysBetween(start, end time.Time) int
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datetime"
)
func main() {
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC)
result := datetime.DaysBetween(start, end)
fmt.Println(result)
// Output:
// 9
}
```
### <span id="GenerateDatetimesBetween">GenerateDatetimesBetween</span>
<p>生成从start到end的所有日期时间的字符串列表。layout参数表示时间格式例如"2006-01-02 15:04:05"interval参数表示时间间隔例如"1h"表示1小时"30m"表示30分钟。</p>
<b>函数签名:</b>
```go
func GenerateDatetimesBetween(start, end time.Time, layout string, interval string) ([]string, error)
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datetime"
)
func main() {
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC)
layout := "2006-01-02 15:04:05"
interval := "1h"
result, err := datetime.GenerateDatetimesBetween(start, end, layout, interval)
fmt.Println(result)
fmt.Println(err)
// Output:
// [2024-09-01 00:00:00 2024-09-01 01:00:00 2024-09-01 02:00:00]
// <nil>
}
```

View File

@@ -45,8 +45,10 @@ import (
- [Sha](#Sha)
- [ReadCsvFile](#ReadCsvFile)
- [WriteCsvFile](#WriteCsvFile)
- [WriteMapsToCsv](#WriteMapsToCsv)
- [WriteStringToFile](#WriteStringToFile)
- [WriteBytesToFile](#WriteBytesToFile)
- [ReadFile](#ReadFile)
<div STYLE="page-break-after: always;"></div>
@@ -693,6 +695,7 @@ func main() {
}
```
### <span id="WriteCsvFile">WriteCsvFile</span>
<p>Write content to target csv file.</p>
@@ -730,6 +733,60 @@ func main() {
}
```
### <span id="WriteMapsToCsv">WriteMapsToCsv</span>
<p>write slice of map to csv file.</p>
<b>Signature:</b>
```go
// filepath: path of the CSV file.
// records: slice of maps to be written. the value of map should be basic type. The maps will be sorted by key in alphabeta order, then be written into csv file.
// appendToExistingFile: If true, data will be appended to the file if it exists.
// delimiter: Delimiter to use in the CSV file.
// headers: order of the csv column headers, needs to be consistent with the key of the map.
func WriteMapsToCsv(filepath string, records []map[string]interface{}, appendToExistingFile bool, delimiter rune, headers ...[]string) error
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/fileutil"
)
func main() {
fpath := "./test.csv"
fileutil.CreateFile(fpath)
f, _ := os.OpenFile(fpath, os.O_WRONLY|os.O_TRUNC, 0777)
defer f.Close()
records := []map[string]interface{}{
{"Name": "Lili", "Age": "22", "Gender": "female"},
{"Name": "Jim", "Age": "21", "Gender": "male"},
}
headers := []string{"Name", "Age", "Gender"}
err := fileutil.WriteMapsToCsv(csvFilePath, records, false, ';', headers)
if err != nil {
log.Fatal(err)
}
content, err := fileutil.ReadCsvFile(csvFilePath, ';')
fmt.Println(content)
// Output:
// [[Name Age Gender] [Lili 22 female] [Jim 21 male]]
}
```
### <span id="WriteBytesToFile">WriteBytesToFile</span>
<p>Writes bytes to target file.</p>
@@ -827,3 +884,41 @@ func main() {
// hello
}
```
### <span id="ReadFile">ReadFile</span>
<p>Read File/URL</p>
<b>Signature:</b>
```go
func ReadFile(path string) (reader io.ReadCloser, closeFn func(), err error)
```
<b>Example:<span style="float:right;display:inline-block;"> </span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/fileutil"
)
func main() {
reader, fn, err := ReadFile("https://httpbin.org/robots.txt")
if err != nil {
return
}
defer fn()
dat, err := io.ReadAll(reader)
if err != nil {
return
}
fmt.Println(string(dat))
// Output:
// User-agent: *
// Disallow: /deny
}
```

View File

@@ -45,8 +45,10 @@ import (
- [Sha](#Sha)
- [ReadCsvFile](#ReadCsvFile)
- [WriteCsvFile](#WriteCsvFile)
- [WriteMapsToCsv](#WriteMapsToCsv)
- [WriteStringToFile](#WriteStringToFile)
- [WriteBytesToFile](#WriteBytesToFile)
- [ReadFile](#ReadFile)
<div STYLE="page-break-after: always;"></div>
@@ -730,6 +732,59 @@ func main() {
}
```
### <span id="WriteMapsToCsv">WriteMapsToCsv</span>
<p>将map切片写入csv文件中。</p>
<b>函数签名:</b>
```go
// filepath: CSV文件路径。
// records: 写入文件的map切片。map值必须为基本类型。会以map键的字母顺序写入。
// appendToExistingFile: 是否为追加写模式。
// delimiter: CSV文件分割符。
// headers: CSV文件表头顺序需要与map key保持一致),不指定时按字母排序。
func WriteMapsToCsv(filepath string, records []map[string]interface{}, appendToExistingFile bool, delimiter rune, headers ...[]string) error
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/fileutil"
)
func main() {
fpath := "./test.csv"
fileutil.CreateFile(fpath)
f, _ := os.OpenFile(fpath, os.O_WRONLY|os.O_TRUNC, 0777)
defer f.Close()
records := []map[string]interface{}{
{"Name": "Lili", "Age": "22", "Gender": "female"},
{"Name": "Jim", "Age": "21", "Gender": "male"},
}
headers := []string{"Name", "Age", "Gender"}
err := fileutil.WriteMapsToCsv(csvFilePath, records, false, ';', headers)
if err != nil {
log.Fatal(err)
}
content, err := fileutil.ReadCsvFile(csvFilePath, ';')
fmt.Println(content)
// Output:
// [[Name Age Gender] [Lili 22 female] [Jim 21 male]]
}
```
### <span id="WriteBytesToFile">WriteBytesToFile</span>
<p>将bytes写入文件。</p>
@@ -827,3 +882,41 @@ func main() {
// hello
}
```
### <span id="ReadFile">ReadFile</span>
<p>读取文件或者URL</p>
<b>函数签名:</b>
```go
func ReadFile(path string) (reader io.ReadCloser, closeFn func(), err error)
```
<b>示例:<span style="float:right;display:inline-block;"></span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/fileutil"
)
func main() {
reader, fn, err := ReadFile("https://httpbin.org/robots.txt")
if err != nil {
return
}
defer fn()
dat, err := io.ReadAll(reader)
if err != nil {
return
}
fmt.Println(string(dat))
// Output:
// User-agent: *
// Disallow: /deny
}
```

View File

@@ -42,7 +42,7 @@ Param should be number or numberic string.</p>
<b>Signature:</b>
```go
func Comma(value interface{}, symbol string) string
func Comma(value interface{}, prefixSymbol string) string
```
<b>Example:</b>

View File

@@ -41,7 +41,7 @@ import (
<b>函数签名:</b>
```go
func Comma(v interface{}, symbol string) string
func Comma(v interface{}, prefixSymbol string) string
```
<b>例子:</b>

View File

@@ -27,7 +27,9 @@ import (
- [Before](#Before)
- [Curry](#Curry)
- [Compose](#Compose)
- [Debounced](#Debounced)
- [Debounce](#Debounce)
- [Debounced<sup>deprecated</sup>](#Debounced)
- [Throttle](#Throttle)
- [Delay](#Delay)
- [Pipeline](#Pipeline)
- [Schedule](#Schedule)
@@ -199,7 +201,7 @@ func main() {
### <span id="Debounced">Debounced</span>
<p>Creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.</p>
<p>Creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked. This function is deprecated. use Debounce instead.</p>
<b>Signature:</b>
@@ -238,6 +240,53 @@ func main() {
}
```
### <span id="Debounce">Debounce</span>
<p>Creates a debounced version of the provided function. The debounced function will only invoke the original function after the specified delay has passed since the last time it was invoked. It also supports canceling the debounce.</p>
<b>Signature:</b>
```go
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func())
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/function"
)
func main() {
callCount := 0
fn := func() {
callCount++
}
debouncedFn, _ := function.Debounce(fn, 500*time.Millisecond)
for i := 0; i < 10; i++ {
debouncedFn()
time.Sleep(50 * time.Millisecond)
}
time.Sleep(1 * time.Second)
fmt.Println(callCount)
debouncedFn()
time.Sleep(1 * time.Second)
fmt.Println(callCount)
// Output:
// 1
// 2
}
```
### <span id="Delay">Delay</span>
<p>Invoke function after delayed time.</p>
@@ -266,6 +315,48 @@ func main() {
}
```
### <span id="Throttle">Throttle</span>
<p>Creates a throttled version of the provided function. The returned function guarantees that it will only be invoked at most once per interval.</p>
<b>Signature:</b>
```go
func Throttle(fn func(), interval time.Duration) func()
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/function"
)
func main() {
callCount := 0
fn := func() {
callCount++
}
throttledFn := function.Throttle(fn, 1*time.Second)
for i := 0; i < 5; i++ {
throttledFn()
}
time.Sleep(1 * time.Second)
fmt.Println(callCount)
// Output:
// 1
}
```
### <span id="Schedule">Schedule</span>
<p>Invoke function every duration time, until close the returned bool chan.</p>

View File

@@ -27,7 +27,9 @@ import (
- [Before](#Before)
- [Curry](#Curry)
- [Compose](#Compose)
- [Debounced](#Debounced)
- [Debounce](#Debounce)
- [Debounced<sup>deprecated</sup>](#Debounced)
- [Throttle](#Throttle)
- [Delay](#Delay)
- [Pipeline](#Pipeline)
- [Schedule](#Schedule)
@@ -197,6 +199,53 @@ func main() {
}
```
### <span id="Debounce">Debounce</span>
<p>创建一个函数的去抖动版本。该去抖动函数仅在上次调用后的指定延迟时间过去之后才会调用原始函数。支持取消去抖动。</p>
<b>函数签名:</b>
```go
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func())
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/function"
)
func main() {
callCount := 0
fn := func() {
callCount++
}
debouncedFn, _ := function.Debounce(fn, 500*time.Millisecond)
for i := 0; i < 10; i++ {
debouncedFn()
time.Sleep(50 * time.Millisecond)
}
time.Sleep(1 * time.Second)
fmt.Println(callCount)
debouncedFn()
time.Sleep(1 * time.Second)
fmt.Println(callCount)
// Output:
// 1
// 2
}
```
### <span id="Debounced">Debounced</span>
<p>创建一个 debounced 函数,该函数延迟调用 fn 直到自上次调用 debounced 函数后等待持续时间过去。</p>
@@ -238,6 +287,48 @@ func main() {
}
```
### <span id="Throttle">Throttle</span>
<p>创建一个函数的节流版本。返回的函数保证在每个时间间隔内最多只会被调用一次。</p>
<b>函数签名:</b>
```go
func Throttle(fn func(), interval time.Duration) func()
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/function"
)
func main() {
callCount := 0
fn := func() {
callCount++
}
throttledFn := function.Throttle(fn, 1*time.Second)
for i := 0; i < 5; i++ {
throttledFn()
}
time.Sleep(1 * time.Second)
fmt.Println(callCount)
// Output:
// 1
}
```
### <span id="Delay">Delay</span>
<p>延迟delay时间后调用函数</p>

View File

@@ -29,6 +29,10 @@ import (
- [RoundToFloat](#RoundToFloat)
- [RoundToString](#RoundToString)
- [TruncRound](#TruncRound)
- [CeilToFloat](#CeilToFloat)
- [CeilToString](#CeilToString)
- [FloorToFloat](#FloorToFloat)
- [FloorToString](#FloorToString)
- [AngleToRadian](#AngleToRadian)
- [RadianToAngle](#RadianToAngle)
- [PointDistance](#PointDistance)
@@ -240,6 +244,150 @@ func main() {
}
```
### <span id="CeilToFloat">CeilToFloat</span>
<p>Round float up n decimal places.</p>
<b>Signature:</b>
```go
func CeilToFloat(x float64, n int) float64
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/mathutil"
)
func main() {
result1 := mathutil.CeilToFloat(3.14159, 1)
result2 := mathutil.CeilToFloat(3.14159, 2)
result3 := mathutil.CeilToFloat(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.2
// 3.15
// 5
}
```
### <span id="CeilToString">CeilToString</span>
<p>Round float up n decimal places.</p>
<b>Signature:</b>
```go
func CeilToString(x float64, n int) string
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/mathutil"
)
func main() {
result1 := mathutil.CeilToString(3.14159, 1)
result2 := mathutil.CeilToString(3.14159, 2)
result3 := mathutil.CeilToString(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.2
// 3.15
// 5.0000
}
```
### <span id="FloorToFloat">FloorToFloat</span>
<p>Round float down n decimal places.</p>
<b>Signature:</b>
```go
func CeilToString(x float64, n int) string
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/mathutil"
)
func main() {
result1 := mathutil.FloorToFloat(3.14159, 1)
result2 := mathutil.FloorToFloat(3.14159, 2)
result3 := mathutil.FloorToFloat(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.1
// 3.14
// 5
}
```
### <span id="FloorToString">FloorToString</span>
<p>Round float down n decimal places.</p>
<b>Signature:</b>
```go
func CeilToString(x float64, n int) string
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/mathutil"
)
func main() {
result1 := mathutil.FloorToString(3.14159, 1)
result2 := mathutil.FloorToString(3.14159, 2)
result3 := mathutil.FloorToString(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.1
// 3.14
// 5.0000
}
```
### <span id="AngleToRadian">AngleToRadian</span>
<p>Converts angle value to radian value.</p>

View File

@@ -29,6 +29,10 @@ import (
- [RoundToFloat](#RoundToFloat)
- [RoundToString](#RoundToString)
- [TruncRound](#TruncRound)
- [CeilToFloat](#CeilToFloat)
- [CeilToString](#CeilToString)
- [FloorToFloat](#FloorToFloat)
- [FloorToString](#FloorToString)
- [AngleToRadian](#AngleToRadian)
- [RadianToAngle](#RadianToAngle)
- [PointDistance](#PointDistance)
@@ -240,6 +244,151 @@ func main() {
}
```
### <span id="CeilToFloat">CeilToFloat</span>
<p>向上舍入进一法保留n位小数。</p>
<b>函数签名:</b>
```go
func CeilToFloat(x float64, n int) float64
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/mathutil"
)
func main() {
result1 := mathutil.CeilToFloat(3.14159, 1)
result2 := mathutil.CeilToFloat(3.14159, 2)
result3 := mathutil.CeilToFloat(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.2
// 3.15
// 5
}
```
### <span id="CeilToString">CeilToString</span>
<p>向上舍入进一法保留n位小数返回字符串。</p>
<b>函数签名:</b>
```go
func CeilToString(x float64, n int) string
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/mathutil"
)
func main() {
result1 := mathutil.CeilToString(3.14159, 1)
result2 := mathutil.CeilToString(3.14159, 2)
result3 := mathutil.CeilToString(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.2
// 3.15
// 5.0000
}
```
### <span id="FloorToFloat">FloorToFloat</span>
<p>向下舍入去尾法保留n位小数。</p>
<b>函数签名:</b>
```go
func CeilToString(x float64, n int) string
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/mathutil"
)
func main() {
result1 := mathutil.FloorToFloat(3.14159, 1)
result2 := mathutil.FloorToFloat(3.14159, 2)
result3 := mathutil.FloorToFloat(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.1
// 3.14
// 5
}
```
### <span id="FloorToString">FloorToString</span>
<p>向下舍入去尾法保留n位小数返回字符串。</p>
<b>函数签名:</b>
```go
func CeilToString(x float64, n int) string
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/mathutil"
)
func main() {
result1 := mathutil.FloorToString(3.14159, 1)
result2 := mathutil.FloorToString(3.14159, 2)
result3 := mathutil.FloorToString(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.1
// 3.14
// 5.0000
}
```
### <span id="AngleToRadian">AngleToRadian</span>
<p>将角度值转为弧度值</p>

View File

@@ -30,7 +30,13 @@ import (
- [RandNumeral](#RandNumeral)
- [RandNumeralOrLetter](#RandNumeralOrLetter)
- [UUIdV4](#UUIdV4)
- [RandFloat](#RandFloat)
- [RandFloats](#RandFloats)
- [RandUniqueIntSlice](#RandUniqueIntSlice)
- [RandIntSlice](#RandIntSlice)
- [RandStringSlice](#RandStringSlice)
- [RandBool](#RandBool)
- [RandBoolSlice](#RandBoolSlice)
<div STYLE="page-break-after: always;"></div>
@@ -247,6 +253,58 @@ func main() {
}
```
### <span id="RandFloat">RandFloat</span>
<p>Generate a random float64 number between [min, max) with specific precision.</p>
<b>Signature:</b>
```go
func RandFloat(min, max float64, precision int) float64
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/zbD_tuobJtr)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/random"
)
func main() {
floatNumber := random.RandFloat(1.0, 5.0, 2)
fmt.Println(floatNumber) //2.14 (random number)
}
```
### <span id="RandFloats">RandFloats</span>
<p>Generate a slice of random float64 numbers of length n that do not repeat. Number range in [min, max)</p>
<b>Signature:</b>
```go
func RandFloats(length int, min, max float64, precision int) []float64
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/I3yndUQ-rhh)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/random"
)
func main() {
floatNumbers := random.RandFloats(5, 1.0, 5.0, 2)
fmt.Println(floatNumbers) //[3.42 3.99 1.3 2.38 4.23] (random)
}
```
### <span id="RandUniqueIntSlice">RandUniqueIntSlice</span>
<p>Generate a slice of random int of length n that do not repeat.</p>
@@ -272,3 +330,110 @@ func main() {
fmt.Println(result) //[0 4 7 1 5] (random)
}
```
### <span id="RandIntSlice">RandIntSlice</span>
<p>Generate a slice of random int. Number range in [min, max)</p>
<b>Signature:</b>
```go
func RandIntSlice(length, min, max int) []int
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/random"
)
func main() {
result := random.RandIntSlice(5, 0, 10)
fmt.Println(result) //[1 4 7 1 5] (random)
}
```
### <span id="RandStringSlice">RandStringSlice</span>
<p>Generate a slice of random string of length strLen based on charset. chartset should be one of the following: random.Numeral, random.LowwerLetters, random.UpperLetters random.Letters, random.SymbolChars, random.AllChars. or a combination of them.</p>
<b>Signature:</b>
```go
func RandStringSlice(charset string, sliceLen, strLen int) []string
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/random"
)
func main() {
strs := random.RandStringSlice(random.Letters, 4, 6)
fmt.Println(strs)
// output random string slice like below:
//[CooSMq RUFjDz FAeMPf heRyGv]
}
```
### <span id="RandBool">RandBool</span>
<p>Generate a random boolean value (true or false).</p>
<b>Signature:</b>
```go
func RandBool() bool
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/random"
)
func main() {
result := random.RandBool()
fmt.Println(result) // true or false (random)
}
```
### <span id="RandBoolSlice">RandBoolSlice</span>
<p>Generates a random boolean slice of specified length.</p>
<b>Signature:</b>
```go
func RandBoolSlice(length int) []bool
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/random"
)
func main() {
result := random.RandBoolSlice(2)
fmt.Println(result) // [true false] (random)
}
```

View File

@@ -30,7 +30,13 @@ import (
- [RandNumeral](#RandNumeral)
- [RandNumeralOrLetter](#RandNumeralOrLetter)
- [UUIdV4](#UUIdV4)
- [RandFloat](#RandFloat)
- [RandFloats](#RandFloats)
- [RandUniqueIntSlice](#RandUniqueIntSlice)
- [RandIntSlice](#RandIntSlice)
- [RandStringSlice](#RandStringSlice)
- [RandBool](#RandBool)
- [RandBoolSlice](#RandBoolSlice)
<div STYLE="page-break-after: always;"></div>
@@ -247,6 +253,58 @@ func main() {
}
```
### <span id="RandFloat">RandFloat</span>
<p>Generate a random float64 number between [min, max) with specific precision.</p>
<b>Signature:</b>
```go
func RandFloat(min, max float64, precision int) float64
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/zbD_tuobJtr)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/random"
)
func main() {
floatNumber := random.RandFloat(1.0, 5.0, 2)
fmt.Println(floatNumber) //2.14 (random number)
}
```
### <span id="RandFloats">RandFloats</span>
<p>Generate a slice of random float64 numbers of length n that do not repeat. Number range in [min, max)</p>
<b>Signature:</b>
```go
func RandFloats(length int, min, max float64, precision int) []float64
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/I3yndUQ-rhh)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/random"
)
func main() {
floatNumbers := random.RandFloats(5, 1.0, 5.0, 2)
fmt.Println(floatNumbers) //[3.42 3.99 1.3 2.38 4.23] (random)
}
```
### <span id="RandUniqueIntSlice">RandUniqueIntSlice</span>
<p>生成一个不重复的长度为n的随机int切片。</p>
@@ -272,3 +330,110 @@ func main() {
fmt.Println(result) //[0 4 7 1 5] (random)
}
```
### <span id="RandIntSlice">RandIntSlice</span>
<p>生成一个特定长度的随机int切片数值范围[min, max)。</p>
<b>函数签名:</b>
```go
func RandIntSlice(length, min, max int) []int
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/random"
)
func main() {
result := random.RandIntSlice(5, 0, 10)
fmt.Println(result) //[1 2 7 1 5] (random)
}
```
### <span id="RandStringSlice">RandStringSlice</span>
<p>生成随机字符串slice. 字符串类型需要是以下几种或者它们的组合: random.Numeral, random.LowwerLetters, random.UpperLetters random.Letters, random.SymbolChars, random.AllChars。</p>
<b>函数签名:</b>
```go
func RandStringSlice(charset string, sliceLen, strLen int) []string
```
<b>实例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/random"
)
func main() {
strs := random.RandStringSlice(random.Letters, 4, 6)
fmt.Println(strs)
// output random string slice like below:
//[CooSMq RUFjDz FAeMPf heRyGv]
}
```
### <span id="RandBool">RandBool</span>
<p>生成随机bool值(true or false)。</p>
<b>函数签名:</b>
```go
func RandBool() bool
```
<b>实例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/random"
)
func main() {
result := random.RandBool()
fmt.Println(result) // true or false (random)
}
```
### <span id="RandBoolSlice">RandBoolSlice</span>
<p>生成特定长度的随机bool slice。</p>
<b>函数签名:</b>
```go
func RandBoolSlice(length int) []bool
```
<b>实例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/random"
)
func main() {
result := random.RandBoolSlice(2)
fmt.Println(result) // [true false] (random)
}
```

View File

@@ -58,6 +58,14 @@ import (
- [SplitAndTrim](#SplitAndTrim)
- [HideString](#HideString)
- [RemoveWhiteSpace](#RemoveWhiteSpace)
- [SubInBetween](#SubInBetween)
- [HammingDistance](#HammingDistance)
- [Concat](#Concat)
- [Ellipsis](#Ellipsis)
- [Shuffle](#Shuffle)
- [Rotate](#Rotate)
- [TemplateReplace](#TemplateReplace)
- [RegexMatchAllGroups](#RegexMatchAllGroups)
<div STYLE="page-break-after: always;"></div>
@@ -1274,3 +1282,259 @@ func main() {
// hello world
}
```
### <span id="SubInBetween">SubInBetween</span>
<p>Return substring between the start and end position(excluded) of source string.</p>
<b>Signature:</b>
```go
func SubInBetween(str string, start string, end string) string
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/strutil"
)
func main() {
str := "abcde"
result1 := strutil.SubInBetween(str, "", "de")
result2 := strutil.SubInBetween(str, "a", "d")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// abc
// bc
}
```
### <span id="HammingDistance">HammingDistance</span>
<p>HammingDistance calculates the Hamming distance between two strings. The Hamming distance is the number of positions at which the corresponding symbols are different.</p>
<b>Signature:</b>
```go
HammingDistance(a, b string) (int, error)
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/strutil"
)
func main() {
result1, _ := strutil.HammingDistance("de", "de")
result2, _ := strutil.HammingDistance("a", "d")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 0
// 1
}
```
### <span id="Concat">Concat</span>
<p>Concatenates strings. <b>length</b> is the length of the concatenated string. If unsure, pass 0 or a negative number.</p>
<b>Signature:</b>
```go
func Concat(length int, str ...string) string
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/strutil"
)
func main() {
result1 := strutil.Concat(12, "Hello", " ", "World", "!")
result2 := strutil.Concat(11, "Go", " ", "Language")
result3 := strutil.Concat(0, "An apple a ", "day", "keeps the", " doctor away")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// Hello World!
// Go Language
// An apple a daykeeps the doctor away
}
```
### <span id="Ellipsis">Ellipsis</span>
<p>Truncates a string to a specified length and appends an ellipsis.</p>
<b>Signature:</b>
```go
func Ellipsis(str string, length int) string
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/strutil"
)
func main() {
result1 := strutil.Ellipsis("hello world", 5)
result2 := strutil.Ellipsis("你好,世界!", 2)
result3 := strutil.Ellipsis("😀😃😄😁😆", 3)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// hello...
// 你好...
// 😀😃😄...
}
```
### <span id="Shuffle">Shuffle</span>
<p>Shuffle the order of characters of given string.</p>
<b>Signature:</b>
```go
func Shuffle(str string) string
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/strutil"
)
func main() {
result := strutil.Shuffle("hello")
fmt.Println(result) //olelh (random order)
}
```
### <span id="Rotate">Rotate</span>
<p>Rotates the string by the specified number of characters.</p>
<b>Signature:</b>
```go
func Rotate(str string, shift int) string
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/strutil"
)
func main() {
result1 := Rotate("Hello", 0)
result2 := Rotate("Hello", 1)
result3 := Rotate("Hello", 2)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// Hello
// oHell
// loHel
}
### <span id="TemplateReplace">TemplateReplace</span>
<p>Replaces the placeholders in the template string with the corresponding values in the data map.The placeholders are enclosed in curly braces, e.g. {key}. for example, the template string is "Hello, {name}!", and the data map is {"name": "world"}, the result will be "Hello, world!".</p>
<b>Signature:</b>
```go
func TemplateReplace(template string, data map[string]string string
```
<b>example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/strutil"
)
func main() {
template := `Hello, my name is {name}, I'm {age} years old.`
data := map[string]string{
"name": "Bob",
"age": "20",
}
result := strutil.TemplateReplace(template, data)
fmt.Println(result)
// Output:
// Hello, my name is Bob, I'm 20 years old.
}
```
### <span id="RegexMatchAllGroups">RegexMatchAllGroups</span>
<p>Matches all subgroups in a string using a regular expression and returns the result.</p>
<b>Signature:</b>
```go
func RegexMatchAllGroups(pattern, str string) [][]string
```
<b>example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/strutil"
)
func main() {
pattern := `(\w+\.+\w+)@(\w+)\.(\w+)`
str := "Emails: john.doe@example.com and jane.doe@example.com"
result := strutil.RegexMatchAllGroups(pattern, str)
fmt.Println(result[0])
fmt.Println(result[1])
// Output:
// [john.doe@example.com john.doe example com]
// [jane.doe@example.com jane.doe example com]
}
```

View File

@@ -58,6 +58,14 @@ import (
- [SplitAndTrim](#SplitAndTrim)
- [HideString](#HideString)
- [RemoveWhiteSpace](#RemoveWhiteSpace)
- [SubInBetween](#SubInBetween)
- [HammingDistance](#HammingDistance)
- [Concat](#Concat)
- [Ellipsis](#Ellipsis)
- [Shuffle](#Shuffle)
- [Rotate](#Rotate)
- [TemplateReplace](#TemplateReplace)
- [RegexMatchAllGroups](#RegexMatchAllGroups)
<div STYLE="page-break-after: always;"></div>
@@ -1307,3 +1315,261 @@ func main() {
// hello world
}
```
### <span id="SubInBetween">SubInBetween</span>
<p>获取字符串中指定的起始字符串start和终止字符串end直接的子字符串。</p>
<b>函数签名:</b>
```go
func SubInBetween(str string, start string, end string) string
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/strutil"
)
func main() {
str := "abcde"
result1 := strutil.SubInBetween(str, "", "de")
result2 := strutil.SubInBetween(str, "a", "d")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// abc
// bc
}
```
### <span id="HammingDistance">HammingDistance</span>
<p>计算两个字符串之间的汉明距离。汉明距离是指对应符号不同的位置数。</p>
<b>函数签名:</b>
```go
HammingDistance(a, b string) (int, error)
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/strutil"
)
func main() {
result1, _ := strutil.HammingDistance("de", "de")
result2, _ := strutil.HammingDistance("a", "d")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 0
// 1
}
```
### <span id="Concat">Concat</span>
<p>拼接字符串。length是拼接后字符串的长度如果不确定则传0或负数。</p>
<b>函数签名:</b>
```go
func Concat(length int, str ...string) string
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/strutil"
)
func main() {
result1 := strutil.Concat(12, "Hello", " ", "World", "!")
result2 := strutil.Concat(11, "Go", " ", "Language")
result3 := strutil.Concat(0, "An apple a ", "day", "keeps the", " doctor away")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// Hello World!
// Go Language
// An apple a daykeeps the doctor away
}
```
### <span id="Ellipsis">Ellipsis</span>
<p>将字符串截断到指定长度,并在末尾添加省略号。</p>
<b>函数签名:</b>
```go
func Ellipsis(str string, length int) string
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/strutil"
)
func main() {
result1 := strutil.Ellipsis("hello world", 5)
result2 := strutil.Ellipsis("你好,世界!", 2)
result3 := strutil.Ellipsis("😀😃😄😁😆", 3)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// hello...
// 你好...
// 😀😃😄...
}
```
### <span id="Shuffle">Shuffle</span>
<p>打乱给定字符串中的字符顺序。</p>
<b>函数签名:</b>
```go
func Shuffle(str string) string
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/strutil"
)
func main() {
result := strutil.Shuffle("hello")
fmt.Println(result) //olelh (random order)
}
```
### <span id="Rotate">Rotate</span>
<p>按指定的字符数旋转字符串。</p>
<b>函数签名:</b>
```go
func Rotate(str string, shift int) string
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/strutil"
)
func main() {
result1 := Rotate("Hello", 0)
result2 := Rotate("Hello", 1)
result3 := Rotate("Hello", 2)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// Hello
// oHell
// loHel
}
```
### <span id="TemplateReplace">TemplateReplace</span>
<p>将模板字符串中的占位符替换为数据映射中的相应值。占位符括在花括号中,例如 {key}。例如模板字符串为“Hello, {name}!”,数据映射为{"name": "world"}结果将为“Hello, world!”。</p>
<b>函数签名:</b>
```go
func TemplateReplace(template string, data map[string]string) string
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/strutil"
)
func main() {
template := `Hello, my name is {name}, I'm {age} years old.`
data := map[string]string{
"name": "Bob",
"age": "20",
}
result := strutil.TemplateReplace(template, data)
fmt.Println(result)
// Output:
// Hello, my name is Bob, I'm 20 years old.
}
```
### <span id="RegexMatchAllGroups">RegexMatchAllGroups</span>
<p>使用正则表达式匹配字符串中的所有子组并返回结果。</p>
<b>函数签名:</b>
```go
func RegexMatchAllGroups(pattern, str string) [][]string
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/strutil"
)
func main() {
pattern := `(\w+\.+\w+)@(\w+)\.(\w+)`
str := "Emails: john.doe@example.com and jane.doe@example.com"
result := strutil.RegexMatchAllGroups(pattern, str)
fmt.Println(result[0])
fmt.Println(result[1])
// Output:
// [john.doe@example.com john.doe example com]
// [jane.doe@example.com jane.doe example com]
}
```

View File

@@ -54,7 +54,16 @@ import (
- [IsZeroValue](#IsZeroValue)
- [IsGBK](#IsGBK)
- [IsASCII](#IsASCII)
- [IsAIsPrintableSCII](#IsPrintable)
- [IsPrintable](#IsPrintable)
- [IsBin](#IsBin)
- [IsHex](#IsHex)
- [IsBase64URL](#IsBase64URL)
- [IsJWT](#IsJWT)
- [IsVisa](#IsVisa)
- [IsMasterCard](#IsMasterCard)
- [IsAmericanExpress](#IsAmericanExpress)
- [IsUnionPay](#IsUnionPay)
- [IsChinaUnionPay](#IsChinaUnionPay)
<div STYLE="page-break-after: always;"></div>
@@ -1026,4 +1035,301 @@ func main() {
// true
// false
}
```
```
### <span id="IsBin">IsBin</span>
<p>Checks if a give string is a valid binary value or not.</p>
<b>Signature:</b>
```go
func IsBin(v string) bool
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/validator"
)
func main() {
result1 := validator.IsBin("0101")
result2 := validator.IsBin("0b1101")
result3 := validator.IsBin("b1101")
result4 := validator.IsBin("1201")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// true
// true
// false
// false
}
```
### <span id="IsHex">IsHex</span>
<p>Checks if a give string is a valid hexadecimal value or not.</p>
<b>Signature:</b>
```go
func IsHex(v string) bool
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/validator"
)
func main() {
result1 := validator.IsHex("0xabcde")
result2 := validator.IsHex("0XABCDE")
result3 := validator.IsHex("cdfeg")
result4 := validator.IsHex("0xcdfeg")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// true
// true
// false
// false
}
```
### <span id="IsBase64URL">IsBase64URL</span>
<p>Checks if a give string is a valid URL-safe Base64 encoded string.</p>
<b>Signature:</b>
```go
func IsBase64URL(v string) bool
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/validator"
)
func main() {
result1 := validator.IsBase64URL("SAGsbG8sIHdvcmxkIQ")
result2 := validator.IsBase64URL("SAGsbG8sIHdvcmxkIQ==")
result3 := validator.IsBase64URL("SAGsbG8sIHdvcmxkIQ=")
result4 := validator.IsBase64URL("SAGsbG8sIHdvcmxkIQ===")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// true
// true
// false
// false
}
```
### <span id="IsJWT">IsJWT</span>
<p>Checks if a give string is is a valid JSON Web Token (JWT).</p>
<b>Signature:</b>
```go
func IsJWT(v string) bool
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/validator"
)
func main() {
result1 := validator.IsJWT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibWVzc2FnZSI6IlB1dGluIGlzIGFic29sdXRlIHNoaXQiLCJpYXQiOjE1MTYyMzkwMjJ9.wkLWA5GtCpWdxNOrRse8yHZgORDgf8TpJp73WUQb910")
result2 := validator.IsJWT("abc")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
```
### <span id="IsVisa">IsVisa</span>
<p>Checks if a give string is a valid visa card nubmer or not.</p>
<b>Signature:</b>
```go
func IsVisa(v string) bool
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/validator"
)
func main() {
result1 := validator.IsVisa("4111111111111111")
result2 := validator.IsVisa("123")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
```
### <span id="IsMasterCard">IsMasterCard</span>
<p>Checks if a give string is a valid mastercard nubmer or not.</p>
<b>Signature:</b>
```go
func IsMasterCard(v string) bool
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/validator"
)
func main() {
result1 := validator.IsMasterCard("5425233430109903")
result2 := validator.IsMasterCard("4111111111111111")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
```
### <span id="IsAmericanExpress">IsAmericanExpress</span>
<p>Checks if a give string is a valid american express nubmer or not.</p>
<b>Signature:</b>
```go
func IsAmericanExpress(v string) bool
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/validator"
)
func main() {
result1 := validator.IsAmericanExpress("342883359122187")
result2 := validator.IsAmericanExpress("3782822463100007")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
```
### <span id="IsUnionPay">IsVisa</span>
<p>Checks if a give string is a valid union pay nubmer or not.</p>
<b>Signature:</b>
```go
func IsUnionPay(v string) bool
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/validator"
)
func main() {
result1 := validator.IsUnionPay("6221263430109903")
result2 := validator.IsUnionPay("3782822463100007")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
```
### <span id="IsChinaUnionPay">IsChinaUnionPay</span>
<p>Checks if a give string is a valid china union pay nubmer or not.</p>
<b>Signature:</b>
```go
func IsChinaUnionPay(v string) bool
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/validator"
)
func main() {
result1 := validator.IsChinaUnionPay("6250941006528599")
result2 := validator.IsChinaUnionPay("3782822463100007")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
```

View File

@@ -55,6 +55,15 @@ import (
- [IsGBK](#IsGBK)
- [IsASCII](#IsASCII)
- [IsPrintable](#IsPrintable)
- [IsBin](#IsBin)
- [IsHex](#IsHex)
- [IsBase64URL](#IsBase64URL)
- [IsJWT](#IsJWT)
- [IsVisa](#IsVisa)
- [IsMasterCard](#IsMasterCard)
- [IsAmericanExpress](#IsAmericanExpress)
- [IsUnionPay](#IsUnionPay)
- [IsChinaUnionPay](#IsChinaUnionPay)
<div STYLE="page-break-after: always;"></div>
@@ -958,6 +967,7 @@ func main() {
fmt.Println("data encoding is unknown")
}
```
### <span id="IsASCII">IsASCII</span>
<p>验证字符串全部为ASCII字符。</p>
@@ -1036,4 +1046,301 @@ func main() {
// true
// false
}
```
```
### <span id="IsBin">IsBin</span>
<p>检查字符串是否是有效的二进制数。</p>
<b>函数签名:</b>
```go
func IsBin(v string) bool
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/validator"
)
func main() {
result1 := validator.IsBin("0101")
result2 := validator.IsBin("0b1101")
result3 := validator.IsBin("b1101")
result4 := validator.IsBin("1201")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// true
// true
// false
// false
}
```
### <span id="IsHex">IsHex</span>
<p>检查字符串是否是有效的十六进制数。</p>
<b>函数签名:</b>
```go
func IsHex(v string) bool
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/validator"
)
func main() {
result1 := validator.IsHex("0xabcde")
result2 := validator.IsHex("0XABCDE")
result3 := validator.IsHex("cdfeg")
result4 := validator.IsHex("0xcdfeg")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// true
// true
// false
// false
}
```
### <span id="IsBase64URL">IsBase64URL</span>
<p>检查字符串是否是有效的base64 url。</p>
<b>函数签名:</b>
```go
func IsBase64URL(v string) bool
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/validator"
)
func main() {
result1 := validator.IsBase64URL("SAGsbG8sIHdvcmxkIQ")
result2 := validator.IsBase64URL("SAGsbG8sIHdvcmxkIQ==")
result3 := validator.IsBase64URL("SAGsbG8sIHdvcmxkIQ=")
result4 := validator.IsBase64URL("SAGsbG8sIHdvcmxkIQ===")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// true
// true
// false
// false
}
```
### <span id="IsJWT">IsJWT</span>
<p>检查字符串是否是有效的JSON Web Token (JWT)。</p>
<b>函数签名:</b>
```go
func IsJWT(v string) bool
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/validator"
)
func main() {
result1 := validator.IsJWT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibWVzc2FnZSI6IlB1dGluIGlzIGFic29sdXRlIHNoaXQiLCJpYXQiOjE1MTYyMzkwMjJ9.wkLWA5GtCpWdxNOrRse8yHZgORDgf8TpJp73WUQb910")
result2 := validator.IsJWT("abc")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
```
### <span id="IsVisa">IsVisa</span>
<p>检查字符串是否是有效的visa卡号。</p>
<b>函数签名:</b>
```go
func IsVisa(v string) bool
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/validator"
)
func main() {
result1 := validator.IsVisa("4111111111111111")
result2 := validator.IsVisa("123")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
```
### <span id="IsMasterCard">IsMasterCard</span>
<p>检查字符串是否是有效的MasterCard卡号。</p>
<b>函数签名:</b>
```go
func IsMasterCard(v string) bool
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/validator"
)
func main() {
result1 := validator.IsMasterCard("5425233430109903")
result2 := validator.IsMasterCard("4111111111111111")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
```
### <span id="IsAmericanExpress">IsAmericanExpress</span>
<p>检查字符串是否是有效的American Express卡号。</p>
<b>函数签名:</b>
```go
func IsAmericanExpress(v string) bool
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/validator"
)
func main() {
result1 := validator.IsAmericanExpress("342883359122187")
result2 := validator.IsAmericanExpress("3782822463100007")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
```
### <span id="IsUnionPay">IsUnionPay</span>
<p>检查字符串是否是有效的美国银联卡号。</p>
<b>函数签名:</b>
```go
func IsUnionPay(v string) bool
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/validator"
)
func main() {
result1 := validator.IsUnionPay("6221263430109903")
result2 := validator.IsUnionPay("3782822463100007")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
```
### <span id="IsChinaUnionPay">IsChinaUnionPay</span>
<p>检查字符串是否是有效的中国银联卡号。</p>
<b>函数签名:</b>
```go
func IsChinaUnionPay(v string) bool
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/validator"
)
func main() {
result1 := validator.IsChinaUnionPay("6250941006528599")
result2 := validator.IsChinaUnionPay("3782822463100007")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
```

View File

@@ -17,12 +17,16 @@ import (
"io"
"io/fs"
"io/ioutil"
"log"
"net/http"
"os"
"path"
"path/filepath"
"runtime"
"sort"
"strings"
"sync"
"github.com/duke-git/lancet/validator"
)
// IsExist checks if a file or directory exists
@@ -168,7 +172,15 @@ func ListFileNames(path string) ([]string, error) {
}
// Zip create zip file, fpath could be a single file or a directory
func Zip(fpath string, destPath string) error {
func Zip(path string, destPath string) error {
if IsDir(path) {
return zipFolder(path, destPath)
}
return zipFile(path, destPath)
}
func zipFile(filePath string, destPath string) error {
zipFile, err := os.Create(destPath)
if err != nil {
return err
@@ -178,7 +190,97 @@ func Zip(fpath string, destPath string) error {
archive := zip.NewWriter(zipFile)
defer archive.Close()
return addFileToArchive(fpath, archive)
return addFileToArchive1(filePath, archive)
}
func zipFolder(folderPath string, destPath string) error {
outFile, err := os.Create(destPath)
if err != nil {
return err
}
defer outFile.Close()
w := zip.NewWriter(outFile)
err = addFileToArchive2(w, folderPath, "")
if err != nil {
return err
}
err = w.Close()
if err != nil {
return err
}
return nil
}
func addFileToArchive1(fpath string, archive *zip.Writer) error {
err := filepath.Walk(fpath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
header.Name = strings.TrimPrefix(path, filepath.Dir(fpath)+"/")
if info.IsDir() {
header.Name += "/"
} else {
header.Method = zip.Deflate
writer, err := archive.CreateHeader(header)
if err != nil {
return err
}
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
if _, err := io.Copy(writer, file); err != nil {
return err
}
}
return nil
})
return err
}
func addFileToArchive2(w *zip.Writer, basePath, baseInZip string) error {
files, err := os.ReadDir(basePath)
if err != nil {
return err
}
if !strings.HasSuffix(basePath, "/") {
basePath = basePath + "/"
}
for _, file := range files {
if !file.IsDir() {
dat, err := os.ReadFile(basePath + file.Name())
if err != nil {
return err
}
f, err := w.Create(baseInZip + file.Name())
if err != nil {
return err
}
_, err = f.Write(dat)
if err != nil {
return err
}
} else if file.IsDir() {
newBase := basePath + file.Name() + "/"
addFileToArchive2(w, newBase, baseInZip+file.Name()+"/")
}
}
return nil
}
// UnZip unzip the file and save it to destPath
@@ -259,7 +361,7 @@ func ZipAppendEntry(fpath string, destPath string) error {
}
}
err = addFileToArchive(fpath, archive)
err = addFileToArchive1(fpath, archive)
if err != nil {
return err
@@ -281,41 +383,6 @@ func ZipAppendEntry(fpath string, destPath string) error {
return CopyFile(tempFile.Name(), destPath)
}
func addFileToArchive(fpath string, archive *zip.Writer) error {
err := filepath.Walk(fpath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
header.Name = strings.TrimPrefix(path, filepath.Dir(fpath)+"/")
if info.IsDir() {
header.Name += "/"
} else {
header.Method = zip.Deflate
writer, err := archive.CreateHeader(header)
if err != nil {
return err
}
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
if _, err := io.Copy(writer, file); err != nil {
return err
}
}
return nil
})
return err
}
func safeFilepathJoin(path1, path2 string) (string, error) {
relPath, err := filepath.Rel(".", path2)
if err != nil || strings.HasPrefix(relPath, "..") {
@@ -386,7 +453,7 @@ func CurrentPath() string {
var absPath string
_, filename, _, ok := runtime.Caller(1)
if ok {
absPath = path.Dir(filename)
absPath = filepath.Dir(filename)
}
return absPath
@@ -460,15 +527,19 @@ func Sha(filepath string, shaType ...int) (string, error) {
}
// ReadCsvFile read file content into slice.
func ReadCsvFile(filepath string) ([][]string, error) {
func ReadCsvFile(filepath string, delimiter ...rune) ([][]string, error) {
f, err := os.Open(filepath)
if err != nil {
return nil, err
}
defer f.Close()
csvReader := csv.NewReader(f)
records, err := csvReader.ReadAll()
reader := csv.NewReader(f)
if len(delimiter) > 0 {
reader.Comma = delimiter[0]
}
records, err := reader.ReadAll()
if err != nil {
return nil, err
}
@@ -477,7 +548,7 @@ func ReadCsvFile(filepath string) ([][]string, error) {
}
// WriteCsvFile write content to target csv file.
func WriteCsvFile(filepath string, records [][]string, append bool) error {
func WriteCsvFile(filepath string, records [][]string, append bool, delimiter ...rune) error {
flag := os.O_RDWR | os.O_CREATE
if append {
@@ -492,11 +563,90 @@ func WriteCsvFile(filepath string, records [][]string, append bool) error {
defer f.Close()
writer := csv.NewWriter(f)
writer.Comma = ','
// 设置默认分隔符为逗号,除非另外指定
if len(delimiter) > 0 {
writer.Comma = delimiter[0]
} else {
writer.Comma = ','
}
// 遍历所有记录并处理包含分隔符或双引号的单元格
for i := range records {
for j := range records[i] {
records[i][j] = escapeCSVField(records[i][j], writer.Comma)
}
}
return writer.WriteAll(records)
}
// escapeCSVField 处理单元格内容,如果包含分隔符,则用双引号包裹
func escapeCSVField(field string, delimiter rune) string {
// 替换所有的双引号为两个双引号
escapedField := strings.ReplaceAll(field, "\"", "\"\"")
// 如果字段包含分隔符、双引号或换行符,用双引号包裹整个字段
if strings.ContainsAny(escapedField, string(delimiter)+"\"\n") {
escapedField = fmt.Sprintf("\"%s\"", escapedField)
}
return escapedField
}
// WriteMapsToCsv write slice of map to csv file.
// Play: https://go.dev/play/p/umAIomZFV1c
// filepath: Path to the CSV file.
// records: Slice of maps to be written. the value of map should be basic type.
// the maps will be sorted by key in alphabeta order, then be written into csv file.
// appendToExistingFile: If true, data will be appended to the file if it exists.
// delimiter: Delimiter to use in the CSV file.
// headers: order of the csv column headers, needs to be consistent with the key of the map.
func WriteMapsToCsv(filepath string, records []map[string]interface{}, appendToExistingFile bool, delimiter rune, headers ...[]string) error {
for _, record := range records {
for _, value := range record {
if !isCsvSupportedType(value) {
return errors.New("unsupported value type detected; only basic types are supported: \nbool, rune, string, int, int64, float32, float64, uint, byte, complex128, complex64, uintptr")
}
}
}
var columnHeaders []string
if len(headers) > 0 {
columnHeaders = headers[0]
} else {
for key := range records[0] {
columnHeaders = append(columnHeaders, key)
}
// sort keys in alphabeta order
sort.Strings(columnHeaders)
}
var datasToWrite [][]string
if !appendToExistingFile {
datasToWrite = append(datasToWrite, columnHeaders)
}
for _, record := range records {
var row []string
for _, h := range columnHeaders {
row = append(row, fmt.Sprintf("%v", record[h]))
}
datasToWrite = append(datasToWrite, row)
}
return WriteCsvFile(filepath, datasToWrite, appendToExistingFile, delimiter)
}
// check if the value of map which to be written into csv is basic type.
func isCsvSupportedType(v interface{}) bool {
switch v.(type) {
case bool, rune, string, int, int64, float32, float64, uint, byte, complex128, complex64, uintptr:
return true
default:
return false
}
}
// WriteStringToFile write string to target file.
func WriteStringToFile(filepath string, content string, append bool) error {
flag := os.O_RDWR | os.O_CREATE
@@ -526,3 +676,109 @@ func WriteBytesToFile(filepath string, content []byte) error {
_, err = f.Write(content)
return err
}
// ReadFile get file reader by a url or a local file.
func ReadFile(path string) (reader io.ReadCloser, closeFn func(), err error) {
switch {
case validator.IsUrl(path):
resp, err := http.Get(path)
if err != nil {
return nil, func() {}, err
}
return resp.Body, func() { resp.Body.Close() }, nil
case IsExist(path):
reader, err := os.Open(path)
if err != nil {
return nil, func() {}, err
}
return reader, func() { reader.Close() }, nil
default:
return nil, func() {}, errors.New("unknown file type")
}
}
// ChunkRead 从文件的指定偏移读取块并返回块内所有行
func ChunkRead(f *os.File, offset int64, size int, bufPool *sync.Pool) []string {
buf := bufPool.Get().([]byte)[:size] // 从Pool获取缓冲区并调整大小
n, err := f.ReadAt(buf, offset) // 从指定偏移读取数据到缓冲区
if err != nil && err != io.EOF {
log.Fatal(err)
}
buf = buf[:n] // 调整切片以匹配实际读取的字节数
var lines []string
var lineStart int
for i, b := range buf {
if b == '\n' {
line := string(buf[lineStart:i]) // 不包括换行符
lines = append(lines, line)
lineStart = i + 1 // 设置下一行的开始
}
}
if lineStart < len(buf) { // 处理块末尾的行
line := string(buf[lineStart:])
lines = append(lines, line)
}
bufPool.Put(buf) // 读取完成后将缓冲区放回Pool
return lines
}
// 并行读取文件并将每个块的行发送到指定通道
// filePath 文件路径
// ChunkSizeMB 分块的大小单位MB设置为0时使用默认100MB,设置过大反而不利,视情调整
// MaxGoroutine 并发读取分块的数量设置为0时使用CPU核心数
// linesCh用于接收返回结果的通道。
func ParallelChunkRead(filePath string, linesCh chan<- []string, ChunkSizeMB, MaxGoroutine int) {
if ChunkSizeMB == 0 {
ChunkSizeMB = 100
}
ChunkSize := ChunkSizeMB * 1024 * 1024
// 内存复用
bufPool := sync.Pool{
New: func() interface{} {
return make([]byte, 0, ChunkSize)
},
}
if MaxGoroutine == 0 {
MaxGoroutine = runtime.NumCPU() // 设置为0时使用CPU核心数
}
f, err := os.Open(filePath)
if err != nil {
log.Fatalf("failed to open file: %v", err)
}
defer f.Close()
info, err := f.Stat()
if err != nil {
log.Fatalf("failed to get file info: %v", err)
}
wg := sync.WaitGroup{}
chunkOffsetCh := make(chan int64, MaxGoroutine)
// 分配工作
go func() {
for i := int64(0); i < info.Size(); i += int64(ChunkSize) {
chunkOffsetCh <- i
}
close(chunkOffsetCh)
}()
// 启动工作协程
for i := 0; i < MaxGoroutine; i++ {
wg.Add(1)
go func() {
for chunkOffset := range chunkOffsetCh {
linesCh <- ChunkRead(f, chunkOffset, ChunkSize, &bufPool)
}
wg.Done()
}()
}
// 等待所有解析完成后关闭行通道
wg.Wait()
close(linesCh)
}

View File

@@ -1,6 +1,7 @@
package fileutil
import (
"io"
"os"
"testing"
@@ -294,7 +295,11 @@ func TestSha(t *testing.T) {
assert := internal.NewAssert(t, "TestSha")
sha1, err := Sha("./testdata/test.txt", 1)
assert.IsNil(err)
sha256, err := Sha("./testdata/test.txt", 256)
assert.IsNil(err)
sha512, err := Sha("./testdata/test.txt", 512)
assert.IsNil(err)
@@ -337,6 +342,31 @@ func TestWriteCsvFile(t *testing.T) {
assert.Equal("Lili", content[0][0])
}
func TestWriteMapsToCsv(t *testing.T) {
assert := internal.NewAssert(t, "TestWriteMapsToCSV")
csvFilePath := "./testdata/test4.csv"
records := []map[string]interface{}{
{"Name": "Lili", "Age": "22", "Gender": "female"},
{"Name": "Jim", "Age": "21", "Gender": "male"},
}
headers := []string{"Name", "Age", "Gender"}
err := WriteMapsToCsv(csvFilePath, records, false, ';', headers)
assert.IsNil(err)
content, err := ReadCsvFile(csvFilePath, ';')
assert.IsNil(err)
assert.Equal(3, len(content))
assert.Equal(3, len(content[0]))
assert.Equal("Lili", content[1][0])
assert.Equal("22", content[1][1])
assert.Equal("female", content[1][2])
}
func TestWriteStringToFile(t *testing.T) {
assert := internal.NewAssert(t, "TestWriteStringToFile")
@@ -401,3 +431,21 @@ func TestWriteBytesToFile(t *testing.T) {
os.Remove(filepath)
}
func TestReadFile(t *testing.T) {
reader, close, err := ReadFile("https://httpbin.org/robots.txt")
if err != nil {
t.Fail()
}
defer close()
dat, err := io.ReadAll(reader)
if err != nil {
t.Fail()
}
want := `User-agent: *
Disallow: /deny
`
internal.NewAssert(t, "TestReadFile").Equal(want, string(dat))
}

View File

@@ -1,2 +1,3 @@
Lili,22,female
Jim,21,male
1 Lili 22 female
2 Jim 21 male
3

3
fileutil/testdata/test4.csv vendored Normal file
View File

@@ -0,0 +1,3 @@
Name;Age;Gender
Lili;22;female
Jim;21;male
1 Name Age Gender
2 Lili 22 female
3 Jim 21 male

View File

@@ -8,41 +8,45 @@ import (
"encoding/json"
"fmt"
"io"
"math"
"strconv"
"strings"
"unicode"
"github.com/duke-git/lancet/convertor"
"github.com/duke-git/lancet/strutil"
"github.com/duke-git/lancet/validator"
)
// Comma add comma to a number value by every 3 numbers from right. ahead by symbol char.
// Comma add comma to a number value by every 3 numbers from right. ahead by prefix symbol char.
// if value is invalid number string eg "aa", return empty string
// Comma("12345", "$") => "$12,345", Comma(12345, "$") => "$12,345"
func Comma(value interface{}, symbol string) string {
if validator.IsInt(value) {
v, err := convertor.ToInt(value)
if err != nil {
return ""
}
return symbol + commaInt(v)
}
func Comma(value interface{}, prefixSymbol string) string {
numString := convertor.ToString(value)
if validator.IsFloat(value) {
v, err := convertor.ToFloat(value)
if err != nil {
return ""
}
return symbol + commaFloat(v)
}
if strutil.IsString(value) {
v := fmt.Sprintf("%v", value)
if validator.IsNumberStr(v) {
return symbol + commaStr(v)
}
_, err := strconv.ParseFloat(numString, 64)
if err != nil {
return ""
}
return ""
isNegative := strings.HasPrefix(numString, "-")
if isNegative {
numString = numString[1:]
}
index := strings.Index(numString, ".")
if index == -1 {
index = len(numString)
}
for index > 3 {
index -= 3
numString = numString[:index] + "," + numString[index:]
}
if isNegative {
numString = "-" + numString
}
return prefixSymbol + numString
}
// Pretty data to JSON string.
@@ -177,3 +181,50 @@ func ParseDecimalBytes(size string) (uint64, error) {
func ParseBinaryBytes(size string) (uint64, error) {
return parseBytes(size, "binary")
}
// see https://github.com/dustin/go-humanize/blob/master/bytes.go
func parseBytes(s string, kind string) (uint64, error) {
lastDigit := 0
hasComma := false
for _, r := range s {
if !(unicode.IsDigit(r) || r == '.' || r == ',') {
break
}
if r == ',' {
hasComma = true
}
lastDigit++
}
num := s[:lastDigit]
if hasComma {
num = strings.Replace(num, ",", "", -1)
}
f, err := strconv.ParseFloat(num, 64)
if err != nil {
return 0, err
}
extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
if kind == "decimal" {
if m, ok := decimalByteMap[extra]; ok {
f *= float64(m)
if f >= math.MaxUint64 {
return 0, fmt.Errorf("too large: %v", s)
}
return uint64(f), nil
}
} else {
if m, ok := binaryByteMap[extra]; ok {
f *= float64(m)
if f >= math.MaxUint64 {
return 0, fmt.Errorf("too large: %v", s)
}
return uint64(f), nil
}
}
return 0, fmt.Errorf("unhandled size name: %v", extra)
}

View File

@@ -1,134 +0,0 @@
package formatter
import (
"bytes"
"fmt"
"math"
"strconv"
"strings"
"unicode"
)
// see https://github.com/dustin/go-humanize/blob/master/comma.go
func commaInt(v int64) string {
sign := ""
// Min int64 can't be negated to a usable value, so it has to be special cased.
if v == math.MinInt64 {
return "-9,223,372,036,854,775,808"
}
if v < 0 {
sign = "-"
v = 0 - v
}
parts := []string{"", "", "", "", "", "", ""}
j := len(parts) - 1
for v > 999 {
parts[j] = strconv.FormatInt(v%1000, 10)
switch len(parts[j]) {
case 2:
parts[j] = "0" + parts[j]
case 1:
parts[j] = "00" + parts[j]
}
v = v / 1000
j--
}
parts[j] = strconv.Itoa(int(v))
return sign + strings.Join(parts[j:], ",")
}
func commaFloat(v float64) string {
buf := &bytes.Buffer{}
if v < 0 {
buf.Write([]byte{'-'})
v = 0 - v
}
comma := []byte{','}
parts := strings.Split(strconv.FormatFloat(v, 'f', -1, 64), ".")
pos := 0
if len(parts[0])%3 != 0 {
pos += len(parts[0]) % 3
buf.WriteString(parts[0][:pos])
buf.Write(comma)
}
for ; pos < len(parts[0]); pos += 3 {
buf.WriteString(parts[0][pos : pos+3])
buf.Write(comma)
}
buf.Truncate(buf.Len() - 1)
if len(parts) > 1 {
buf.Write([]byte{'.'})
buf.WriteString(parts[1])
}
return buf.String()
}
func commaStr(s string) string {
dotIndex := strings.Index(s, ".")
if dotIndex != -1 {
return commaStrRecursive(s[:dotIndex]) + s[dotIndex:]
}
return commaStrRecursive(s)
}
func commaStrRecursive(s string) string {
if len(s) <= 3 {
return s
}
return commaStrRecursive(s[:len(s)-3]) + "," + commaStrRecursive(s[len(s)-3:])
}
// see https://github.com/dustin/go-humanize/blob/master/bytes.go
func parseBytes(s string, kind string) (uint64, error) {
lastDigit := 0
hasComma := false
for _, r := range s {
if !(unicode.IsDigit(r) || r == '.' || r == ',') {
break
}
if r == ',' {
hasComma = true
}
lastDigit++
}
num := s[:lastDigit]
if hasComma {
num = strings.Replace(num, ",", "", -1)
}
f, err := strconv.ParseFloat(num, 64)
if err != nil {
return 0, err
}
extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
if kind == "decimal" {
if m, ok := decimalByteMap[extra]; ok {
f *= float64(m)
if f >= math.MaxUint64 {
return 0, fmt.Errorf("too large: %v", s)
}
return uint64(f), nil
}
} else {
if m, ok := binaryByteMap[extra]; ok {
f *= float64(m)
if f >= math.MaxUint64 {
return 0, fmt.Errorf("too large: %v", s)
}
return uint64(f), nil
}
}
return 0, fmt.Errorf("unhandled size name: %v", extra)
}

View File

@@ -26,6 +26,10 @@ func TestComma(t *testing.T) {
assert.Equal("12,345.6789", Comma(+12345.6789, ""))
assert.Equal("12,345,678.9", Comma(12345678.9, ""))
assert.Equal("123,456,789,000", Comma(123456789000, ""))
assert.Equal("-999", Comma(-999, ""))
assert.Equal("-1,000", Comma(-1000, ""))
assert.Equal("-1,234,567", Comma(-1234567, ""))
}
func TestPretty(t *testing.T) {

View File

@@ -6,6 +6,7 @@ package function
import (
"reflect"
"sync"
"time"
)
@@ -75,6 +76,7 @@ func Delay(delay time.Duration, fn interface{}, args ...interface{}) {
}
// Debounced creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.
// Deprecated: Use Debounce function instead.
func Debounced(fn func(), duration time.Duration) func() {
// Catch programming error while constructing the closure
mustBeFunction(fn)
@@ -94,6 +96,74 @@ func Debounced(fn func(), duration time.Duration) func() {
return func() { timer.Reset(duration) }
}
// Debounce creates a debounced version of the provided function.
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func()) {
var (
timer *time.Timer
mu sync.Mutex
)
debouncedFn = func() {
mu.Lock()
defer mu.Unlock()
if timer != nil {
timer.Stop()
}
timer = time.AfterFunc(delay, func() {
fn()
})
}
cancelFn = func() {
mu.Lock()
defer mu.Unlock()
if timer != nil {
timer.Stop()
}
}
return debouncedFn, cancelFn
}
// Throttle creates a throttled version of the provided function.
// The returned function guarantees that it will only be invoked at most once per interval.
func Throttle(fn func(), interval time.Duration) func() {
var (
timer *time.Timer
lastRun time.Time
mu sync.Mutex
)
return func() {
mu.Lock()
defer mu.Unlock()
now := time.Now()
if now.Sub(lastRun) >= interval {
fn()
lastRun = now
if timer != nil {
timer.Stop()
timer = nil
}
} else if timer == nil {
delay := interval - now.Sub(lastRun)
timer = time.AfterFunc(delay, func() {
mu.Lock()
defer mu.Unlock()
fn()
lastRun = time.Now()
timer = nil
})
}
}
}
// Schedule invoke function every duration time, util close the returned bool chan
func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool {
// Catch programming error while constructing the closure

View File

@@ -43,7 +43,7 @@ func TestBefore(t *testing.T) {
var res []int64
type cb func(args ...interface{}) []reflect.Value
appendStr := func(i int, s string, fn cb) {
appendStr := func(i int, _ string, fn cb) {
v := fn(i)
res = append(res, v[0].Int())
}
@@ -148,3 +148,145 @@ func TestPipeline(t *testing.T) {
assert.Equal(36, f(2))
}
func TestDebounce(t *testing.T) {
assert := internal.NewAssert(t, "TestDebounce")
t.Run("Single call", func(t *testing.T) {
callCount := 0
debouncedFn, _ := Debounce(func() {
callCount++
}, 500*time.Millisecond)
debouncedFn()
time.Sleep(1 * time.Second)
assert.Equal(1, callCount)
})
t.Run("Multiple calls within debounce interval", func(t *testing.T) {
callCount := 0
debouncedFn, _ := Debounce(func() {
callCount++
}, 1*time.Second)
for i := 0; i < 5; i++ {
go func(index int) {
time.Sleep(time.Duration(index) * 200 * time.Millisecond)
debouncedFn()
}(i)
}
time.Sleep(2 * time.Second)
assert.Equal(1, callCount)
})
t.Run("Immediate consecutive calls", func(t *testing.T) {
callCount := 0
debouncedFn, _ := Debounce(func() {
callCount++
}, 500*time.Millisecond)
for i := 0; i < 10; i++ {
debouncedFn()
time.Sleep(50 * time.Millisecond)
}
time.Sleep(1 * time.Second)
assert.Equal(1, callCount)
})
t.Run("Cancel calls", func(t *testing.T) {
callCount := 0
debouncedFn, cancelFn := Debounce(func() {
callCount++
}, 500*time.Millisecond)
debouncedFn()
cancelFn()
time.Sleep(1 * time.Second)
assert.Equal(0, callCount)
})
}
func TestThrottle(t *testing.T) {
assert := internal.NewAssert(t, "TestThrottle")
t.Run("Single call", func(t *testing.T) {
callCount := 0
throttledFn := Throttle(func() {
callCount++
}, 1*time.Second)
throttledFn()
time.Sleep(100 * time.Millisecond)
assert.Equal(1, callCount)
})
t.Run("Multiple calls within throttle interval", func(t *testing.T) {
callCount := 0
throttledFn := Throttle(func() {
callCount++
}, 1*time.Second)
for i := 0; i < 5; i++ {
throttledFn()
}
time.Sleep(1 * time.Second)
assert.Equal(1, callCount)
})
t.Run("Mutiple calls space out throttle interval", func(t *testing.T) {
callCount := 0
throttledFn := Throttle(func() {
callCount++
}, 500*time.Millisecond)
throttledFn()
time.Sleep(600 * time.Millisecond)
throttledFn()
time.Sleep(600 * time.Millisecond)
throttledFn()
time.Sleep(1 * time.Second)
assert.Equal(3, callCount)
})
t.Run("Call function near the end of the interval", func(t *testing.T) {
callCount := 0
throttledFn := Throttle(func() {
callCount++
}, 1*time.Second)
throttledFn()
time.Sleep(900 * time.Millisecond)
throttledFn()
time.Sleep(200 * time.Millisecond)
assert.Equal(2, callCount)
})
}

View File

@@ -91,6 +91,34 @@ func TruncRound(x float64, n int) float64 {
return res
}
// FloorToFloat round down to n decimal places.
func FloorToFloat(x float64, n int) float64 {
return math.Floor(x*math.Pow(10, float64(n))) / math.Pow(10.0, float64(n))
}
// FloorToString round down to n decimal places.
func FloorToString(x float64, n int) string {
tmp := math.Pow(10.0, float64(n))
x *= tmp
x = math.Floor(x)
res := strconv.FormatFloat(x/tmp, 'f', n, 64)
return res
}
// CeilToFloat round up to n decimal places.
func CeilToFloat(x float64, n int) float64 {
pow10n := math.Pow10(n)
return math.Ceil(x*pow10n) / pow10n
}
// CeilToString round up to n decimal places.
func CeilToString(x float64, n int) string {
multiplier := math.Pow(10, float64(n))
rounded := math.Ceil(x*multiplier) / multiplier
return fmt.Sprintf("%.*f", n, rounded)
}
// AngleToRadian converts angle value to radian value.
func AngleToRadian(angle float64) float64 {
radian := angle * (math.Pi / 180)

View File

@@ -180,3 +180,46 @@ func TestLog(t *testing.T) {
assert.EqualValues(3, TruncRound(Log(27, 3), 0))
assert.EqualValues(2.32, TruncRound(Log(5, 2), 2))
}
func TestFloorToFloat(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestFloorToFloat")
assert.Equal(3.14, FloorToFloat(3.14159, 2))
assert.Equal(3.141, FloorToFloat(3.14159, 3))
assert.Equal(5.0, FloorToFloat(5, 4))
assert.Equal(2.0, FloorToFloat(9/4, 2))
}
func TestFloorToString(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestFloorToString")
assert.Equal("3.14", FloorToString(3.14159, 2))
assert.Equal("3.141", FloorToString(3.14159, 3))
assert.Equal("5.0000", FloorToString(5, 4))
}
func TestCeilToFloat(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestCeilToFloat")
assert.Equal(3.15, CeilToFloat(3.14159, 2))
assert.Equal(3.142, CeilToFloat(3.14159, 3))
assert.Equal(5.0, CeilToFloat(5, 4))
assert.Equal(0.15, CeilToFloat(float64(1)/float64(7), 2))
}
func TestCeilToString(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestCeilToFloat")
assert.Equal("3.15", CeilToString(3.14159, 2))
assert.Equal("3.142", CeilToString(3.14159, 3))
assert.Equal("5.0000", CeilToString(5, 4))
assert.Equal("0.15", CeilToString(float64(1)/float64(7), 2))
}

View File

@@ -8,22 +8,49 @@ import (
crand "crypto/rand"
"fmt"
"io"
"math"
"math/rand"
"time"
"unsafe"
"github.com/duke-git/lancet/mathutil"
)
const (
NUMERAL = "0123456789"
LOWER_LETTERS = "abcdefghijklmnopqrstuvwxyz"
UPPER_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
MaximumCapacity = math.MaxInt32>>1 + 1
Numeral = "0123456789"
LowwerLetters = "abcdefghijklmnopqrstuvwxyz"
UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
SymbolChars = "!@#$%^&*()_+-=[]{}|;':\",./<>?"
)
var rn = rand.NewSource(time.Now().UnixNano())
func init() {
rand.Seed(time.Now().UnixNano())
}
// RandInt generate random int between min and max, maybe min, not be max
// RandBool generates a random boolean value (true or false).
func RandBool() bool {
return rand.Intn(2) == 1
}
// RandBoolSlice generates a random boolean slice of specified length.
func RandBoolSlice(length int) []bool {
if length <= 0 {
return []bool{}
}
result := make([]bool, length)
for i := range result {
result[i] = RandBool()
}
return result
}
// RandInt generate random int between [min, max).
func RandInt(min, max int) int {
if min == max {
return min
@@ -36,70 +63,19 @@ func RandInt(min, max int) int {
return rand.Intn(max-min) + min
}
// RandBytes generate random byte slice
func RandBytes(length int) []byte {
if length < 1 {
return []byte{}
}
b := make([]byte, length)
if _, err := io.ReadFull(crand.Reader, b); err != nil {
return nil
}
return b
}
// RandString generate random string
func RandString(length int) string {
return random(LETTERS, length)
}
// RandUpper generate a random upper case string
func RandUpper(length int) string {
return random(UPPER_LETTERS, length)
}
// RandLower generate a random lower case string
func RandLower(length int) string {
return random(LOWER_LETTERS, length)
}
// RandNumeral generate a random numeral string
func RandNumeral(length int) string {
return random(NUMERAL, length)
}
// RandNumeralOrLetter generate a random numeral or letter string
func RandNumeralOrLetter(length int) string {
return random(NUMERAL+LETTERS, length)
}
// random generate a random string based on given string range
func random(s string, length int) string {
b := make([]byte, length)
// fix: https://github.com/duke-git/lancet/issues/75
// r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := range b {
b[i] = s[rand.Int63()%int64(len(s))]
}
return string(b)
}
// UUIdV4 generate a random UUID of version 4 according to RFC 4122
func UUIdV4() (string, error) {
uuid := make([]byte, 16)
n, err := io.ReadFull(crand.Reader, uuid)
if n != len(uuid) || err != nil {
return "", err
// RandIntSlice generates a slice of random integers.
// The generated integers are between min and max (exclusive).
func RandIntSlice(length, min, max int) []int {
if length <= 0 || min > max {
return []int{}
}
uuid[8] = uuid[8]&^0xc0 | 0x80
uuid[6] = uuid[6]&^0xf0 | 0x40
result := make([]int, length)
for i := range result {
result[i] = RandInt(min, max)
}
return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil
return result
}
// RandUniqueIntSlice generate a slice of random int of length n that do not repeat.
@@ -125,3 +101,170 @@ func RandUniqueIntSlice(n, min, max int) []int {
return nums
}
// RandFloat generate random float64 number between [min, max) with specific precision.
func RandFloat(min, max float64, precision int) float64 {
if min == max {
return min
}
if max < min {
min, max = max, min
}
n := rand.Float64()*(max-min) + min
return mathutil.RoundToFloat(n, precision)
}
// RandFloats generate a slice of random float64 numbers of length n that do not repeat.
func RandFloats(n int, min, max float64, precision int) []float64 {
nums := make([]float64, n)
used := make(map[float64]struct{}, n)
for i := 0; i < n; {
r := RandFloat(min, max, precision)
if _, use := used[r]; use {
continue
}
used[r] = struct{}{}
nums[i] = r
i++
}
return nums
}
// RandBytes generate random byte slice.
// Play: https://go.dev/play/p/EkiLESeXf8d
func RandBytes(length int) []byte {
if length < 1 {
return []byte{}
}
b := make([]byte, length)
if _, err := io.ReadFull(crand.Reader, b); err != nil {
return nil
}
return b
}
// RandString generate random alpha string of specified length.
func RandString(length int) string {
return random(Letters, length)
}
// RandString generate a slice of random string of length strLen based on charset.
// chartset should be one of the following: random.Numeral, random.LowwerLetters, random.UpperLetters
// random.Letters, random.SymbolChars, random.AllChars. or a combination of them.
func RandStringSlice(charset string, sliceLen, strLen int) []string {
if sliceLen <= 0 || strLen <= 0 {
return []string{}
}
result := make([]string, sliceLen)
for i := range result {
result[i] = random(charset, strLen)
}
return result
}
// RandUpper generate a random upper case string of specified length.
func RandUpper(length int) string {
return random(UpperLetters, length)
}
// RandLower generate a random lower case string of specified length.
func RandLower(length int) string {
return random(LowwerLetters, length)
}
// RandNumeral generate a random numeral string of specified length.
func RandNumeral(length int) string {
return random(Numeral, length)
}
// RandNumeralOrLetter generate a random numeral or alpha string of specified length.
func RandNumeralOrLetter(length int) string {
return random(Numeral+Letters, length)
}
// RandSymbolChar generate a random symbol char of specified length.
// symbol chars: !@#$%^&*()_+-=[]{}|;':\",./<>?.
func RandSymbolChar(length int) string {
return random(SymbolChars, length)
}
// nearestPowerOfTwo 返回一个大于等于cap的最近的2的整数次幂参考java8的hashmap的tableSizeFor函数
func nearestPowerOfTwo(cap int) int {
n := cap - 1
n |= n >> 1
n |= n >> 2
n |= n >> 4
n |= n >> 8
n |= n >> 16
if n < 0 {
return 1
} else if n >= MaximumCapacity {
return MaximumCapacity
}
return n + 1
}
// random generate a random string based on given string range.
func random(s string, length int) string {
// 仿照strings.Builder
// 创建一个长度为 length 的字节切片
bytes := make([]byte, length)
strLength := len(s)
if strLength <= 0 {
return ""
} else if strLength == 1 {
for i := 0; i < length; i++ {
bytes[i] = s[0]
}
return *(*string)(unsafe.Pointer(&bytes))
}
// s的字符需要使用多少个比特位数才能表示完
// letterIdBits := int(math.Ceil(math.Log2(strLength))),下面比上面的代码快
letterIdBits := int(math.Log2(float64(nearestPowerOfTwo(strLength))))
// 最大的字母id掩码
var letterIdMask int64 = 1<<letterIdBits - 1
// 可用次数的最大值
letterIdMax := 63 / letterIdBits
// 循环生成随机字符串
for i, cache, remain := length-1, rn.Int63(), letterIdMax; i >= 0; {
// 检查随机数生成器是否用尽所有随机数
if remain == 0 {
cache, remain = rn.Int63(), letterIdMax
}
// 从可用字符的字符串中随机选择一个字符
if idx := int(cache & letterIdMask); idx < strLength {
bytes[i] = s[idx]
i--
}
// 右移比特位数,为下次选择字符做准备
cache >>= letterIdBits
remain--
}
// 仿照strings.Builder用unsafe包返回一个字符串避免拷贝
// 将字节切片转换为字符串并返回
return *(*string)(unsafe.Pointer(&bytes))
}
// UUIdV4 generate a random UUID of version 4 according to RFC 4122.
func UUIdV4() (string, error) {
uuid := make([]byte, 16)
n, err := io.ReadFull(crand.Reader, uuid)
if n != len(uuid) || err != nil {
return "", err
}
uuid[8] = uuid[8]&^0xc0 | 0x80
uuid[6] = uuid[6]&^0xf0 | 0x40
return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil
}

View File

@@ -6,6 +6,7 @@ import (
"testing"
"github.com/duke-git/lancet/internal"
"github.com/duke-git/lancet/validator"
)
func TestRandString(t *testing.T) {
@@ -138,3 +139,92 @@ func hasDuplicate(arr []int) bool {
}
return false
}
func TestRandBool(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRandBool")
result := RandBool()
assert.Equal(true, result == true || result == false)
}
func TestRandBoolSlice(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRandBoolSlice")
t.Run("empty slice", func(t *testing.T) {
bools := RandBoolSlice(-1)
assert.Equal([]bool{}, bools)
bools = RandBoolSlice(0)
assert.Equal([]bool{}, bools)
})
t.Run("random bool slice", func(t *testing.T) {
bools := RandBoolSlice(6)
assert.Equal(6, len(bools))
for _, b := range bools {
assert.Equal(true, b == true || b == false)
}
})
}
func TestRandStringSlice(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRandStringSlice")
t.Run("empty slice", func(t *testing.T) {
strs := RandStringSlice(Letters, -1, -1)
assert.Equal([]string{}, strs)
strs = RandStringSlice(Letters, 0, 0)
assert.Equal([]string{}, strs)
strs = RandStringSlice(Letters, -1, 0)
assert.Equal([]string{}, strs)
strs = RandStringSlice(Letters, 0, -1)
assert.Equal([]string{}, strs)
strs = RandStringSlice(Letters, 1, 0)
assert.Equal([]string{}, strs)
strs = RandStringSlice(Letters, 0, 1)
assert.Equal([]string{}, strs)
})
t.Run("random string slice", func(t *testing.T) {
strs := RandStringSlice(Letters, 4, 6)
assert.Equal(4, len(strs))
for _, s := range strs {
assert.Equal(true, validator.IsAlpha(s))
assert.Equal(6, len(s))
}
})
}
func TestRandIntSlice(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRandIntSlice")
t.Run("empty slice", func(t *testing.T) {
numbers := RandIntSlice(-1, 1, 5)
assert.Equal([]int{}, numbers)
numbers = RandIntSlice(0, 1, 5)
assert.Equal([]int{}, numbers)
numbers = RandIntSlice(3, 5, 1)
assert.Equal([]int{}, numbers)
})
t.Run("random int slice", func(t *testing.T) {
numbers := RandIntSlice(5, 1, 1)
assert.Equal([]int{1, 1, 1, 1, 1}, numbers)
numbers = RandIntSlice(5, 1, 5)
assert.Equal(5, len(numbers))
})
}

View File

@@ -5,14 +5,20 @@
package strutil
import (
"reflect"
"errors"
"regexp"
"strings"
"time"
"unicode"
"unicode/utf8"
"unsafe"
"math/rand"
)
// used in `Shuffle` function
var rng = rand.New(rand.NewSource(int64(time.Now().UnixNano())))
// CamelCase covert string to camelCase string.
// non letters and numbers will be ignored
// eg. "Foo-#1😄$_%^&*(1bar" => "foo11Bar"
@@ -121,37 +127,45 @@ func UpperSnakeCase(s string) string {
// Before create substring in source string before position when char first appear
func Before(s, char string) string {
if s == "" || char == "" {
i := strings.Index(s, char)
if s == "" || char == "" || i == -1 {
return s
}
i := strings.Index(s, char)
return s[0:i]
}
// BeforeLast create substring in source string before position when char last appear
func BeforeLast(s, char string) string {
if s == "" || char == "" {
i := strings.LastIndex(s, char)
if s == "" || char == "" || i == -1 {
return s
}
i := strings.LastIndex(s, char)
return s[0:i]
}
// After create substring in source string after position when char first appear
func After(s, char string) string {
if s == "" || char == "" {
i := strings.Index(s, char)
if s == "" || char == "" || i == -1 {
return s
}
i := strings.Index(s, char)
return s[i+len(char):]
}
// AfterLast create substring in source string after position when char last appear
func AfterLast(s, char string) string {
if s == "" || char == "" {
i := strings.LastIndex(s, char)
if s == "" || char == "" || i == -1 {
return s
}
i := strings.LastIndex(s, char)
return s[i+len(char):]
}
@@ -342,14 +356,10 @@ func RemoveNonPrintable(str string) string {
// StringToBytes converts a string to byte slice without a memory allocation.
func StringToBytes(str string) (b []byte) {
sh := *(*reflect.StringHeader)(unsafe.Pointer(&str))
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len
return b
return *(*[]byte)(unsafe.Pointer(&str))
}
// BytesToString converts a byte slice to string without a memory allocation.
// Play: todo
func BytesToString(bytes []byte) string {
return *(*string)(unsafe.Pointer(&bytes))
}
@@ -522,3 +532,141 @@ func RemoveWhiteSpace(str string, repalceAll bool) string {
return strings.TrimSpace(str)
}
// SubInBetween return substring between the start and end position(excluded) of source string.
func SubInBetween(str string, start string, end string) string {
if _, after, ok := strings.Cut(str, start); ok {
if before, _, ok := strings.Cut(after, end); ok {
return before
}
}
return ""
}
// HammingDistance calculates the Hamming distance between two strings.
// The Hamming distance is the number of positions at which the corresponding symbols are different.
// This func returns an error if the input strings are of unequal lengths.
func HammingDistance(a, b string) (int, error) {
if len(a) != len(b) {
return -1, errors.New("a length and b length are unequal")
}
ar := []rune(a)
br := []rune(b)
var distance int
for i, codepoint := range ar {
if codepoint != br[i] {
distance++
}
}
return distance, nil
}
// Concat uses the strings.Builder to concatenate the input strings.
// - `length` is the expected length of the concatenated string.
// - if you are unsure about the length of the string to be concatenated, please pass 0 or a negative number.
func Concat(length int, str ...string) string {
if len(str) == 0 {
return ""
}
sb := strings.Builder{}
if length <= 0 {
sb.Grow(len(str[0]) * len(str))
} else {
sb.Grow(length)
}
for _, s := range str {
sb.WriteString(s)
}
return sb.String()
}
// Ellipsis truncates a string to a specified length and appends an ellipsis.
func Ellipsis(str string, length int) string {
str = strings.TrimSpace(str)
if length <= 0 {
return ""
}
runes := []rune(str)
if len(runes) <= length {
return str
}
return string(runes[:length]) + "..."
}
// Shuffle the order of characters of given string.
func Shuffle(str string) string {
runes := []rune(str)
for i := len(runes) - 1; i > 0; i-- {
j := rng.Intn(i + 1)
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
// Rotate rotates the string by the specified number of characters.
func Rotate(str string, shift int) string {
if shift == 0 {
return str
}
runes := []rune(str)
length := len(runes)
if length == 0 {
return str
}
shift = shift % length
if shift < 0 {
shift = length + shift
}
var sb strings.Builder
sb.Grow(length)
sb.WriteString(string(runes[length-shift:]))
sb.WriteString(string(runes[:length-shift]))
return sb.String()
}
// TemplateReplace replaces the placeholders in the template string with the corresponding values in the data map.
// The placeholders are enclosed in curly braces, e.g. {key}.
// for example, the template string is "Hello, {name}!", and the data map is {"name": "world"},
// the result will be "Hello, world!".
func TemplateReplace(template string, data map[string]string) string {
re := regexp.MustCompile(`\{(\w+)\}`)
result := re.ReplaceAllStringFunc(template, func(s string) string {
key := strings.Trim(s, "{}")
if val, ok := data[key]; ok {
return val
}
return s
})
result = strings.ReplaceAll(result, "{{", "{")
result = strings.ReplaceAll(result, "}}", "}")
return result
}
// RegexMatchAllGroups Matches all subgroups in a string using a regular expression and returns the result.
func RegexMatchAllGroups(pattern, str string) [][]string {
re := regexp.MustCompile(pattern)
matches := re.FindAllStringSubmatch(str, -1)
return matches
}

View File

@@ -207,6 +207,7 @@ func TestBefore(t *testing.T) {
assert := internal.NewAssert(t, "TestBefore")
assert.Equal("lancet", Before("lancet", ""))
assert.Equal("lancet", Before("lancet", "abcdef"))
assert.Equal("github.com", Before("github.com/test/lancet", "/"))
assert.Equal("github.com/", Before("github.com/test/lancet", "test"))
}
@@ -215,16 +216,42 @@ func TestBeforeLast(t *testing.T) {
assert := internal.NewAssert(t, "TestBeforeLast")
assert.Equal("lancet", BeforeLast("lancet", ""))
assert.Equal("lancet", BeforeLast("lancet", "abcdef"))
assert.Equal("github.com/test", BeforeLast("github.com/test/lancet", "/"))
assert.Equal("github.com/test/", BeforeLast("github.com/test/test/lancet", "test"))
assert.NotEqual("github.com/", BeforeLast("github.com/test/test/lancet", "test"))
}
func TestHammingDistance(t *testing.T) {
assert := internal.NewAssert(t, "HammingDistance")
hd := func(a, b string) int {
c, _ := HammingDistance(a, b)
return c
}
assert.Equal(0, hd(" ", " "))
assert.Equal(1, hd(" ", "c"))
assert.Equal(1, hd("a", "d"))
assert.Equal(1, hd("a", " "))
assert.Equal(1, hd("a", "f"))
assert.Equal(0, hd("", ""))
assert.Equal(-1, hd("abc", "ab"))
assert.Equal(3, hd("abc", "def"))
assert.Equal(-1, hd("kitten", "sitting"))
assert.Equal(1, hd("ö", "ü"))
assert.Equal(0, hd("日本語", "日本語"))
assert.Equal(3, hd("日本語", "語日本"))
}
func TestAfter(t *testing.T) {
assert := internal.NewAssert(t, "TestAfter")
assert.Equal("lancet", After("lancet", ""))
assert.Equal("lancet", After("lancet", "abcdef"))
assert.Equal("test/lancet", After("github.com/test/lancet", "/"))
assert.Equal("/lancet", After("github.com/test/lancet", "test"))
}
@@ -233,6 +260,7 @@ func TestAfterLast(t *testing.T) {
assert := internal.NewAssert(t, "TestAfterLast")
assert.Equal("lancet", AfterLast("lancet", ""))
assert.Equal("lancet", AfterLast("lancet", "abcdef"))
assert.Equal("lancet", AfterLast("github.com/test/lancet", "/"))
assert.Equal("/lancet", AfterLast("github.com/test/lancet", "test"))
assert.Equal("/lancet", AfterLast("github.com/test/test/lancet", "test"))
@@ -465,3 +493,178 @@ func TestRemoveWhiteSpace(t *testing.T) {
assert.Equal("helloworld", RemoveWhiteSpace(str, true))
assert.Equal("hello world", RemoveWhiteSpace(str, false))
}
func TestSubInBetween(t *testing.T) {
assert := internal.NewAssert(t, "TestSubInBetween")
str := "abcde"
assert.Equal("", SubInBetween(str, "", ""))
assert.Equal("ab", SubInBetween(str, "", "c"))
assert.Equal("bc", SubInBetween(str, "a", "d"))
assert.Equal("", SubInBetween(str, "a", ""))
assert.Equal("", SubInBetween(str, "a", "f"))
}
func TestConcat(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestConcat")
assert.Equal("", Concat(0))
assert.Equal("a", Concat(1, "a"))
assert.Equal("ab", Concat(2, "a", "b"))
assert.Equal("abc", Concat(3, "a", "b", "c"))
assert.Equal("abc", Concat(3, "a", "", "b", "c", ""))
assert.Equal("你好,世界!", Concat(0, "你好", "", "", "世界!", ""))
assert.Equal("Hello World!", Concat(0, "Hello", " Wo", "r", "ld!", ""))
}
func TestEllipsis(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEllipsis")
tests := []struct {
input string
length int
expected string
}{
{"", 0, ""},
{"hello world", 0, ""},
{"hello world", -1, ""},
{"hello world", 5, "hello..."},
{"hello world", 11, "hello world"},
{"你好,世界!", 2, "你好..."},
{"😀😃😄😁😆", 3, "😀😃😄..."},
{"This is a test.", 10, "This is a ..."},
}
for _, tt := range tests {
assert.Equal(tt.expected, Ellipsis(tt.input, tt.length))
}
}
func TestShuffle(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestShuffle")
assert.Equal("", Shuffle(""))
assert.Equal("a", Shuffle("a"))
str := "hello"
shuffledStr := Shuffle(str)
assert.Equal(5, len(shuffledStr))
}
func TestRotate(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRotate")
tests := []struct {
input string
shift int
expected string
}{
{"", 1, ""},
{"a", 0, "a"},
{"a", 1, "a"},
{"a", -1, "a"},
{"Hello", -2, "lloHe"},
{"Hello", 1, "oHell"},
{"Hello, world!", 3, "ld!Hello, wor"},
}
for _, tt := range tests {
assert.Equal(tt.expected, Rotate(tt.input, tt.shift))
}
}
func TestTemplateReplace(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestTemplateReplace")
t.Run("basic", func(t *testing.T) {
template := `Hello, my name is {name}, I'm {age} years old.`
data := map[string]string{
"name": "Bob",
"age": "20",
}
expected := `Hello, my name is Bob, I'm 20 years old.`
result := TemplateReplace(template, data)
assert.Equal(expected, result)
})
t.Run("not found", func(t *testing.T) {
template := `Hello, my name is {name}, I'm {age} years old.`
data := map[string]string{
"name": "Bob",
}
expected := `Hello, my name is Bob, I'm {age} years old.`
result := TemplateReplace(template, data)
assert.Equal(expected, result)
})
t.Run("brackets", func(t *testing.T) {
template := `Hello, my name is {name}, I'm {{age}} years old.`
data := map[string]string{
"name": "Bob",
"age": "20",
}
expected := `Hello, my name is Bob, I'm {20} years old.`
result := TemplateReplace(template, data)
assert.Equal(expected, result)
})
}
func TestRegexMatchAllGroups(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRegexMatchAllGroups")
tests := []struct {
pattern string
str string
expected [][]string
}{
{
pattern: `(\w+\.+\w+)@(\w+)\.(\w+)`,
str: "Emails: john.doe@example.com and jane.doe@example.com",
expected: [][]string{{"john.doe@example.com", "john.doe", "example", "com"}, {"jane.doe@example.com", "jane.doe", "example", "com"}},
},
{
pattern: `(\d+)`,
str: "No numbers here!",
expected: nil,
},
{
pattern: `(\d{3})-(\d{2})-(\d{4})`,
str: "My number is 123-45-6789",
expected: [][]string{{"123-45-6789", "123", "45", "6789"}},
},
{
pattern: `(\w+)\s(\d+)`,
str: "Item A 123, Item B 456",
expected: [][]string{{"A 123", "A", "123"}, {"B 456", "B", "456"}},
},
{
pattern: `(\d{2})-(\d{2})-(\d{4})`,
str: "Dates: 01-01-2020, 12-31-1999",
expected: [][]string{{"01-01-2020", "01", "01", "2020"}, {"12-31-1999", "12", "31", "1999"}},
},
}
for _, tt := range tests {
result := RegexMatchAllGroups(tt.pattern, tt.str)
assert.Equal(tt.expected, result)
}
}

View File

@@ -16,18 +16,26 @@ import (
)
var (
alphaMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]+$`)
letterRegexMatcher *regexp.Regexp = regexp.MustCompile(`[a-zA-Z]`)
intStrMatcher *regexp.Regexp = regexp.MustCompile(`^[\+-]?\d+$`)
urlMatcher *regexp.Regexp = regexp.MustCompile(`^((ftp|http|https?):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-z\x{00a1}-\x{ffff}]{2,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$`)
dnsMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$`)
emailMatcher *regexp.Regexp = regexp.MustCompile(`\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*`)
chineseMobileMatcher *regexp.Regexp = regexp.MustCompile(`^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$`)
chineseIdMatcher *regexp.Regexp = regexp.MustCompile(`^[1-9]\d{5}(18|19|20|21|22)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$`)
chineseMatcher *regexp.Regexp = regexp.MustCompile("[\u4e00-\u9fa5]")
chinesePhoneMatcher *regexp.Regexp = regexp.MustCompile(`\d{3}-\d{8}|\d{4}-\d{7}`)
creditCardMatcher *regexp.Regexp = regexp.MustCompile(`^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11}|6[27][0-9]{14})$`)
base64Matcher *regexp.Regexp = regexp.MustCompile(`^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$`)
alphaMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]+$`)
letterRegexMatcher *regexp.Regexp = regexp.MustCompile(`[a-zA-Z]`)
intStrMatcher *regexp.Regexp = regexp.MustCompile(`^[\+-]?\d+$`)
urlMatcher *regexp.Regexp = regexp.MustCompile(`^((ftp|http|https?):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-z\x{00a1}-\x{ffff}]{2,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$`)
dnsMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$`)
emailMatcher *regexp.Regexp = regexp.MustCompile(`\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*`)
chineseMobileMatcher *regexp.Regexp = regexp.MustCompile(`^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$`)
chineseIdMatcher *regexp.Regexp = regexp.MustCompile(`^[1-9]\d{5}(18|19|20|21|22)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$`)
chineseMatcher *regexp.Regexp = regexp.MustCompile("[\u4e00-\u9fa5]")
chinesePhoneMatcher *regexp.Regexp = regexp.MustCompile(`\d{3}-\d{8}|\d{4}-\d{7}`)
creditCardMatcher *regexp.Regexp = regexp.MustCompile(`^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11}|6[27][0-9]{14})$`)
base64Matcher *regexp.Regexp = regexp.MustCompile(`^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$`)
base64URLMatcher *regexp.Regexp = regexp.MustCompile(`^([A-Za-z0-9_-]{4})*([A-Za-z0-9_-]{2}(==)?|[A-Za-z0-9_-]{3}=?)?$`)
binMatcher *regexp.Regexp = regexp.MustCompile(`^(0b)?[01]+$`)
hexMatcher *regexp.Regexp = regexp.MustCompile(`^(#|0x|0X)?[0-9a-fA-F]+$`)
visaMatcher *regexp.Regexp = regexp.MustCompile(`^4[0-9]{12}(?:[0-9]{3})?$`)
masterCardMatcher *regexp.Regexp = regexp.MustCompile(`^5[1-5][0-9]{14}$`)
americanExpressMatcher *regexp.Regexp = regexp.MustCompile(`^3[47][0-9]{13}$`)
unionPay *regexp.Regexp = regexp.MustCompile("^62[0-5]\\d{13,16}$")
chinaUnionPay *regexp.Regexp = regexp.MustCompile(`^62[0-9]{14,17}$`)
)
// IsAlpha checks if the string contains only letters (a-zA-Z)
@@ -362,3 +370,59 @@ func IsPrintable(str string) bool {
}
return true
}
// IsBin check if a give string is a valid binary value or not.
func IsBin(v string) bool {
return binMatcher.MatchString(v)
}
// IsHex check if a give string is a valid hexadecimal value or not.
func IsHex(v string) bool {
return hexMatcher.MatchString(v)
}
// IsBase64URL check if a give string is a valid URL-safe Base64 encoded string.
func IsBase64URL(v string) bool {
return base64URLMatcher.MatchString(v)
}
// IsJWT check if a give string is a valid JSON Web Token (JWT).
func IsJWT(v string) bool {
strings := strings.Split(v, ".")
if len(strings) != 3 {
return false
}
for _, s := range strings {
if !IsBase64URL(s) {
return false
}
}
return true
}
// IsVisa check if a give string is a valid visa card nubmer or not.
func IsVisa(v string) bool {
return visaMatcher.MatchString(v)
}
// IsMasterCard check if a give string is a valid master card nubmer or not.
func IsMasterCard(v string) bool {
return masterCardMatcher.MatchString(v)
}
// IsAmericanExpress check if a give string is a valid american expression card nubmer or not.
func IsAmericanExpress(v string) bool {
return americanExpressMatcher.MatchString(v)
}
// IsUnionPay check if a give string is a valid union pay nubmer or not.
func IsUnionPay(v string) bool {
return unionPay.MatchString(v)
}
// IsChinaUnionPay check if a give string is a valid china union pay nubmer or not.
func IsChinaUnionPay(v string) bool {
return chinaUnionPay.MatchString(v)
}

View File

@@ -448,3 +448,90 @@ func TestIsPrintable(t *testing.T) {
assert.Equal(true, IsPrintable("😄"))
assert.Equal(false, IsPrintable("\u0000"))
}
func TestIsBin(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIsBin")
assert.Equal(true, IsBin("0101"))
assert.Equal(true, IsBin("0b1101"))
assert.Equal(false, IsBin("b1101"))
assert.Equal(false, IsBin("1201"))
assert.Equal(false, IsBin(""))
}
func TestIsHex(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIsHex")
assert.Equal(true, IsHex("ABCDE"))
assert.Equal(true, IsHex("abcde"))
assert.Equal(true, IsHex("0xabcde"))
assert.Equal(true, IsHex("0Xabcde"))
assert.Equal(true, IsHex("#abcde"))
assert.Equal(false, IsHex("cdfeg"))
assert.Equal(false, IsHex("0xcdfeg"))
assert.Equal(false, IsHex(""))
}
func TestIsBase64URL(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIsBase64URL")
assert.Equal(true, IsBase64URL("SAGsbG8sIHdvcmxkIQ"))
assert.Equal(true, IsBase64URL("SAGsbG8sIHdvcmxkIQ=="))
assert.Equal(false, IsBase64URL("SAGsbG8sIHdvcmxkIQ="))
assert.Equal(false, IsBase64URL("SAGsbG8sIHdvcmxkIQ==="))
// assert.Equal(false, IsBase64URL(""))
}
func TestIsJWT(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIsJWT")
assert.Equal(true, IsJWT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibWVzc2FnZSI6IlB1dGluIGlzIGFic29sdXRlIHNoaXQiLCJpYXQiOjE1MTYyMzkwMjJ9.wkLWA5GtCpWdxNOrRse8yHZgORDgf8TpJp73WUQb910"))
assert.Equal(false, IsJWT("abc"))
}
func TestIsVisa(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIsVisa")
assert.Equal(true, IsVisa("4111111111111111"))
assert.Equal(false, IsVisa("123"))
}
func TestIsMasterCard(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIsMasterCard")
assert.Equal(true, IsMasterCard("5425233430109903"))
assert.Equal(false, IsMasterCard("4111111111111111"))
}
func TestIsAmericanExpress(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIsAmericanExpress")
assert.Equal(true, IsAmericanExpress("342883359122187"))
assert.Equal(false, IsAmericanExpress("3782822463100007"))
}
func TestIsUnionPay(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIsUnionPay")
assert.Equal(true, IsUnionPay("6221263430109903"))
assert.Equal(false, IsUnionPay("3782822463100007"))
}
func TestIsChinaUnionPay(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIsChinaUnionPay")
assert.Equal(true, IsChinaUnionPay("6250941006528599"))
assert.Equal(false, IsChinaUnionPay("3782822463100007"))
}