mirror of
https://github.com/tiennm99/zfoo.git
synced 2026-10-16 02:15:51 +00:00
feat[golang]: 添加一些go的常用工具类
This commit is contained in:
1 parent
71cd7ccdbe
commit
770c2f3380
15 files changed
+4571
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -18,7 +18,9 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
@@ -158,3 +160,214 @@ func HexToBye(hex string) []byte {
|
|||||||
}
|
}
|
||||||
return slice
|
return slice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Bytes struct
|
||||||
|
Bytes struct{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// binary units (IEC 60027)
|
||||||
|
const (
|
||||||
|
_ = 1.0 << (10 * iota) // ignore first value by assigning to blank identifier
|
||||||
|
KiB
|
||||||
|
MiB
|
||||||
|
GiB
|
||||||
|
TiB
|
||||||
|
PiB
|
||||||
|
EiB
|
||||||
|
)
|
||||||
|
|
||||||
|
// decimal units (SI international system of units)
|
||||||
|
const (
|
||||||
|
KB = 1000
|
||||||
|
MB = KB * 1000
|
||||||
|
GB = MB * 1000
|
||||||
|
TB = GB * 1000
|
||||||
|
PB = TB * 1000
|
||||||
|
EB = PB * 1000
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
patternBinary = regexp.MustCompile(`(?i)^(-?\d+(?:\.\d+)?)\s?([KMGTPE]iB?)$`)
|
||||||
|
patternDecimal = regexp.MustCompile(`(?i)^(-?\d+(?:\.\d+)?)\s?([KMGTPE]B?|B?)$`)
|
||||||
|
global = New()
|
||||||
|
)
|
||||||
|
|
||||||
|
// New creates a Bytes instance.
|
||||||
|
func New() *Bytes {
|
||||||
|
return &Bytes{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format formats bytes integer to human readable string according to IEC 60027.
|
||||||
|
// For example, 31323 bytes will return 30.59KB.
|
||||||
|
func (b *Bytes) Format(value int64) string {
|
||||||
|
return b.FormatBinary(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatBinary formats bytes integer to human readable string according to IEC 60027.
|
||||||
|
// For example, 31323 bytes will return 30.59KB.
|
||||||
|
func (*Bytes) FormatBinary(value int64) string {
|
||||||
|
multiple := ""
|
||||||
|
val := float64(value)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case value >= EiB:
|
||||||
|
val /= EiB
|
||||||
|
multiple = "EiB"
|
||||||
|
case value >= PiB:
|
||||||
|
val /= PiB
|
||||||
|
multiple = "PiB"
|
||||||
|
case value >= TiB:
|
||||||
|
val /= TiB
|
||||||
|
multiple = "TiB"
|
||||||
|
case value >= GiB:
|
||||||
|
val /= GiB
|
||||||
|
multiple = "GiB"
|
||||||
|
case value >= MiB:
|
||||||
|
val /= MiB
|
||||||
|
multiple = "MiB"
|
||||||
|
case value >= KiB:
|
||||||
|
val /= KiB
|
||||||
|
multiple = "KiB"
|
||||||
|
case value == 0:
|
||||||
|
return "0"
|
||||||
|
default:
|
||||||
|
return strconv.FormatInt(value, 10) + "B"
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%.2f%s", val, multiple)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatDecimal formats bytes integer to human readable string according to SI international system of units.
|
||||||
|
// For example, 31323 bytes will return 31.32KB.
|
||||||
|
func (*Bytes) FormatDecimal(value int64) string {
|
||||||
|
multiple := ""
|
||||||
|
val := float64(value)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case value >= EB:
|
||||||
|
val /= EB
|
||||||
|
multiple = "EB"
|
||||||
|
case value >= PB:
|
||||||
|
val /= PB
|
||||||
|
multiple = "PB"
|
||||||
|
case value >= TB:
|
||||||
|
val /= TB
|
||||||
|
multiple = "TB"
|
||||||
|
case value >= GB:
|
||||||
|
val /= GB
|
||||||
|
multiple = "GB"
|
||||||
|
case value >= MB:
|
||||||
|
val /= MB
|
||||||
|
multiple = "MB"
|
||||||
|
case value >= KB:
|
||||||
|
val /= KB
|
||||||
|
multiple = "KB"
|
||||||
|
case value == 0:
|
||||||
|
return "0"
|
||||||
|
default:
|
||||||
|
return strconv.FormatInt(value, 10) + "B"
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%.2f%s", val, multiple)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse parses human readable bytes string to bytes integer.
|
||||||
|
// For example, 6GiB (6Gi is also valid) will return 6442450944, and
|
||||||
|
// 6GB (6G is also valid) will return 6000000000.
|
||||||
|
func (b *Bytes) Parse(value string) (int64, error) {
|
||||||
|
|
||||||
|
i, err := b.ParseBinary(value)
|
||||||
|
if err == nil {
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.ParseDecimal(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseBinary parses human readable bytes string to bytes integer.
|
||||||
|
// For example, 6GiB (6Gi is also valid) will return 6442450944.
|
||||||
|
func (*Bytes) ParseBinary(value string) (i int64, err error) {
|
||||||
|
parts := patternBinary.FindStringSubmatch(value)
|
||||||
|
if len(parts) < 3 {
|
||||||
|
return 0, fmt.Errorf("error parsing value=%s", value)
|
||||||
|
}
|
||||||
|
bytesString := parts[1]
|
||||||
|
multiple := strings.ToUpper(parts[2])
|
||||||
|
bytes, err := strconv.ParseFloat(bytesString, 64)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch multiple {
|
||||||
|
case "KI", "KIB":
|
||||||
|
return int64(bytes * KiB), nil
|
||||||
|
case "MI", "MIB":
|
||||||
|
return int64(bytes * MiB), nil
|
||||||
|
case "GI", "GIB":
|
||||||
|
return int64(bytes * GiB), nil
|
||||||
|
case "TI", "TIB":
|
||||||
|
return int64(bytes * TiB), nil
|
||||||
|
case "PI", "PIB":
|
||||||
|
return int64(bytes * PiB), nil
|
||||||
|
case "EI", "EIB":
|
||||||
|
return int64(bytes * EiB), nil
|
||||||
|
default:
|
||||||
|
return int64(bytes), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseDecimal parses human readable bytes string to bytes integer.
|
||||||
|
// For example, 6GB (6G is also valid) will return 6000000000.
|
||||||
|
func (*Bytes) ParseDecimal(value string) (i int64, err error) {
|
||||||
|
parts := patternDecimal.FindStringSubmatch(value)
|
||||||
|
if len(parts) < 3 {
|
||||||
|
return 0, fmt.Errorf("error parsing value=%s", value)
|
||||||
|
}
|
||||||
|
bytesString := parts[1]
|
||||||
|
multiple := strings.ToUpper(parts[2])
|
||||||
|
bytes, err := strconv.ParseFloat(bytesString, 64)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch multiple {
|
||||||
|
case "K", "KB":
|
||||||
|
return int64(bytes * KB), nil
|
||||||
|
case "M", "MB":
|
||||||
|
return int64(bytes * MB), nil
|
||||||
|
case "G", "GB":
|
||||||
|
return int64(bytes * GB), nil
|
||||||
|
case "T", "TB":
|
||||||
|
return int64(bytes * TB), nil
|
||||||
|
case "P", "PB":
|
||||||
|
return int64(bytes * PB), nil
|
||||||
|
case "E", "EB":
|
||||||
|
return int64(bytes * EB), nil
|
||||||
|
default:
|
||||||
|
return int64(bytes), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format wraps global Bytes's Format function.
|
||||||
|
func Format(value int64) string {
|
||||||
|
return global.Format(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatBinary wraps global Bytes's FormatBinary function.
|
||||||
|
func FormatBinary(value int64) string {
|
||||||
|
return global.FormatBinary(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatDecimal wraps global Bytes's FormatDecimal function.
|
||||||
|
func FormatDecimal(value int64) string {
|
||||||
|
return global.FormatDecimal(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse wraps global Bytes's Parse function.
|
||||||
|
func Parse(value string) (int64, error) {
|
||||||
|
return global.Parse(value)
|
||||||
|
}
|
||||||
@@ -0,0 +1,348 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The zfoo Authors
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||||
|
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
package bitset
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/bits"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
// BitSet bit set
|
||||||
|
type BitSet struct {
|
||||||
|
set []byte
|
||||||
|
mu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a bit set object.
|
||||||
|
func New(init ...byte) *BitSet {
|
||||||
|
return &BitSet{set: init}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFromHex creates a bit set object from hex string.
|
||||||
|
func NewFromHex(s string) (*BitSet, error) {
|
||||||
|
init, err := hex.DecodeString(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &BitSet{set: init}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the bit bool value on the specified offset,
|
||||||
|
// and returns the value of before setting.
|
||||||
|
// NOTE:
|
||||||
|
// 0 means the 1st bit, -1 means the bottom 1th bit, -2 means the bottom 2th bit and so on;
|
||||||
|
// If offset>=len(b.set), automatically grow the bit set;
|
||||||
|
// If the bit offset is out of the left range, returns error.
|
||||||
|
func (b *BitSet) Set(offset int, value bool) (bool, error) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
size := b.size()
|
||||||
|
// 0 means the 1st bit, -1 means the bottom 1th bit,
|
||||||
|
// -2 means the bottom 2th bit and so on.
|
||||||
|
if offset < 0 {
|
||||||
|
offset += size
|
||||||
|
}
|
||||||
|
if offset < 0 {
|
||||||
|
return false, errors.New("the bit offset is out of the left range")
|
||||||
|
}
|
||||||
|
|
||||||
|
// the bit group index
|
||||||
|
gi := offset / 8
|
||||||
|
// the bit index of in the group
|
||||||
|
bi := offset % 8
|
||||||
|
|
||||||
|
// if the bit offset is out of the right range, automatically grow.
|
||||||
|
if gi >= len(b.set) {
|
||||||
|
newSet := make([]byte, gi+1)
|
||||||
|
copy(newSet, b.set)
|
||||||
|
b.set = newSet
|
||||||
|
}
|
||||||
|
|
||||||
|
gb := b.set[gi]
|
||||||
|
rOff := byte(7 - bi)
|
||||||
|
var mask byte = 1 << rOff
|
||||||
|
oldVal := gb & mask >> rOff
|
||||||
|
if (oldVal == 1) != value {
|
||||||
|
if oldVal == 1 {
|
||||||
|
b.set[gi] = gb &^ mask
|
||||||
|
} else {
|
||||||
|
b.set[gi] = gb | mask
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return oldVal == 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get gets the bit bool value on the specified offset.
|
||||||
|
// NOTE:
|
||||||
|
// 0 means the 1st bit, -1 means the bottom 1th bit, -2 means the bottom 2th bit and so on;
|
||||||
|
// If offset>=len(b.set), returns false.
|
||||||
|
func (b *BitSet) Get(offset int) bool {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
size := b.size()
|
||||||
|
// 0 means the 1st bit, -1 means the bottom 1th bit,
|
||||||
|
// -2 means the bottom 2th bit and so on.
|
||||||
|
if offset < 0 {
|
||||||
|
offset += size
|
||||||
|
}
|
||||||
|
if offset < 0 || offset >= size {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return getBit(b.set[offset/8], byte(offset%8)) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Range calls f sequentially for each bit present in the bit set.
|
||||||
|
// If f returns false, range stops the iteration.
|
||||||
|
func (b *BitSet) Range(f func(offset int, truth bool) bool) {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
size := b.size()
|
||||||
|
if size == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for offset := 0; offset < size; offset++ {
|
||||||
|
if !f(offset, getBit(b.set[offset/8], byte(offset%8)) == 1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBit(gb, offset byte) byte {
|
||||||
|
var rOff = 7 - offset
|
||||||
|
var mask byte = 1 << rOff
|
||||||
|
return gb & mask >> rOff
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count counts the amount of bit set to 1 within the specified range of the bit set.
|
||||||
|
// NOTE:
|
||||||
|
// 0 means the 1st bit, -1 means the bottom 1th bit, -2 means the bottom 2th bit and so on.
|
||||||
|
func (b *BitSet) Count(start, end int) int {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
sgi, sbi, egi, ebi, valid := b.validRange(start, end)
|
||||||
|
if !valid {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
var n int
|
||||||
|
n += bits.OnesCount8(b.set[sgi] << sbi)
|
||||||
|
for _, v := range b.set[sgi+1 : egi] {
|
||||||
|
n += bits.OnesCount8(v)
|
||||||
|
}
|
||||||
|
n += bits.OnesCount8(b.set[egi] >> (7 - ebi) << (7 - ebi))
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BitSet) validRange(start, end int) (sgi, sbi, egi, ebi uint, valid bool) {
|
||||||
|
size := b.size()
|
||||||
|
if start < 0 {
|
||||||
|
start += size
|
||||||
|
}
|
||||||
|
if start >= size {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if start < 0 {
|
||||||
|
start = 0
|
||||||
|
}
|
||||||
|
if end < 0 {
|
||||||
|
end += size
|
||||||
|
}
|
||||||
|
if end >= size {
|
||||||
|
end = size - 1
|
||||||
|
}
|
||||||
|
if start > end {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
valid = true
|
||||||
|
sgi, sbi = uint(start/8), uint(start%8)
|
||||||
|
egi, ebi = uint(end/8), uint(end%8)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not returns ^b.
|
||||||
|
func (b *BitSet) Not() *BitSet {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
rBitSet := &BitSet{
|
||||||
|
set: make([]byte, len(b.set)),
|
||||||
|
}
|
||||||
|
for i, gb := range b.set {
|
||||||
|
rBitSet.set[i] = ^gb
|
||||||
|
}
|
||||||
|
return rBitSet
|
||||||
|
}
|
||||||
|
|
||||||
|
// And returns all the "AND" bit sets.
|
||||||
|
// NOTE:
|
||||||
|
// If the bitSets are empty, returns b.
|
||||||
|
func (b *BitSet) And(bitSets ...*BitSet) *BitSet {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
if len(bitSets) == 0 {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
maxLen = len(b.set)
|
||||||
|
minLen = maxLen
|
||||||
|
currLen int
|
||||||
|
)
|
||||||
|
for _, g := range bitSets {
|
||||||
|
g.mu.RLock()
|
||||||
|
defer g.mu.RUnlock()
|
||||||
|
|
||||||
|
currLen = len(g.set)
|
||||||
|
if currLen > maxLen {
|
||||||
|
maxLen = currLen
|
||||||
|
} else if currLen < minLen {
|
||||||
|
minLen = currLen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rBitSet := &BitSet{
|
||||||
|
set: make([]byte, maxLen),
|
||||||
|
}
|
||||||
|
for i := range rBitSet.set[:minLen] {
|
||||||
|
rBitSet.set[i] = b.set[i]
|
||||||
|
for _, g := range bitSets {
|
||||||
|
rBitSet.set[i] &= g.set[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rBitSet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Or returns all the "OR" bit sets.
|
||||||
|
// NOTE:
|
||||||
|
// If the bitSets are empty, returns b.
|
||||||
|
func (b *BitSet) Or(bitSet ...*BitSet) *BitSet {
|
||||||
|
return b.op("|", bitSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Xor returns all the "XOR" bit sets.
|
||||||
|
// NOTE:
|
||||||
|
// If the bitSets are empty, returns b.
|
||||||
|
func (b *BitSet) Xor(bitSet ...*BitSet) *BitSet {
|
||||||
|
return b.op("^", bitSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AndNot returns all the "&^" bit sets.
|
||||||
|
// NOTE:
|
||||||
|
// If the bitSets are empty, returns b.
|
||||||
|
func (b *BitSet) AndNot(bitSet ...*BitSet) *BitSet {
|
||||||
|
return b.op("&^", bitSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BitSet) op(op string, bitSets []*BitSet) *BitSet {
|
||||||
|
if len(bitSets) == 0 {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
var (
|
||||||
|
maxLen, currLen = len(b.set), 0
|
||||||
|
)
|
||||||
|
for _, g := range bitSets {
|
||||||
|
g.mu.RLock()
|
||||||
|
defer g.mu.RUnlock()
|
||||||
|
currLen = len(g.set)
|
||||||
|
if currLen > maxLen {
|
||||||
|
maxLen = currLen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rBitSet := &BitSet{
|
||||||
|
set: make([]byte, maxLen),
|
||||||
|
}
|
||||||
|
copy(rBitSet.set, b.set)
|
||||||
|
for _, g := range bitSets {
|
||||||
|
for i, gb := range g.set {
|
||||||
|
switch op {
|
||||||
|
case "|":
|
||||||
|
rBitSet.set[i] = rBitSet.set[i] | gb
|
||||||
|
case "^":
|
||||||
|
rBitSet.set[i] = rBitSet.set[i] ^ gb
|
||||||
|
case "&^":
|
||||||
|
rBitSet.set[i] = rBitSet.set[i] &^ gb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rBitSet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear clears the bit set.
|
||||||
|
func (b *BitSet) Clear() {
|
||||||
|
b.mu.Lock()
|
||||||
|
for i := range b.set {
|
||||||
|
b.set[i] = 0
|
||||||
|
}
|
||||||
|
b.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size returns the bits size.
|
||||||
|
func (b *BitSet) Size() int {
|
||||||
|
b.mu.RLock()
|
||||||
|
size := b.size()
|
||||||
|
b.mu.RUnlock()
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BitSet) size() int {
|
||||||
|
size := len(b.set) * 8
|
||||||
|
if size/8 != len(b.set) {
|
||||||
|
panic("overflow when calculating the bit set size")
|
||||||
|
}
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the bit set copy bytes.
|
||||||
|
func (b *BitSet) Bytes() []byte {
|
||||||
|
b.mu.RLock()
|
||||||
|
set := make([]byte, len(b.set))
|
||||||
|
copy(set, b.set)
|
||||||
|
b.mu.RUnlock()
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// String returns the bit set by hex type.
|
||||||
|
func (b *BitSet) String() string {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
return hex.EncodeToString(b.set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub returns the bit subset within the specified range of the bit set.
|
||||||
|
// NOTE:
|
||||||
|
// 0 means the 1st bit, -1 means the bottom 1th bit, -2 means the bottom 2th bit and so on.
|
||||||
|
func (b *BitSet) Sub(start, end int) *BitSet {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
newBitSet := &BitSet{
|
||||||
|
set: make([]byte, 0, len(b.set)),
|
||||||
|
}
|
||||||
|
sgi, sbi, egi, ebi, valid := b.validRange(start, end)
|
||||||
|
if !valid {
|
||||||
|
return newBitSet
|
||||||
|
}
|
||||||
|
pre := b.set[sgi] << sbi
|
||||||
|
for _, v := range b.set[sgi+1 : egi] {
|
||||||
|
newBitSet.set = append(newBitSet.set, pre|v>>(7-sbi))
|
||||||
|
pre = v << sbi
|
||||||
|
}
|
||||||
|
last := b.set[egi] >> (7 - ebi) << (7 - ebi)
|
||||||
|
newBitSet.set = append(newBitSet.set, pre|last>>(7-sbi))
|
||||||
|
if sbi < ebi {
|
||||||
|
newBitSet.set = append(newBitSet.set, last<<sbi)
|
||||||
|
}
|
||||||
|
return newBitSet
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ package fileutil
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"go/build"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
@@ -171,3 +172,71 @@ func GetCurrentDirectory() string {
|
|||||||
dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
|
dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
|
||||||
return strings.Replace(dir, "\\", "/", -1)
|
return strings.Replace(dir, "\\", "/", -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// GetGopaths returns the list of Go path directories.
|
||||||
|
func GetGopaths() []string {
|
||||||
|
var all []string
|
||||||
|
for _, p := range filepath.SplitList(build.Default.GOPATH) {
|
||||||
|
if p == "" || p == build.Default.GOROOT {
|
||||||
|
// Empty paths are uninteresting.
|
||||||
|
// If the path is the GOROOT, ignore it.
|
||||||
|
// People sometimes set GOPATH=$GOROOT.
|
||||||
|
// Do not get confused by this common mistake.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(p, "~") {
|
||||||
|
// Path segments starting with ~ on Unix are almost always
|
||||||
|
// users who have incorrectly quoted ~ while setting GOPATH,
|
||||||
|
// preventing it from expanding to $HOME.
|
||||||
|
// The situation is made more confusing by the fact that
|
||||||
|
// bash allows quoted ~ in $PATH (most shells do not).
|
||||||
|
// Do not get confused by this, and do not try to use the path.
|
||||||
|
// It does not exist, and printing errors about it confuses
|
||||||
|
// those users even more, because they think "sure ~ exists!".
|
||||||
|
// The go command diagnoses this situation and prints a
|
||||||
|
// useful error.
|
||||||
|
// On Windows, ~ is used in short names, such as c:\progra~1
|
||||||
|
// for c:\program files.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
all = append(all, p)
|
||||||
|
}
|
||||||
|
for k, v := range all {
|
||||||
|
// GOPATH should end with / or \
|
||||||
|
if strings.HasSuffix(v, "/") || strings.HasSuffix(v, string(os.PathSeparator)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
v += string(os.PathSeparator)
|
||||||
|
all[k] = v
|
||||||
|
}
|
||||||
|
return all
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFirstGopath gets the first $GOPATH value.
|
||||||
|
func GetFirstGopath(allowAutomaticGuessing bool) (gopath string, err error) {
|
||||||
|
a := GetGopaths()
|
||||||
|
if len(a) > 0 {
|
||||||
|
gopath = a[0]
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
gopath = strings.Replace(gopath, "/", string(os.PathSeparator), -1)
|
||||||
|
}()
|
||||||
|
if gopath != "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !allowAutomaticGuessing {
|
||||||
|
err = errors.New("not found GOPATH")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p, _ := os.Getwd()
|
||||||
|
p = strings.Replace(p, "\\", "/", -1) + "/"
|
||||||
|
i := strings.LastIndex(p, "/src/")
|
||||||
|
if i == -1 {
|
||||||
|
err = errors.New("not found GOPATH")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gopath = p[:i+1]
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -14,12 +14,16 @@ package fileutil
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,3 +100,447 @@ func Download(sourceUrl, targetPath string) error {
|
|||||||
func Name(filePath string) string {
|
func Name(filePath string) string {
|
||||||
return filepath.Base(filePath)
|
return filepath.Base(filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// RelPath gets relative path.
|
||||||
|
func RelPath(targpath string) string {
|
||||||
|
basepath, _ := filepath.Abs("./")
|
||||||
|
rel, _ := filepath.Rel(basepath, targpath)
|
||||||
|
return strings.Replace(rel, `\`, `/`, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var curpath = SelfDir()
|
||||||
|
|
||||||
|
// SelfChdir switch the working path to my own path.
|
||||||
|
func SelfChdir() {
|
||||||
|
if err := os.Chdir(curpath); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileExists reports whether the named file or directory exists.
|
||||||
|
func FileExists(name string) (existed bool) {
|
||||||
|
existed, _ = FileExist(name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileExist reports whether the named file or directory exists.
|
||||||
|
func FileExist(name string) (existed bool, isDir bool) {
|
||||||
|
info, err := os.Stat(name)
|
||||||
|
if err != nil {
|
||||||
|
return !os.IsNotExist(err), false
|
||||||
|
}
|
||||||
|
return true, info.IsDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchFile Search a file in paths.
|
||||||
|
// this is often used in search config file in /etc ~/
|
||||||
|
func SearchFile(filename string, paths ...string) (fullpath string, err error) {
|
||||||
|
for _, path := range paths {
|
||||||
|
fullpath = filepath.Join(path, filename)
|
||||||
|
existed, _ := FileExist(fullpath)
|
||||||
|
if existed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GrepFile like command grep -E
|
||||||
|
// for example: GrepFile(`^hello`, "hello.txt")
|
||||||
|
// \n is striped while read
|
||||||
|
func GrepFile(patten string, filename string) (lines []string, err error) {
|
||||||
|
re, err := regexp.Compile(patten)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lines = make([]string, 0)
|
||||||
|
reader := bufio.NewReader(fd)
|
||||||
|
prefix := ""
|
||||||
|
isLongLine := false
|
||||||
|
for {
|
||||||
|
byteLine, isPrefix, er := reader.ReadLine()
|
||||||
|
if er != nil && er != io.EOF {
|
||||||
|
return nil, er
|
||||||
|
}
|
||||||
|
if er == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
line := string(byteLine)
|
||||||
|
if isPrefix {
|
||||||
|
prefix += line
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
isLongLine = true
|
||||||
|
}
|
||||||
|
|
||||||
|
line = prefix + line
|
||||||
|
if isLongLine {
|
||||||
|
prefix = ""
|
||||||
|
}
|
||||||
|
if re.MatchString(line) {
|
||||||
|
lines = append(lines, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lines, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WalkDirs traverses the directory, return to the relative path.
|
||||||
|
// You can specify the suffix.
|
||||||
|
func WalkDirs(targpath string, suffixes ...string) (dirlist []string) {
|
||||||
|
if !filepath.IsAbs(targpath) {
|
||||||
|
targpath, _ = filepath.Abs(targpath)
|
||||||
|
}
|
||||||
|
err := filepath.Walk(targpath, func(retpath string, f os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !f.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(suffixes) == 0 {
|
||||||
|
dirlist = append(dirlist, RelPath(retpath))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_retpath := RelPath(retpath)
|
||||||
|
for _, suffix := range suffixes {
|
||||||
|
if strings.HasSuffix(_retpath, suffix) {
|
||||||
|
dirlist = append(dirlist, _retpath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("utils.WalkRelDirs: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilepathSplitExt splits the filename into a pair (root, ext) such that root + ext == filename,
|
||||||
|
// and ext is empty or begins with a period and contains at most one period.
|
||||||
|
// Leading periods on the basename are ignored; splitext('.cshrc') returns ('', '.cshrc').
|
||||||
|
func FilepathSplitExt(filename string, slashInsensitive ...bool) (root, ext string) {
|
||||||
|
insensitive := false
|
||||||
|
if len(slashInsensitive) > 0 {
|
||||||
|
insensitive = slashInsensitive[0]
|
||||||
|
}
|
||||||
|
if insensitive {
|
||||||
|
filename = FilepathSlashInsensitive(filename)
|
||||||
|
}
|
||||||
|
for i := len(filename) - 1; i >= 0 && !os.IsPathSeparator(filename[i]); i-- {
|
||||||
|
if filename[i] == '.' {
|
||||||
|
return filename[:i], filename[i:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filename, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilepathStem returns the stem of filename.
|
||||||
|
// Example:
|
||||||
|
// FilepathStem("/root/dir/sub/file.ext") // output "file"
|
||||||
|
// NOTE:
|
||||||
|
// If slashInsensitive is empty, default is false.
|
||||||
|
func FilepathStem(filename string, slashInsensitive ...bool) string {
|
||||||
|
insensitive := false
|
||||||
|
if len(slashInsensitive) > 0 {
|
||||||
|
insensitive = slashInsensitive[0]
|
||||||
|
}
|
||||||
|
if insensitive {
|
||||||
|
filename = FilepathSlashInsensitive(filename)
|
||||||
|
}
|
||||||
|
base := filepath.Base(filename)
|
||||||
|
for i := len(base) - 1; i >= 0; i-- {
|
||||||
|
if base[i] == '.' {
|
||||||
|
return base[:i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilepathSlashInsensitive ignore the difference between the slash and the backslash,
|
||||||
|
// and convert to the same as the current system.
|
||||||
|
func FilepathSlashInsensitive(path string) string {
|
||||||
|
if filepath.Separator == '/' {
|
||||||
|
return strings.Replace(path, "\\", "/", -1)
|
||||||
|
}
|
||||||
|
return strings.Replace(path, "/", "\\", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilepathContains checks if the basepath path contains the subpaths.
|
||||||
|
func FilepathContains(basepath string, subpaths []string) error {
|
||||||
|
basepath, err := filepath.Abs(basepath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, p := range subpaths {
|
||||||
|
p, err = filepath.Abs(p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rel, err := filepath.Rel(basepath, p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(rel, "..") {
|
||||||
|
return fmt.Errorf("%s is not include %s", basepath, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func filepathRelative(basepath, targpath string) (string, error) {
|
||||||
|
abs, err := filepath.Abs(targpath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
rel, err := filepath.Rel(basepath, abs)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(rel, "..") {
|
||||||
|
return "", fmt.Errorf("%s is not include %s", basepath, abs)
|
||||||
|
}
|
||||||
|
return rel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilepathDistinct removes the same path and return in the original order.
|
||||||
|
// If toAbs is true, return the result to absolute paths.
|
||||||
|
func FilepathDistinct(paths []string, toAbs bool) ([]string, error) {
|
||||||
|
m := make(map[string]bool, len(paths))
|
||||||
|
ret := make([]string, 0, len(paths))
|
||||||
|
for _, p := range paths {
|
||||||
|
abs, err := filepath.Abs(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if m[abs] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
m[abs] = true
|
||||||
|
if toAbs {
|
||||||
|
ret = append(ret, abs)
|
||||||
|
} else {
|
||||||
|
ret = append(ret, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// FilepathSame checks if the two paths are the same.
|
||||||
|
func FilepathSame(path1, path2 string) (bool, error) {
|
||||||
|
if path1 == path2 {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
p1, err := filepath.Abs(path1)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
p2, err := filepath.Abs(path2)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return p1 == p2, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MkdirAll creates a directory named path,
|
||||||
|
// along with any necessary parents, and returns nil,
|
||||||
|
// or else returns an error.
|
||||||
|
// The permission bits perm (before umask) are used for all
|
||||||
|
// directories that MkdirAll creates.
|
||||||
|
// If path is already a directory, MkdirAll does nothing
|
||||||
|
// and returns nil.
|
||||||
|
// If perm is empty, default use 0755.
|
||||||
|
func MkdirAll(path string, perm ...os.FileMode) error {
|
||||||
|
var fm os.FileMode = 0755
|
||||||
|
if len(perm) > 0 {
|
||||||
|
fm = perm[0]
|
||||||
|
}
|
||||||
|
return os.MkdirAll(path, fm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteFile writes file, and automatically creates the directory if necessary.
|
||||||
|
// NOTE:
|
||||||
|
// If perm is empty, automatically determine the file permissions based on extension.
|
||||||
|
func WriteFile(filename string, data []byte, perm ...os.FileMode) error {
|
||||||
|
filename = filepath.FromSlash(filename)
|
||||||
|
err := MkdirAll(filepath.Dir(filename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(perm) > 0 {
|
||||||
|
return ioutil.WriteFile(filename, data, perm[0])
|
||||||
|
}
|
||||||
|
var ext string
|
||||||
|
if idx := strings.LastIndex(filename, "."); idx != -1 {
|
||||||
|
ext = filename[idx:]
|
||||||
|
}
|
||||||
|
switch ext {
|
||||||
|
case ".sh", ".py", ".rb", ".bat", ".com", ".vbs", ".htm", ".run", ".App", ".exe", ".reg":
|
||||||
|
return ioutil.WriteFile(filename, data, 0755)
|
||||||
|
default:
|
||||||
|
return ioutil.WriteFile(filename, data, 0644)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RewriteFile rewrites the file.
|
||||||
|
func RewriteFile(filename string, fn func(content []byte) (newContent []byte, err error)) error {
|
||||||
|
f, err := os.OpenFile(filename, os.O_RDWR, 0777)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
content, err := ioutil.ReadAll(f)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newContent, err := fn(content)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if bytes.Equal(content, newContent) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
f.Seek(0, 0)
|
||||||
|
f.Truncate(0)
|
||||||
|
_, err = f.Write(newContent)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RewriteToFile rewrites the file to newfilename.
|
||||||
|
// If newfilename already exists and is not a directory, replaces it.
|
||||||
|
func RewriteToFile(filename, newfilename string, fn func(content []byte) (newContent []byte, err error)) error {
|
||||||
|
f, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
info, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cnt, err := ioutil.ReadAll(f)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newContent, err := fn(cnt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return WriteFile(newfilename, newContent, info.Mode())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ReadLines reads all lines of the specified file.
|
||||||
|
func ReadLines(path string) ([]string, int, error) {
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
var lines []string
|
||||||
|
lineCount := 0
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
lines = append(lines, scanner.Text())
|
||||||
|
lineCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
if scanner.Err() == bufio.ErrTooLong {
|
||||||
|
panic(scanner.Err())
|
||||||
|
}
|
||||||
|
return lines, lineCount, scanner.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadLinesV2 reads all lines of the specified file.
|
||||||
|
func ReadLinesV2(path string) ([]string, int, error) {
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
var lines []string
|
||||||
|
lineCount := 0
|
||||||
|
reader := bufio.NewReader(file)
|
||||||
|
for {
|
||||||
|
line, err := reader.ReadString('\n')
|
||||||
|
lines = append(lines, line)
|
||||||
|
lineCount++
|
||||||
|
if err == io.EOF {
|
||||||
|
return lines, lineCount, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return lines, lineCount, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListDir lists all the files in the directory
|
||||||
|
func ListDir(path string) []string {
|
||||||
|
files, err := ioutil.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
filenames := make([]string, len(files))
|
||||||
|
for idx, file := range files {
|
||||||
|
filenames[idx] = file.Name()
|
||||||
|
}
|
||||||
|
return filenames
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPathExist determines whether a file/dir path exists.
|
||||||
|
// User os.Stat to get the info of target file or dir to check whether exists.
|
||||||
|
// If os.Stat returns nil err, the target exists.
|
||||||
|
// If os.Stat returns a os.ErrNotExist err, the target does not exist.
|
||||||
|
// If the error returned is another type, the target is uncertain whether exists.
|
||||||
|
func IsPathExist(path string) (bool, error) {
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
if err == nil {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Create creates or truncates the target file specified by path.
|
||||||
|
// If the parent directory does not exist, it will be created with mode os.ModePerm.is cr truncated.
|
||||||
|
// If the file does not exist, it is created with mode 0666.
|
||||||
|
// If successful, methods on the returned File can be used for I/O; the associated file descriptor has mode O_RDWR.
|
||||||
|
func Create(filePath string) (*os.File, error) {
|
||||||
|
if exist, err := IsPathExist(filePath); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if exist {
|
||||||
|
return os.Create(filePath)
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return os.Create(filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileToBytes serialize the file to bytes.
|
||||||
|
func FileToBytes(path string) []byte {
|
||||||
|
byteStream, _ := ioutil.ReadFile(path)
|
||||||
|
return byteStream
|
||||||
|
}
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
package maputil
|
package maputil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -95,3 +96,249 @@ func Struct2Map(obj interface{}) map[string]interface{} {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Part 1: convert a slice or array to the specified type map set strictly.
|
||||||
|
// Note that the the element type of slice or array need to be equal to map key type.
|
||||||
|
// For example, []uint64{1, 2, 3} can be converted to map[uint64]struct{}{1:struct{}, 2:struct{},
|
||||||
|
// 3:struct{}} by calling ToUint64MapSetStrict() but can't be converted to map[string]struct{}{"1":struct{},
|
||||||
|
// "2":struct{}, "3":struct{}}.
|
||||||
|
//
|
||||||
|
|
||||||
|
// ToBoolMapSetStrict converts a slice or array to map[bool]struct{} strictly.
|
||||||
|
func ToBoolMapSetStrict(i interface{}) map[bool]struct{} {
|
||||||
|
m, _ := ToBoolMapSetStrictE(i)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToBoolMapSetStrictE converts a slice or array to map[bool]struct{} with error.
|
||||||
|
func ToBoolMapSetStrictE(i interface{}) (map[bool]struct{}, error) {
|
||||||
|
m, err := ToMapSetStrictE(i)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if v, ok := m.(map[bool]struct{}); ok {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[bool]struct{}", m, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToIntMapSetStrict converts a slice or array to map[int]struct{}.
|
||||||
|
func ToIntMapSetStrict(i interface{}) map[int]struct{} {
|
||||||
|
m, _ := ToIntMapSetStrictE(i)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToIntMapSetStrictE converts a slice or array to map[int]struct{} with error.
|
||||||
|
func ToIntMapSetStrictE(i interface{}) (map[int]struct{}, error) {
|
||||||
|
m, err := ToMapSetStrictE(i)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if v, ok := m.(map[int]struct{}); ok {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("convert success but the type %T of result %#v isn't map[int]struct{}", m, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToInt8MapSetStrict converts a slice or array to map[int8]struct{}.
|
||||||
|
func ToInt8MapSetStrict(i interface{}) map[int8]struct{} {
|
||||||
|
m, _ := ToInt8MapSetStrictE(i)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToInt8MapSetStrictE converts a slice or array to map[int8]struct{} with error.
|
||||||
|
func ToInt8MapSetStrictE(i interface{}) (map[int8]struct{}, error) {
|
||||||
|
m, err := ToMapSetStrictE(i)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if v, ok := m.(map[int8]struct{}); ok {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[int8]struct{}", m, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToInt16MapSetStrict converts a slice or array to map[int16]struct{}.
|
||||||
|
func ToInt16MapSetStrict(i interface{}) map[int16]struct{} {
|
||||||
|
m, _ := ToInt16MapSetStrictE(i)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToInt16MapSetStrictE converts a slice or array to map[int16]struct{} with error.
|
||||||
|
func ToInt16MapSetStrictE(i interface{}) (map[int16]struct{}, error) {
|
||||||
|
m, err := ToMapSetStrictE(i)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if v, ok := m.(map[int16]struct{}); ok {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[int16]struct{}", m, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToInt32MapSetStrict converts a slice or array to map[int32]struct{}.
|
||||||
|
func ToInt32MapSetStrict(i interface{}) map[int32]struct{} {
|
||||||
|
m, _ := ToInt32MapSetStrictE(i)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToInt32MapSetStrictE converts a slice or array to map[int32]struct{} with error.
|
||||||
|
func ToInt32MapSetStrictE(i interface{}) (map[int32]struct{}, error) {
|
||||||
|
m, err := ToMapSetStrictE(i)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if v, ok := m.(map[int32]struct{}); ok {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[int32]struct{}", m, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToInt64MapSetStrict converts a slice or array to map[int64]struct{}.
|
||||||
|
func ToInt64MapSetStrict(i interface{}) map[int64]struct{} {
|
||||||
|
m, _ := ToInt64MapSetStrictE(i)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToInt64MapSetStrictE converts a slice or array to map[int64]struct{} with error.
|
||||||
|
func ToInt64MapSetStrictE(i interface{}) (map[int64]struct{}, error) {
|
||||||
|
m, err := ToMapSetStrictE(i)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if v, ok := m.(map[int64]struct{}); ok {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[int64]struct{}", m, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUintMapSetStrict converts a slice or array to map[uint]struct{}.
|
||||||
|
func ToUintMapSetStrict(i interface{}) map[uint]struct{} {
|
||||||
|
m, _ := ToUintMapSetStrictE(i)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUintMapSetStrictE converts a slice or array to map[uint]struct{} with error.
|
||||||
|
func ToUintMapSetStrictE(i interface{}) (map[uint]struct{}, error) {
|
||||||
|
m, err := ToMapSetStrictE(i)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if v, ok := m.(map[uint]struct{}); ok {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[uint8]struct{}", m, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUint8MapSetStrict converts a slice or array to map[uint8]struct{}.
|
||||||
|
func ToUint8MapSetStrict(i interface{}) map[uint8]struct{} {
|
||||||
|
m, _ := ToUint8MapSetStrictE(i)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUint8MapSetStrictE converts a slice or array to map[uint8]struct{} with error.
|
||||||
|
func ToUint8MapSetStrictE(i interface{}) (map[uint8]struct{}, error) {
|
||||||
|
m, err := ToMapSetStrictE(i)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if v, ok := m.(map[uint8]struct{}); ok {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[uint8]struct{}", m, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUint16MapSetStrict converts a slice or array to map[uint16]struct{}.
|
||||||
|
func ToUint16MapSetStrict(i interface{}) map[uint16]struct{} {
|
||||||
|
m, _ := ToUint16MapSetStrictE(i)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUint16MapSetStrictE converts a slice or array to map[uint16]struct{} with error.
|
||||||
|
func ToUint16MapSetStrictE(i interface{}) (map[uint16]struct{}, error) {
|
||||||
|
m, err := ToMapSetStrictE(i)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if v, ok := m.(map[uint16]struct{}); ok {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[uint16]struct{}", m, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUint32MapSetStrict converts a slice or array to map[uint32]struct{}.
|
||||||
|
func ToUint32MapSetStrict(i interface{}) map[uint32]struct{} {
|
||||||
|
m, _ := ToUint32MapSetStrictE(i)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUint32MapSetStrictE converts a slice or array to map[uint32]struct{} with error.
|
||||||
|
func ToUint32MapSetStrictE(i interface{}) (map[uint32]struct{}, error) {
|
||||||
|
m, err := ToMapSetStrictE(i)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if v, ok := m.(map[uint32]struct{}); ok {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[uint32]struct{}", m, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUint64MapSetStrict converts a slice or array to map[uint64]struct{}.
|
||||||
|
func ToUint64MapSetStrict(i interface{}) map[uint64]struct{} {
|
||||||
|
m, _ := ToUint64MapSetStrictE(i)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUint64MapSetStrictE converts a slice or array to map[uint64]struct{} with error.
|
||||||
|
func ToUint64MapSetStrictE(i interface{}) (map[uint64]struct{}, error) {
|
||||||
|
m, err := ToMapSetStrictE(i)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if v, ok := m.(map[uint64]struct{}); ok {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[uint64]struct{}", m, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToStrMapSetStrict converts a slice or array to map[string]struct{}.
|
||||||
|
func ToStrMapSetStrict(i interface{}) map[string]struct{} {
|
||||||
|
m, _ := ToStrMapSetStrictE(i)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToStrMapSetStrictE converts a slice or array to map[string]struct{} with error.
|
||||||
|
func ToStrMapSetStrictE(i interface{}) (map[string]struct{}, error) {
|
||||||
|
m, err := ToMapSetStrictE(i)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if v, ok := m.(map[string]struct{}); ok {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[string]struct{}", m, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToMapSetStrictE converts a slice or array to map set with error strictly.
|
||||||
|
// The result of map key type is equal to the type input element.
|
||||||
|
func ToMapSetStrictE(i interface{}) (interface{}, error) {
|
||||||
|
// check params.
|
||||||
|
if i == nil {
|
||||||
|
return nil, fmt.Errorf("unable to converts nil to map[interface{}]struct{}")
|
||||||
|
}
|
||||||
|
t := reflect.TypeOf(i)
|
||||||
|
kind := t.Kind()
|
||||||
|
if kind != reflect.Slice && kind != reflect.Array {
|
||||||
|
return nil, fmt.Errorf("the type %T of input %#v isn't a slice or array", i, i)
|
||||||
|
}
|
||||||
|
// execute the convert.
|
||||||
|
v := reflect.ValueOf(i)
|
||||||
|
mT := reflect.MapOf(t.Elem(), reflect.TypeOf(struct{}{}))
|
||||||
|
mV := reflect.MakeMapWithSize(mT, v.Len())
|
||||||
|
for j := 0; j < v.Len(); j++ {
|
||||||
|
mV.SetMapIndex(v.Index(j), reflect.ValueOf(struct{}{}))
|
||||||
|
}
|
||||||
|
return mV.Interface(), nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The zfoo Authors
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||||
|
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
package mathutil
|
||||||
|
|
||||||
|
// AbsInt8 gets absolute value of int8.
|
||||||
|
//
|
||||||
|
// Example 1: AbsInt8(-5)
|
||||||
|
// -5 code value as below
|
||||||
|
// original code 1000,0101
|
||||||
|
// inverse code 1111,1010
|
||||||
|
// complement code 1111,1011
|
||||||
|
// Negative numbers are represented by complement code in memory.
|
||||||
|
// shifted = n >> 7 = (1111,1011) >> 7 = 1111,1111 = -1(10-base) (负数右移,左补1)
|
||||||
|
// 1111,1011
|
||||||
|
// n xor shifted = ----------- = 0000,0100 = 4(10-base)
|
||||||
|
// 1111,1111
|
||||||
|
// (n ^ shifted) - shifted = 4 - (-1) = 5
|
||||||
|
//
|
||||||
|
// Example 2: AbsInt8(5)
|
||||||
|
// 5 code value as below
|
||||||
|
// original code 0000,0101
|
||||||
|
// Positive numbers are represented by original code in memory,
|
||||||
|
// and the XOR operation between positive numbers and 0 is equal to itself.
|
||||||
|
// shifted = n >> 7 = 0
|
||||||
|
// 0000,0101
|
||||||
|
// n xor shifted = ----------- = 0000,0101 = 5(10-base)
|
||||||
|
// 0000,0000
|
||||||
|
// (n ^ shifted) - shifted = 5 - 0 = 5
|
||||||
|
func AbsInt8(n int8) int8 {
|
||||||
|
shifted := n >> 7
|
||||||
|
return (n ^ shifted) - shifted
|
||||||
|
}
|
||||||
|
|
||||||
|
// AbsInt16 gets absolute value of int16.
|
||||||
|
func AbsInt16(n int16) int16 {
|
||||||
|
shifted := n >> 15
|
||||||
|
return (n ^ shifted) - shifted
|
||||||
|
}
|
||||||
|
|
||||||
|
// AbsInt32 gets absolute value of int32.
|
||||||
|
func AbsInt32(n int32) int32 {
|
||||||
|
shifted := n >> 31
|
||||||
|
return (n ^ shifted) - shifted
|
||||||
|
}
|
||||||
|
|
||||||
|
// AbsInt64 gets absolute value of int64.
|
||||||
|
func AbsInt64(n int64) int64 {
|
||||||
|
shifted := n >> 63
|
||||||
|
return (n ^ shifted) - shifted
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The zfoo Authors
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||||
|
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
package mathutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CMPRES int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
INCMP CMPRES = iota - 2
|
||||||
|
LT
|
||||||
|
EQ
|
||||||
|
GT
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compare compare the size relationship between two numeric values or strings.
|
||||||
|
// The result is INCMP(incomparable), LT(less than), EQ(equal) or GT(greater than).
|
||||||
|
func Compare(lhs, rhs interface{}) CMPRES {
|
||||||
|
if !isComparable(lhs, rhs) {
|
||||||
|
return INCMP
|
||||||
|
}
|
||||||
|
|
||||||
|
lhsVal := reflect.ValueOf(lhs)
|
||||||
|
rhsVal := reflect.ValueOf(rhs)
|
||||||
|
|
||||||
|
switch lhsVal.Kind() {
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
switch {
|
||||||
|
case lhsVal.Int() < rhsVal.Int():
|
||||||
|
return LT
|
||||||
|
case lhsVal.Int() == rhsVal.Int():
|
||||||
|
return EQ
|
||||||
|
case lhsVal.Int() > rhsVal.Int():
|
||||||
|
return GT
|
||||||
|
}
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
switch {
|
||||||
|
case lhsVal.Uint() < rhsVal.Uint():
|
||||||
|
return LT
|
||||||
|
case lhsVal.Uint() == rhsVal.Uint():
|
||||||
|
return EQ
|
||||||
|
case lhsVal.Uint() > rhsVal.Uint():
|
||||||
|
return GT
|
||||||
|
}
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
switch {
|
||||||
|
case lhsVal.Float() < rhsVal.Float():
|
||||||
|
return LT
|
||||||
|
case lhsVal.Float() == rhsVal.Float():
|
||||||
|
return EQ
|
||||||
|
case lhsVal.Float() > rhsVal.Float():
|
||||||
|
return GT
|
||||||
|
}
|
||||||
|
case reflect.String:
|
||||||
|
switch {
|
||||||
|
case lhsVal.String() < rhsVal.String():
|
||||||
|
return LT
|
||||||
|
case lhsVal.String() == rhsVal.String():
|
||||||
|
return EQ
|
||||||
|
case lhsVal.String() > rhsVal.String():
|
||||||
|
return GT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return INCMP
|
||||||
|
}
|
||||||
|
|
||||||
|
func isComparable(lhs, rhs interface{}) bool {
|
||||||
|
lhsVal := reflect.ValueOf(lhs)
|
||||||
|
rhsVal := reflect.ValueOf(rhs)
|
||||||
|
return lhsVal.Kind() == rhsVal.Kind()
|
||||||
|
}
|
||||||
|
|
||||||
|
func CompareLT(lhs, rhs interface{}) bool {
|
||||||
|
return Compare(lhs, rhs) == LT
|
||||||
|
}
|
||||||
|
|
||||||
|
func CompareLE(lhs, rhs interface{}) bool {
|
||||||
|
res := Compare(lhs, rhs)
|
||||||
|
return res == LT || res == EQ
|
||||||
|
}
|
||||||
|
|
||||||
|
func CompareEQ(lhs, rhs interface{}) bool {
|
||||||
|
return Compare(lhs, rhs) == EQ
|
||||||
|
}
|
||||||
|
|
||||||
|
func CompareGT(lhs, rhs interface{}) bool {
|
||||||
|
return Compare(lhs, rhs) == GT
|
||||||
|
}
|
||||||
|
|
||||||
|
func CompareGE(lhs, rhs interface{}) bool {
|
||||||
|
res := Compare(lhs, rhs)
|
||||||
|
return Compare(lhs, rhs) == GT || res == EQ
|
||||||
|
}
|
||||||
@@ -0,0 +1,418 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The zfoo Authors
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||||
|
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package netutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IP ip struct info.
|
||||||
|
type IP struct {
|
||||||
|
Begin uint32
|
||||||
|
End uint32
|
||||||
|
ISPCode int
|
||||||
|
ISP string
|
||||||
|
CountryCode int
|
||||||
|
Country string
|
||||||
|
ProvinceCode int
|
||||||
|
Province string
|
||||||
|
CityCode int
|
||||||
|
City string
|
||||||
|
DistrictCode int
|
||||||
|
District string
|
||||||
|
Latitude float64
|
||||||
|
Longitude float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zone ip struct info.
|
||||||
|
type Zone struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Addr string `json:"addr"`
|
||||||
|
ISP string `json:"isp"`
|
||||||
|
Country string `json:"country"`
|
||||||
|
Province string `json:"province"`
|
||||||
|
City string `json:"city"`
|
||||||
|
Latitude float64 `json:"latitude"`
|
||||||
|
Longitude float64 `json:"longitude"`
|
||||||
|
CountryCode int `json:"country_code,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// List struct info list.
|
||||||
|
type List struct {
|
||||||
|
IPs []*IP
|
||||||
|
}
|
||||||
|
|
||||||
|
// New create Xip instance and return.
|
||||||
|
func New(path string) (list *List, err error) {
|
||||||
|
var (
|
||||||
|
ip *IP
|
||||||
|
file *os.File
|
||||||
|
line []byte
|
||||||
|
)
|
||||||
|
list = new(List)
|
||||||
|
if file, err = os.Open(path); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
reader := bufio.NewReader(file)
|
||||||
|
for {
|
||||||
|
if line, _, err = reader.ReadLine(); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
break
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lines := strings.Fields(string(line))
|
||||||
|
if len(lines) < 13 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// lines[2]:country lines[3]:province lines[4]:city lines[5]:unit
|
||||||
|
if lines[3] == "香港" || lines[3] == "澳门" || lines[3] == "台湾" {
|
||||||
|
lines[2] = lines[3]
|
||||||
|
lines[3] = lines[4]
|
||||||
|
lines[4] = "*"
|
||||||
|
}
|
||||||
|
// ex.: from 中国 中国 * to 中国 ”“ ”“
|
||||||
|
if lines[2] == lines[3] || lines[3] == "*" {
|
||||||
|
lines[3] = ""
|
||||||
|
lines[4] = ""
|
||||||
|
} else if lines[3] == lines[4] || lines[4] == "*" {
|
||||||
|
// ex.: from 中国 北京 北京 to 中国 北京 ”“
|
||||||
|
lines[4] = ""
|
||||||
|
}
|
||||||
|
ip = &IP{
|
||||||
|
Begin: InetAtoN(lines[0]),
|
||||||
|
End: InetAtoN(lines[1]),
|
||||||
|
Country: lines[2],
|
||||||
|
Province: lines[3],
|
||||||
|
City: lines[4],
|
||||||
|
ISP: lines[6],
|
||||||
|
}
|
||||||
|
ip.Latitude, _ = strconv.ParseFloat(lines[7], 64)
|
||||||
|
ip.Longitude, _ = strconv.ParseFloat(lines[8], 64)
|
||||||
|
ip.CountryCode, _ = strconv.Atoi(lines[12])
|
||||||
|
list.IPs = append(list.IPs, ip)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP ip zone info by ip
|
||||||
|
func (l *List) IP(ipStr string) (ip *IP) {
|
||||||
|
addr := InetAtoN(ipStr)
|
||||||
|
i, j := 0, len(l.IPs)
|
||||||
|
for i < j {
|
||||||
|
h := i + (j-i)/2 // avoid overflow when computing h
|
||||||
|
ip = l.IPs[h]
|
||||||
|
// i ≤ h < j
|
||||||
|
if addr < ip.Begin {
|
||||||
|
j = h
|
||||||
|
} else if addr > ip.End {
|
||||||
|
i = h + 1
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// All return ipInfos.
|
||||||
|
func (l *List) All() []*IP {
|
||||||
|
return l.IPs
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalIP get external ip.
|
||||||
|
func ExternalIP() (res []string) {
|
||||||
|
inters, err := net.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, inter := range inters {
|
||||||
|
if !strings.HasPrefix(inter.Name, "lo") {
|
||||||
|
addrs, err := inter.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, addr := range addrs {
|
||||||
|
if ipnet, ok := addr.(*net.IPNet); ok {
|
||||||
|
if ipnet.IP.IsLoopback() || ipnet.IP.IsLinkLocalMulticast() || ipnet.IP.IsLinkLocalUnicast() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ip4 := ipnet.IP.To4(); ip4 != nil {
|
||||||
|
switch true {
|
||||||
|
case ip4[0] == 10:
|
||||||
|
continue
|
||||||
|
case ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31:
|
||||||
|
continue
|
||||||
|
case ip4[0] == 192 && ip4[1] == 168:
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
res = append(res, ipnet.IP.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalIP get internal ip.
|
||||||
|
func InternalIP() string {
|
||||||
|
inters, err := net.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
for _, inter := range inters {
|
||||||
|
if !strings.HasPrefix(inter.Name, "lo") {
|
||||||
|
addrs, err := inter.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, addr := range addrs {
|
||||||
|
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
||||||
|
if ipnet.IP.To4() != nil {
|
||||||
|
return ipnet.IP.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// InetAtoN conver ip addr to uint32.
|
||||||
|
func InetAtoN(s string) (sum uint32) {
|
||||||
|
ip := net.ParseIP(s)
|
||||||
|
if ip == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ip = ip.To4()
|
||||||
|
if ip == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sum += uint32(ip[0]) << 24
|
||||||
|
sum += uint32(ip[1]) << 16
|
||||||
|
sum += uint32(ip[2]) << 8
|
||||||
|
sum += uint32(ip[3])
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
// InetNtoA conver uint32 to ip addr.
|
||||||
|
func InetNtoA(sum uint32) string {
|
||||||
|
ip := make(net.IP, net.IPv4len)
|
||||||
|
ip[0] = byte((sum >> 24) & 0xFF)
|
||||||
|
ip[1] = byte((sum >> 16) & 0xFF)
|
||||||
|
ip[2] = byte((sum >> 8) & 0xFF)
|
||||||
|
ip[3] = byte(sum & 0xFF)
|
||||||
|
return ip.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var (
|
||||||
|
nativeEndian binary.ByteOrder
|
||||||
|
|
||||||
|
ipv4PrivateCIDRString = []string{
|
||||||
|
"0.0.0.0/8", "10.0.0.0/8", "100.64.0.0/10", "127.0.0.0/8", "169.254.0.0/16",
|
||||||
|
"172.16.0.0/12", "192.0.0.0/24", "192.0.2.0/24", "192.88.99.0/24", "192.168.0.0/16",
|
||||||
|
"198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "224.0.0.0/4", "240.0.0.0/4", "255.255.255.255/32",
|
||||||
|
}
|
||||||
|
ipv6PrivateCIDRString = []string{
|
||||||
|
"::1/128", "::/128", "64:ff9b::/96", "::ffff:0:0/96", "100::/64", "2001::/23",
|
||||||
|
"2001::/32", "2001:2::/48", "2001:db8::/32", "2001:10::/28", "2002::/16", "fc00::/7", "fe80::/10",
|
||||||
|
"2001:20::/28", "ff00::/8",
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv4PrivateCIDR []*net.IPNet
|
||||||
|
ipv6PrivateCIDR []*net.IPNet
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if nativeEndian == nil {
|
||||||
|
var x uint32 = 0x01020304
|
||||||
|
if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
|
||||||
|
nativeEndian = binary.BigEndian
|
||||||
|
} else {
|
||||||
|
nativeEndian = binary.LittleEndian
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range ipv4PrivateCIDRString {
|
||||||
|
_, n, _ := net.ParseCIDR(v)
|
||||||
|
ipv4PrivateCIDR = append(ipv4PrivateCIDR, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range ipv6PrivateCIDRString {
|
||||||
|
_, n, _ := net.ParseCIDR(v)
|
||||||
|
ipv6PrivateCIDR = append(ipv6PrivateCIDR, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsReservedIP reports whether ip is private.
|
||||||
|
// Support ipv4/ipv6, refer rfc6890.
|
||||||
|
// Return <0 ip is invalid, =0 ip is public, >0 ip is private.
|
||||||
|
func IsReservedIP(ip string) int {
|
||||||
|
addr := net.ParseIP(ip)
|
||||||
|
if addr == nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if addr.IsLoopback() || addr.IsMulticast() || addr.IsLinkLocalMulticast() || addr.IsLinkLocalUnicast() {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if addr.To4() != nil {
|
||||||
|
for _, v := range ipv4PrivateCIDR {
|
||||||
|
if v.Contains(addr) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, v := range ipv6PrivateCIDR {
|
||||||
|
if v.Contains(addr) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap16 swap a 16 bit value if aren't big endian
|
||||||
|
func Swap16(i uint16) uint16 {
|
||||||
|
return (i&0xff00)>>8 | (i&0xff)<<8
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap32 swap a 32 bit value if aren't big endian
|
||||||
|
func Swap32(i uint32) uint32 {
|
||||||
|
return (i&0xff000000)>>24 | (i&0xff0000)>>8 | (i&0xff00)<<8 | (i&0xff)<<24
|
||||||
|
}
|
||||||
|
|
||||||
|
// Htons convert uint16 from host byte order to network byte order
|
||||||
|
func Htons(i uint16) uint16 {
|
||||||
|
if GetNativeEndian() == binary.BigEndian {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
// 大端模式, 高位放在低地址
|
||||||
|
// 0x1234
|
||||||
|
// 0x12 0x34
|
||||||
|
return Swap16(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Htonl convert uint32 from host byte order to network byte order.
|
||||||
|
func Htonl(i uint32) uint32 {
|
||||||
|
if GetNativeEndian() == binary.BigEndian {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
// 大端模式, 高位放在低地址
|
||||||
|
// 0x12345678
|
||||||
|
// 0x12 0x34 0x56 0x78
|
||||||
|
return Swap32(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ntohs convert uint16 from network byte order to host byte order.
|
||||||
|
func Ntohs(i uint16) uint16 {
|
||||||
|
if GetNativeEndian() == binary.BigEndian {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
// 小端模式, 低位放在低地址
|
||||||
|
// 0x1234
|
||||||
|
// 0x34 0x12
|
||||||
|
return Swap16(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ntohl convert uint32 from network byte order to host byte order.
|
||||||
|
func Ntohl(i uint32) uint32 {
|
||||||
|
if GetNativeEndian() == binary.BigEndian {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
// 小端模式, 低位放在低地址
|
||||||
|
// 0x12345678
|
||||||
|
// 0x78 0x56 0x34 0x12
|
||||||
|
return Swap32(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPv4ToU32 convert ipv4(a.b.c.d) to uint32 in host byte order.
|
||||||
|
func IPv4ToU32(ip net.IP) uint32 {
|
||||||
|
if ip == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
a := uint32(ip[12])
|
||||||
|
b := uint32(ip[13])
|
||||||
|
c := uint32(ip[14])
|
||||||
|
d := uint32(ip[15])
|
||||||
|
return uint32(a<<24 | b<<16 | c<<8 | d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// U32ToIPv4 convert uint32 to ipv4(a.b.c.d) in host byte order.
|
||||||
|
func U32ToIPv4(ip uint32) net.IP {
|
||||||
|
a := byte((ip >> 24) & 0xFF)
|
||||||
|
b := byte((ip >> 16) & 0xFF)
|
||||||
|
c := byte((ip >> 8) & 0xFF)
|
||||||
|
d := byte(ip & 0xFF)
|
||||||
|
return net.IPv4(a, b, c, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPv4StrToU32 convert IPv4 string to uint32 in host byte order.
|
||||||
|
func IPv4StrToU32(s string) (ip uint32) {
|
||||||
|
r := `^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})`
|
||||||
|
reg, err := regexp.Compile(r)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ips := reg.FindStringSubmatch(s)
|
||||||
|
if ips == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ip1, _ := strconv.Atoi(ips[1])
|
||||||
|
ip2, _ := strconv.Atoi(ips[2])
|
||||||
|
ip3, _ := strconv.Atoi(ips[3])
|
||||||
|
ip4, _ := strconv.Atoi(ips[4])
|
||||||
|
|
||||||
|
if ip1 > 255 || ip2 > 255 || ip3 > 255 || ip4 > 255 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ip += uint32(ip1 * 0x1000000)
|
||||||
|
ip += uint32(ip2 * 0x10000)
|
||||||
|
ip += uint32(ip3 * 0x100)
|
||||||
|
ip += uint32(ip4)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// U32ToIPv4Str convert uint32 to IPv4 string in host byte order.
|
||||||
|
func U32ToIPv4Str(ip uint32) string {
|
||||||
|
return fmt.Sprintf("%d.%d.%d.%d", ip>>24, ip<<8>>24, ip<<16>>24, ip<<24>>24)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNativeEndian gets byte order for the current system.
|
||||||
|
func GetNativeEndian() binary.ByteOrder {
|
||||||
|
return nativeEndian
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLittleEndian determines whether the host byte order is little endian.
|
||||||
|
func IsLittleEndian() bool {
|
||||||
|
n := 0x1234
|
||||||
|
return *(*byte)(unsafe.Pointer(&n)) == 0x34
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package netutil
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
|
||||||
|
func TestExternalIP(t *testing.T) {
|
||||||
|
t.Log(ExternalIP())
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The zfoo Authors
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||||
|
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
package cmder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmdArg [2]string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
cmdArg[0] = "cmd"
|
||||||
|
cmdArg[1] = "/c"
|
||||||
|
} else {
|
||||||
|
cmdArg[0] = "/bin/sh"
|
||||||
|
cmdArg[1] = "-c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run exec cmd and catch the result.
|
||||||
|
// Waits for the given command to finish with a timeout.
|
||||||
|
// If the command times out, it attempts to kill the process.
|
||||||
|
func Run(cmdLine string, timeout ...time.Duration) *Result {
|
||||||
|
cmd := exec.Command(cmdArg[0], cmdArg[1], cmdLine)
|
||||||
|
var ret = new(Result)
|
||||||
|
cmd.Stdout = &ret.buf
|
||||||
|
cmd.Stderr = &ret.buf
|
||||||
|
cmd.Env = os.Environ()
|
||||||
|
ret.err = cmd.Start()
|
||||||
|
if ret.err != nil {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
if len(timeout) == 0 || timeout[0] <= 0 {
|
||||||
|
ret.err = cmd.Wait()
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
timer := time.NewTimer(timeout[0])
|
||||||
|
done := make(chan error)
|
||||||
|
go func() { done <- cmd.Wait() }()
|
||||||
|
select {
|
||||||
|
case ret.err = <-done:
|
||||||
|
timer.Stop()
|
||||||
|
case <-timer.C:
|
||||||
|
if err := cmd.Process.Kill(); err != nil {
|
||||||
|
ret.err = fmt.Errorf("command timed out and killing process fail: %s", err.Error())
|
||||||
|
} else {
|
||||||
|
// wait for the command to return after killing it
|
||||||
|
<-done
|
||||||
|
ret.err = errors.New("command timed out")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Result cmd exec result
|
||||||
|
type Result struct {
|
||||||
|
buf bytes.Buffer
|
||||||
|
err error
|
||||||
|
str *string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err returns the error log.
|
||||||
|
func (r *Result) Err() error {
|
||||||
|
if r.err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
r.err = errors.New(r.String())
|
||||||
|
return r.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the exec log.
|
||||||
|
func (r *Result) String() string {
|
||||||
|
if r.str == nil {
|
||||||
|
b := bytes.TrimSpace(r.buf.Bytes())
|
||||||
|
if r.err != nil {
|
||||||
|
b = append(b, ' ', '(')
|
||||||
|
b = append(b, r.err.Error()...)
|
||||||
|
b = append(b, ')')
|
||||||
|
}
|
||||||
|
r.str = (*string)(unsafe.Pointer(&b))
|
||||||
|
}
|
||||||
|
return *r.str
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// IsWin determine whether the system is windows
|
||||||
|
func IsWin() bool {
|
||||||
|
return runtime.GOOS == "windows"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMac determines whether the system is darwin
|
||||||
|
func IsMac() bool {
|
||||||
|
return runtime.GOOS == "darwin"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLinux determines whether the system is linux
|
||||||
|
func IsLinux() bool {
|
||||||
|
return runtime.GOOS == "linux"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSupportColor check current console whether support color.
|
||||||
|
// Supported: linux, mac, or windows's ConEmu, Cmder, putty, git-bash.exe
|
||||||
|
// Not support: windows cmd.exe, powerShell.exe
|
||||||
|
func IsSupportColor() bool {
|
||||||
|
// Support color: "TERM=xterm" "TERM=xterm-vt220" "TERM=xterm-256color" "TERM=screen-256color"
|
||||||
|
// Don't support color: "TERM=cygwin"
|
||||||
|
envTerm := os.Getenv("TERM")
|
||||||
|
if strings.Contains(envTerm, "xterm") || strings.Contains(envTerm, "screen") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// like on ConEmu software, e.g "ConEmuANSI=ON"
|
||||||
|
if os.Getenv("ConEmuANSI") == "ON" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// like on ConEmu software, e.g "ANSICON=189x2000 (189x43)"
|
||||||
|
if os.Getenv("ANSICON") != "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSupport256Color check current console whether support 256 color.
|
||||||
|
func IsSupport256Color() bool {
|
||||||
|
// "TERM=xterm-256color" "TERM=screen-256color"
|
||||||
|
return strings.Contains(os.Getenv("TERM"), "256color")
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSupportTrueColor check current console whether support true color
|
||||||
|
func IsSupportTrueColor() bool {
|
||||||
|
// "COLORTERM=truecolor"
|
||||||
|
return strings.Contains(os.Getenv("COLORTERM"), "truecolor")
|
||||||
|
}
|
||||||
@@ -65,3 +65,5 @@ func getRandSeek() int64 {
|
|||||||
l.Unlock()
|
l.Unlock()
|
||||||
return time.Now().UnixNano() + randSeek
|
return time.Now().UnixNano() + randSeek
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -13,6 +13,7 @@ package stringutil
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -250,3 +251,886 @@ func UnicodeEmojiCode(s string) string {
|
|||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
通过+号拼接字符串
|
||||||
|
*/
|
||||||
|
func AddStringWithOperator(str1, str2 string) string {
|
||||||
|
|
||||||
|
return str1 + str2
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
通过strings 包的join连接字符串
|
||||||
|
*/
|
||||||
|
func AddStringWidthJoin(strArray []string) string {
|
||||||
|
|
||||||
|
return strings.Join(strArray, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
通过buffer 拼接字符串
|
||||||
|
*/
|
||||||
|
func AddStringWidthBuffer(strArray []string) string {
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
for _, value := range strArray {
|
||||||
|
buffer.WriteString(value)
|
||||||
|
}
|
||||||
|
return buffer.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
反转字符串
|
||||||
|
*/
|
||||||
|
func ReversString(str string) string {
|
||||||
|
count := len(str)
|
||||||
|
bytes := make([]byte, len(str))
|
||||||
|
for i := 0; i < len(str); i++ {
|
||||||
|
bytes[i] = str[count-1-i]
|
||||||
|
}
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
https://www.cnblogs.com/linghu-java/p/9037262.html 参考这边文章
|
||||||
|
查找给定字符串中的最长不重复子串
|
||||||
|
返回最长不重复子串+子串的长度
|
||||||
|
*/
|
||||||
|
func FindMaxLenNoRepeatSubStr(s string) (string, int) {
|
||||||
|
if len(s) == 1 {
|
||||||
|
return s, 1
|
||||||
|
}
|
||||||
|
head, tail := 0, 0
|
||||||
|
maxLenNoRepeatSubStr := ""
|
||||||
|
for i := 0; i < len(s)-1; i++ {
|
||||||
|
for j := i; j < len(s); j++ {
|
||||||
|
if strings.Contains(maxLenNoRepeatSubStr, s[j:j+1]) {
|
||||||
|
if head == 0 && tail == 0 {
|
||||||
|
head, tail = i, j
|
||||||
|
}
|
||||||
|
if len(s[i:j]) > len(s[head:tail]) {
|
||||||
|
head, tail = i, j
|
||||||
|
}
|
||||||
|
maxLenNoRepeatSubStr = ""
|
||||||
|
break
|
||||||
|
}
|
||||||
|
maxLenNoRepeatSubStr = s[i : j+1]
|
||||||
|
}
|
||||||
|
if maxLenNoRepeatSubStr == s[i:] && len(s[i:]) > len(s[head:tail]) {
|
||||||
|
head, tail = i, len(s)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s[head:tail], tail - head
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
查找给定字符串中的最长不重复子串
|
||||||
|
返回子串的长度
|
||||||
|
*/
|
||||||
|
func FindMaxLenNoRepeatSubStr2(s string) int {
|
||||||
|
length := len(s)
|
||||||
|
ans := 0
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
for j := i + 1; j <= length; j++ {
|
||||||
|
if allUnique(s, i, j) {
|
||||||
|
if (j - i) > ans {
|
||||||
|
ans = j - i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ans
|
||||||
|
}
|
||||||
|
func allUnique(s string, start int, end int) bool {
|
||||||
|
set := make(map[byte]int, 0)
|
||||||
|
for i := start; i < end; i++ {
|
||||||
|
if _, ok := set[s[i]]; ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
set[s[i]]++
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
时间滑动窗口思想
|
||||||
|
查找给定字符串中的最长不重复子串
|
||||||
|
返回子串的长度
|
||||||
|
*/
|
||||||
|
func FindMaxLenNoRepeatSubStr3(s string) int {
|
||||||
|
length := len(s)
|
||||||
|
ans := 0
|
||||||
|
m := make(map[byte]int, 0)
|
||||||
|
i, j := 0, 0
|
||||||
|
for i < length && j < length {
|
||||||
|
//如果不包含
|
||||||
|
if _, ok := m[s[j]]; !ok {
|
||||||
|
m[s[j]]++
|
||||||
|
j++
|
||||||
|
if (j - i) > ans {
|
||||||
|
ans = j - i
|
||||||
|
}
|
||||||
|
} else { //如果包含
|
||||||
|
delete(m, s[i])
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ans
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
从两个给定字符串中找出最长公共子串
|
||||||
|
返回最长公共子串 "gfdef", "abcdef"
|
||||||
|
*/
|
||||||
|
func FindMaxLenCommonSubStr(str1, str2 string) string {
|
||||||
|
start1 := -1
|
||||||
|
start2 := -1
|
||||||
|
longest := 0
|
||||||
|
for i := 0; i < len(str1); i++ {
|
||||||
|
for j := 0; j < len(str2); j++ {
|
||||||
|
length := 0
|
||||||
|
m := i
|
||||||
|
n := j
|
||||||
|
for m < len(str1) && n < len(str2) {
|
||||||
|
if str1[m] != str2[n] {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
length++
|
||||||
|
m++
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
if longest < length {
|
||||||
|
longest = length
|
||||||
|
start1 = i
|
||||||
|
start2 = j
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(str1) > len(str2) {
|
||||||
|
return str1[start1 : start1+longest]
|
||||||
|
} else {
|
||||||
|
return str2[start2 : start2+longest]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 采用动态规划求取最长公共子串
|
||||||
|
func FindMaxLenCommonSubStr2(str1, str2 string) string {
|
||||||
|
l1 := len(str1)
|
||||||
|
l2 := len(str2)
|
||||||
|
max := 0
|
||||||
|
end := 0
|
||||||
|
|
||||||
|
var twoArray [][]int
|
||||||
|
for i := 0; i < l1+1; i++ {
|
||||||
|
tmp := make([]int, l2+1)
|
||||||
|
twoArray = append(twoArray, tmp)
|
||||||
|
}
|
||||||
|
for i := 1; i <= l1; i++ {
|
||||||
|
for j := 1; j <= l2; j++ {
|
||||||
|
if str1[i-1] == str2[j-1] {
|
||||||
|
twoArray[i][j] = twoArray[i-1][j-1] + 1
|
||||||
|
if twoArray[i][j] > max {
|
||||||
|
max = twoArray[i][j]
|
||||||
|
end = j
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
twoArray[i][j] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bytes := make([]byte, 0)
|
||||||
|
for m := end - max; m < end; m++ {
|
||||||
|
bytes = append(bytes, str2[m])
|
||||||
|
}
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
求最长公共子序列
|
||||||
|
*/
|
||||||
|
func FindMaxLenCommonSubSeq(str1, str2 string) string {
|
||||||
|
l1 := len(str1)
|
||||||
|
l2 := len(str2)
|
||||||
|
|
||||||
|
bs := make([]byte, 0)
|
||||||
|
for m := 1; m <= l2; m++ {
|
||||||
|
for n := 1; n <= l1; n++ {
|
||||||
|
if str1[n-1] == str2[m-1] {
|
||||||
|
bs = append(bs, str1[n-1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Deduplicate(string(bs))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
移除字符串中的重复字符
|
||||||
|
*/
|
||||||
|
func RemoveRepeatStr(str string) string {
|
||||||
|
if len(str) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
bs := [256]byte{}
|
||||||
|
for _, v := range str {
|
||||||
|
bs[v] = 1
|
||||||
|
}
|
||||||
|
rs := make([]byte, 0)
|
||||||
|
for index, v := range bs {
|
||||||
|
if v == 1 {
|
||||||
|
rs = append(rs, byte(index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(rs)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
去除重复的字符,返回去重后的字符
|
||||||
|
申请了新的数组用来存储,空间复杂度o(n)
|
||||||
|
*/
|
||||||
|
func Deduplicate(input string) string {
|
||||||
|
if len(input) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
slice := make([]rune, 0, 0)
|
||||||
|
m := make(map[rune]byte, 0)
|
||||||
|
for _, v := range input {
|
||||||
|
if _, ok := m[v]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
slice = append(slice, v)
|
||||||
|
m[v] = 0
|
||||||
|
}
|
||||||
|
return string(slice)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
去除重复的字符,返回去重后的字符
|
||||||
|
空间复杂度o(1)
|
||||||
|
*/
|
||||||
|
func Deduplicate2(input string) string {
|
||||||
|
if len(input) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
m := make(map[rune]byte, 0)
|
||||||
|
bs := []byte(input)
|
||||||
|
current := 0
|
||||||
|
for next, v := range input {
|
||||||
|
if _, ok := m[v]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bs[current] = input[next]
|
||||||
|
current++
|
||||||
|
m[v] = 0
|
||||||
|
}
|
||||||
|
return string(bs[:current])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
你有一个单词列表 words 和一个模式 pattern,你想知道 words 中的哪些单词与模式匹配。
|
||||||
|
如果存在字母的排列 p ,使得将模式中的每个字母 x 替换为 p(x) 之后,我们就得到了所需的单词,那么单词与模式是匹配的。
|
||||||
|
(回想一下,字母的排列是从字母到字母的双射:每个字母映射到另一个字母,没有两个字母映射到同一个字母。)
|
||||||
|
返回 words 中与给定模式匹配的单词列表。
|
||||||
|
你可以按任何顺序返回答案。
|
||||||
|
*/
|
||||||
|
func FindAndReplacePattern(words []string, pattern string) []string {
|
||||||
|
patternWords := make([]string, 0)
|
||||||
|
for _, word := range words {
|
||||||
|
flag := true
|
||||||
|
ruleMap1 := make(map[byte]byte, len(pattern))
|
||||||
|
ruleMap2 := make(map[byte]byte, len(pattern))
|
||||||
|
for j := 0; j < len(pattern); j++ {
|
||||||
|
p := pattern[j]
|
||||||
|
w := word[j]
|
||||||
|
if _, ok := ruleMap1[p]; ok {
|
||||||
|
if ruleMap1[p] != w {
|
||||||
|
flag = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else if _, ok := ruleMap2[w]; ok {
|
||||||
|
flag = false
|
||||||
|
} else {
|
||||||
|
ruleMap1[p] = w
|
||||||
|
ruleMap2[w] = p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if flag {
|
||||||
|
patternWords = append(patternWords, word)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return patternWords
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
判断字符串是否为空
|
||||||
|
true 为空 false 不为空
|
||||||
|
*/
|
||||||
|
func IsBlank(str string) bool {
|
||||||
|
return !(len(str) > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000
|
||||||
|
"abab" true "aba" false
|
||||||
|
*/
|
||||||
|
func RepeatedSubstringPattern(s string) bool {
|
||||||
|
length := len(s)
|
||||||
|
if length == 0 || length == 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
n := 2
|
||||||
|
for n <= length {
|
||||||
|
mid := length / n
|
||||||
|
step := mid
|
||||||
|
index := 0
|
||||||
|
flag := true
|
||||||
|
for mid < length {
|
||||||
|
if (mid+step) > length || s[index:mid] != s[mid:mid+step] {
|
||||||
|
flag = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
index = index + step
|
||||||
|
mid = mid + step
|
||||||
|
}
|
||||||
|
if flag {
|
||||||
|
fmt.Println(step)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000
|
||||||
|
"abab" true "aba" false
|
||||||
|
*/
|
||||||
|
func RepeatedSubstringPattern2(s string) bool {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
size := len(s)
|
||||||
|
ss := (s + s)[1 : size*2-1]
|
||||||
|
return strings.Contains(ss, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetNext(p string) []int { //ababda
|
||||||
|
next := make([]int, len(p))
|
||||||
|
next[0] = -1
|
||||||
|
k := -1
|
||||||
|
i := 0
|
||||||
|
for i < len(p)-1 {
|
||||||
|
if k == -1 || p[i] == p[k] {
|
||||||
|
k++
|
||||||
|
i++
|
||||||
|
next[i] = k
|
||||||
|
} else {
|
||||||
|
k = next[k]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println(next)
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
KMP 算法,字符串模式匹配算法 主要是 GetNext
|
||||||
|
匹配 target 在 source 存在时的起始位置 (字符串搜索) "adabeabcabc", "abcabc"
|
||||||
|
*/
|
||||||
|
func StrMatch(source, target string) int {
|
||||||
|
slen := len(source)
|
||||||
|
tlen := len(target)
|
||||||
|
next := GetNext(target)
|
||||||
|
q := 0
|
||||||
|
for i := 0; i < slen; i++ {
|
||||||
|
for q > 0 && target[q] != source[i] {
|
||||||
|
q = next[q]
|
||||||
|
}
|
||||||
|
if target[q] == source[i] {
|
||||||
|
q++
|
||||||
|
}
|
||||||
|
if q == tlen {
|
||||||
|
return i - tlen + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
判断括号是否成对出现
|
||||||
|
输入: "()[]{}"
|
||||||
|
输出: true
|
||||||
|
输入: "(]"
|
||||||
|
输出: false
|
||||||
|
*/
|
||||||
|
func IsValid(s string) bool {
|
||||||
|
stack := make([]rune, len(s))
|
||||||
|
size := 0
|
||||||
|
for _, v := range s {
|
||||||
|
if v == '(' {
|
||||||
|
stack[size] = ')'
|
||||||
|
} else if v == '[' {
|
||||||
|
stack[size] = ']'
|
||||||
|
} else if v == '{' {
|
||||||
|
stack[size] = '}'
|
||||||
|
} else {
|
||||||
|
if size == 0 && stack[size] == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if size-1 < 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if v != stack[size-1] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
size--
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
size++
|
||||||
|
}
|
||||||
|
if size > 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为1000。
|
||||||
|
示例 1:
|
||||||
|
输入: "babad"
|
||||||
|
输出: "bab"
|
||||||
|
注意: "aba"也是一个有效答案。
|
||||||
|
示例 2:
|
||||||
|
输入: "cbbd"
|
||||||
|
输出: "bb"
|
||||||
|
*/
|
||||||
|
func LongestPalindrome(s string) string {
|
||||||
|
if len(s) == 0 || len(s) == 1 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
head, tail := 0, len(s)-1
|
||||||
|
seq := 0
|
||||||
|
max := 1
|
||||||
|
index := 0
|
||||||
|
for head < len(s)-1 {
|
||||||
|
tail = len(s) - 1
|
||||||
|
tempHead := head
|
||||||
|
for tempHead < tail {
|
||||||
|
if s[tempHead] == s[tail] {
|
||||||
|
seq++
|
||||||
|
if tail-tempHead <= 2 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
tempHead++
|
||||||
|
tail--
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tail--
|
||||||
|
if seq > 0 {
|
||||||
|
tempHead = tempHead - seq
|
||||||
|
tail = tail + seq
|
||||||
|
seq = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if seq > 0 {
|
||||||
|
if (seq*2 + (tail-tempHead)/2) >= max {
|
||||||
|
max = seq*2 + (tail-tempHead)/2
|
||||||
|
index = tempHead - seq + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
head++
|
||||||
|
seq = 0
|
||||||
|
}
|
||||||
|
return s[index : index+max]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
|
||||||
|
示例 1:
|
||||||
|
输入: "abcabcbb"
|
||||||
|
输出: 3
|
||||||
|
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
|
||||||
|
示例 2:
|
||||||
|
输入: "bbbbb"
|
||||||
|
输出: 1
|
||||||
|
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
|
||||||
|
示例 3:
|
||||||
|
输入: "pwwkew"
|
||||||
|
输出: 3
|
||||||
|
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
|
||||||
|
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
|
||||||
|
*/
|
||||||
|
func LengthOfLongestSubstring(s string) int {
|
||||||
|
next := GetNext(s)
|
||||||
|
m := make(map[byte]int, 0)
|
||||||
|
temp := 0
|
||||||
|
max := 0
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if _, ok := m[s[i]]; ok {
|
||||||
|
if max == 0 {
|
||||||
|
max = temp
|
||||||
|
}
|
||||||
|
i = next[i] - 1
|
||||||
|
m = make(map[byte]int, 0)
|
||||||
|
if temp > max {
|
||||||
|
max = temp
|
||||||
|
}
|
||||||
|
temp = 0
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
m[s[i]] = 1
|
||||||
|
temp++
|
||||||
|
}
|
||||||
|
if temp > max {
|
||||||
|
max = temp
|
||||||
|
}
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
最长公共前缀
|
||||||
|
编写一个函数来查找字符串数组中的最长公共前缀。
|
||||||
|
如果不存在公共前缀,返回空字符串 ""。
|
||||||
|
示例 1:
|
||||||
|
输入: ["flower","flow","flight"]
|
||||||
|
输出: "fl"
|
||||||
|
示例 2:
|
||||||
|
输入: ["dog","racecar","car"]
|
||||||
|
输出: ""
|
||||||
|
解释: 输入不存在公共前缀。
|
||||||
|
说明:
|
||||||
|
所有输入只包含小写字母 a-z 。
|
||||||
|
*/
|
||||||
|
|
||||||
|
func LongestCommonPrefix(strs []string) string {
|
||||||
|
if strs == nil || len(strs) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
bs := make([]rune, 0, 0)
|
||||||
|
strs = sort(strs)
|
||||||
|
fmt.Println(strs)
|
||||||
|
min := strs[0]
|
||||||
|
for index, v := range min {
|
||||||
|
for _, n := range strs {
|
||||||
|
if n[index] != byte(v) {
|
||||||
|
return string(bs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bs = append(bs, v)
|
||||||
|
}
|
||||||
|
return string(bs)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
字符串按长度排序 归并
|
||||||
|
*/
|
||||||
|
func sort(strs []string) []string {
|
||||||
|
if len(strs) == 1 {
|
||||||
|
return strs
|
||||||
|
}
|
||||||
|
mid := len(strs) / 2
|
||||||
|
left := sort(strs[:mid])
|
||||||
|
right := sort(strs[mid:])
|
||||||
|
return merger(left, right)
|
||||||
|
|
||||||
|
}
|
||||||
|
func merger(left []string, right []string) []string {
|
||||||
|
s := make([]string, 0, 0)
|
||||||
|
l, r := 0, 0
|
||||||
|
for l < len(left) && r < len(right) {
|
||||||
|
if len(left[l]) < len(right[r]) {
|
||||||
|
s = append(s, left[l])
|
||||||
|
l++
|
||||||
|
} else if len(left[l]) > len(right[r]) {
|
||||||
|
s = append(s, right[r])
|
||||||
|
r++
|
||||||
|
} else {
|
||||||
|
s = append(s, left[l])
|
||||||
|
s = append(s, right[r])
|
||||||
|
l++
|
||||||
|
r++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for l < len(left) {
|
||||||
|
s = append(s, left[l])
|
||||||
|
l++
|
||||||
|
}
|
||||||
|
for r < len(right) {
|
||||||
|
s = append(s, right[r])
|
||||||
|
r++
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
字符串全排列
|
||||||
|
题目:终端随机输入一串字符串,输出该字符串的所有排列。
|
||||||
|
例如,输入:“abc”,输出:abc、acb、bac、bca、cab、cba
|
||||||
|
*/
|
||||||
|
func RecursionPermutation(str string) []string {
|
||||||
|
arrays := make([]string, 0, 0)
|
||||||
|
arrays = permutation([]byte(str), 0, arrays)
|
||||||
|
return arrays
|
||||||
|
}
|
||||||
|
func permutation(s []byte, i int, arrays []string) []string {
|
||||||
|
if i == len(s)-1 {
|
||||||
|
arrays = append(arrays, string(s))
|
||||||
|
return arrays
|
||||||
|
}
|
||||||
|
for temp := i; temp < len(s); temp++ {
|
||||||
|
s[i], s[temp] = s[temp], s[i]
|
||||||
|
arrays = permutation(s, i+1, arrays)
|
||||||
|
s[i], s[temp] = s[temp], s[i]
|
||||||
|
}
|
||||||
|
return arrays
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。
|
||||||
|
换句话说,第一个字符串的排列之一是第二个字符串的子串。
|
||||||
|
示例1:
|
||||||
|
输入: s1 = "ab" s2 = "eidbaooo"
|
||||||
|
输出: True
|
||||||
|
解释: s2 包含 s1 的排列之一 ("ba").
|
||||||
|
示例2:
|
||||||
|
输入: s1= "ab" s2 = "eidboaoo"
|
||||||
|
输出: FalseRecursionPermutation
|
||||||
|
*/
|
||||||
|
//我们不用真的去算出s1的全排列,只要统计字符出现的次数即可。可以使用一个哈希表配上双指针来做
|
||||||
|
func CheckInclusion(s1 string, s2 string) bool {
|
||||||
|
if len(s1) > len(s2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
m := make(map[rune]byte, 0)
|
||||||
|
for index, v := range s1 {
|
||||||
|
m[v]++
|
||||||
|
m[rune(s2[index])]--
|
||||||
|
}
|
||||||
|
if allZero(m) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for temp := len(s1); temp < len(s2); temp++ {
|
||||||
|
m[rune(s2[temp])]--
|
||||||
|
m[rune(s2[temp-len(s1)])]++
|
||||||
|
if allZero(m) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func allZero(m map[rune]byte) bool {
|
||||||
|
for _, v := range m {
|
||||||
|
if v != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
|
||||||
|
示例 1:
|
||||||
|
输入: num1 = "2", num2 = "3"
|
||||||
|
输出: "6"
|
||||||
|
示例 2:
|
||||||
|
输入: num1 = "123", num2 = "456"
|
||||||
|
输出: "56088"
|
||||||
|
说明:
|
||||||
|
num1 和 num2 的长度小于110。
|
||||||
|
num1 和 num2 只包含数字 0-9。
|
||||||
|
num1 和 num2 均不以零开头,除非是数字 0 本身。
|
||||||
|
不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。
|
||||||
|
*/
|
||||||
|
|
||||||
|
func Multiply(num1 string, num2 string) string {
|
||||||
|
m := make(map[int]int, 0)
|
||||||
|
value := 0
|
||||||
|
// 映射0 到9 的ASCII码
|
||||||
|
for i := 48; i < 58; i++ {
|
||||||
|
m[i] = value
|
||||||
|
value++
|
||||||
|
}
|
||||||
|
n1 := len(num1)
|
||||||
|
n2 := len(num2)
|
||||||
|
result := make([]int, n1+n2)
|
||||||
|
rss := make([][]int, 0, 0)
|
||||||
|
index := n1 + n2 - 1
|
||||||
|
for t := len(num1) - 1; t >= 0; t-- {
|
||||||
|
var j, y int = 0, 0
|
||||||
|
for temp := len(num2) - 1; temp >= 0; temp-- {
|
||||||
|
k := m[int(num1[t])] * m[int(num2[temp])]
|
||||||
|
y = (k + j) % 10
|
||||||
|
j = (k + j) / 10
|
||||||
|
result[index] = y
|
||||||
|
index--
|
||||||
|
if temp == 0 { //如果到最高位了,就把商赋值
|
||||||
|
result[index] = j
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index = index + len(num2) - 1
|
||||||
|
rss = append(rss, result)
|
||||||
|
result = make([]int, n1+n2)
|
||||||
|
}
|
||||||
|
fmt.Println(rss)
|
||||||
|
index = n1 + n2 - 1
|
||||||
|
var sum int = 0
|
||||||
|
var j, y int = 0, 0
|
||||||
|
for m := index; m >= 0; m-- {
|
||||||
|
for i := 0; i < len(rss); i++ {
|
||||||
|
sum = sum + rss[i][m]
|
||||||
|
}
|
||||||
|
y = sum % 10
|
||||||
|
if y+j < 10 {
|
||||||
|
result[m] = y + j
|
||||||
|
j = sum / 10
|
||||||
|
} else {
|
||||||
|
result[m] = (y + j) % 10
|
||||||
|
j = sum/10 + (y+j)/10
|
||||||
|
}
|
||||||
|
sum = 0
|
||||||
|
}
|
||||||
|
for index, v := range result {
|
||||||
|
if index == len(result)-1 && v == 0 {
|
||||||
|
return "0"
|
||||||
|
}
|
||||||
|
if v == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = result[index:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
s := make([]string, len(result), len(result))
|
||||||
|
for index, v := range result {
|
||||||
|
fmt.Println(v)
|
||||||
|
s[index] = string(strconv.Itoa(int(v)))
|
||||||
|
}
|
||||||
|
fmt.Println(result)
|
||||||
|
fmt.Println(s)
|
||||||
|
return strings.Join(s, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func LetterCasePermutation(S string) []string {
|
||||||
|
if len(S) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
temps := ""
|
||||||
|
position := 0
|
||||||
|
sArray := make([]string, 0, 0)
|
||||||
|
sArray = dfs(temps, S, sArray, position)
|
||||||
|
return sArray
|
||||||
|
}
|
||||||
|
|
||||||
|
// 65-90 大写
|
||||||
|
// 97-122 小写
|
||||||
|
func dfs(temps string, s string, sArray []string, position int) []string {
|
||||||
|
if position == len(s) {
|
||||||
|
sArray = append(sArray, temps)
|
||||||
|
return sArray
|
||||||
|
}
|
||||||
|
//不是字母
|
||||||
|
if s[position] < 65 || s[position] > 122 || (s[position] > 90 && s[position] < 97) {
|
||||||
|
sArray = dfs(temps+string(rune(s[position])), s, sArray, position+1)
|
||||||
|
} else {
|
||||||
|
sArray = dfs(temps+strings.ToLower(string(rune(s[position]))), s, sArray, position+1)
|
||||||
|
sArray = dfs(temps+strings.ToUpper(string(rune(s[position]))), s, sArray, position+1)
|
||||||
|
}
|
||||||
|
return sArray
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Split replaces strings.Split.
|
||||||
|
// strings.Split has a giant pit because strings.Split ("", ",") will return a slice with an empty string.
|
||||||
|
func Split(s, sep string) []string {
|
||||||
|
if s == "" {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
return strings.Split(s, sep)
|
||||||
|
}
|
||||||
|
|
||||||
|
// JoinStrSkipEmpty concatenates multiple strings to a single string with the specified separator and skips the empty
|
||||||
|
// string.
|
||||||
|
func JoinStrSkipEmpty(sep string, s ...string) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for _, v := range s {
|
||||||
|
if v == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if buf.Len() > 0 {
|
||||||
|
buf.WriteString(sep)
|
||||||
|
}
|
||||||
|
buf.WriteString(v)
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// JoinStr concatenates multiple strings to a single string with the specified separator.
|
||||||
|
// Note that JoinStr doesn't skip the empty string.
|
||||||
|
func JoinStr(sep string, s ...string) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for i, v := range s {
|
||||||
|
if i != 0 {
|
||||||
|
buf.WriteString(sep)
|
||||||
|
}
|
||||||
|
buf.WriteString(v)
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReverseStr reverses the specified string without modifying the original string.
|
||||||
|
func ReverseStr(s string) string {
|
||||||
|
rs := []rune(s)
|
||||||
|
var r []rune
|
||||||
|
for i := len(rs) - 1; i >= 0; i-- {
|
||||||
|
r = append(r, rs[i])
|
||||||
|
}
|
||||||
|
return string(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAlphanumericNumByASCII gets the alphanumeric number based on the ASCII code value.
|
||||||
|
// Note that this function has a better performance than GetAlphanumericNumByRegExp, so this function is recommended.
|
||||||
|
func GetAlphanumericNumByASCII(s string) int {
|
||||||
|
num := int(0)
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
switch {
|
||||||
|
case 48 <= s[i] && s[i] <= 57: // digits
|
||||||
|
fallthrough
|
||||||
|
case 65 <= s[i] && s[i] <= 90: // uppercase letters
|
||||||
|
fallthrough
|
||||||
|
case 97 <= s[i] && s[i] <= 122: // lowercase letters
|
||||||
|
num++
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return num
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAlphanumericNumByASCIIV2 gets the alphanumeric number based on the ASCII code value.
|
||||||
|
// Because range by rune so the performance is worse than GetAlphanumericNumByASCII.
|
||||||
|
func GetAlphanumericNumByASCIIV2(s string) int {
|
||||||
|
num := int(0)
|
||||||
|
for _, c := range s {
|
||||||
|
switch {
|
||||||
|
case '0' <= c && c <= '9':
|
||||||
|
fallthrough
|
||||||
|
case 'a' <= c && c <= 'z':
|
||||||
|
fallthrough
|
||||||
|
case 'A' <= c && c <= 'Z':
|
||||||
|
num++
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return num
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAlphanumericNumByRegExp gets the alphanumeric number based on regular expression.
|
||||||
|
// Note that this function has a poor performance when compared to GetAlphanumericNumByASCII,
|
||||||
|
// so the GetAlphanumericNumByASCII is recommended.
|
||||||
|
func GetAlphanumericNumByRegExp(s string) int {
|
||||||
|
rNum := regexp.MustCompile(`\d`)
|
||||||
|
rLetter := regexp.MustCompile("[a-zA-Z]")
|
||||||
|
return len(rNum.FindAllString(s, -1)) + len(rLetter.FindAllString(s, -1))
|
||||||
|
}
|
||||||
@@ -13,7 +13,11 @@ package dtime
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"database/sql/driver"
|
||||||
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -88,3 +92,350 @@ func TodayNightUnix() int {
|
|||||||
tm1 := TodayTimeUnix() + 86400 - 1
|
tm1 := TodayTimeUnix() + 86400 - 1
|
||||||
return tm1
|
return tm1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Time be used to MySql timestamp converting.
|
||||||
|
type Time int64
|
||||||
|
|
||||||
|
// Scan scan time.
|
||||||
|
func (jt *Time) Scan(src interface{}) (err error) {
|
||||||
|
switch sc := src.(type) {
|
||||||
|
case time.Time:
|
||||||
|
*jt = Time(sc.Unix())
|
||||||
|
case string:
|
||||||
|
var i int64
|
||||||
|
i, err = strconv.ParseInt(sc, 10, 64)
|
||||||
|
*jt = Time(i)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value get time value.
|
||||||
|
func (jt Time) Value() (driver.Value, error) {
|
||||||
|
return time.Unix(int64(jt), 0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time get time.
|
||||||
|
func (jt Time) Time() time.Time {
|
||||||
|
return time.Unix(int64(jt), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duration be used toml unmarshal string time, like 1s, 500ms.
|
||||||
|
type Duration time.Duration
|
||||||
|
|
||||||
|
// UnmarshalText unmarshal text to duration.
|
||||||
|
func (d *Duration) UnmarshalText(text []byte) error {
|
||||||
|
tmp, err := time.ParseDuration(string(text))
|
||||||
|
if err == nil {
|
||||||
|
*d = Duration(tmp)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shrink will decrease the duration by comparing with context's timeout duration
|
||||||
|
// and return new timeout\context\CancelFunc.
|
||||||
|
func (d Duration) Shrink(c context.Context) (Duration, context.Context, context.CancelFunc) {
|
||||||
|
if deadline, ok := c.Deadline(); ok {
|
||||||
|
if ctimeout := time.Until(deadline); ctimeout < time.Duration(d) {
|
||||||
|
// deliver small timeout
|
||||||
|
return Duration(ctimeout), c, func() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithTimeout(c, time.Duration(d))
|
||||||
|
return d, ctx, cancel
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------
|
||||||
|
const (
|
||||||
|
YFormatNum = "2006"
|
||||||
|
YMFormatNum = "200601"
|
||||||
|
DateFormatNum = "20060102"
|
||||||
|
DateHFormatNum = "2006010215"
|
||||||
|
DateHMFormatNum = "200601021504"
|
||||||
|
DateTimeFormatNum = "20060102150405"
|
||||||
|
HFormatNum = "15"
|
||||||
|
HMFormatNum = "1504"
|
||||||
|
TimeFormatNum = "150405"
|
||||||
|
DateFormat = "2006-01-02"
|
||||||
|
TimeFormat = "15:04:05"
|
||||||
|
DateTimeFormat = "2006-01-02 15:04:05"
|
||||||
|
DateTimeFormatMilli = "2006-01-02 15:04:05.000"
|
||||||
|
DateTimeFormatMicro = "2006-01-02 15:04:05.000000"
|
||||||
|
DateTimeFormatNano = "2006-01-02 15:04:05.000000000"
|
||||||
|
)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Part 0: Get some useful infomation about time
|
||||||
|
//
|
||||||
|
|
||||||
|
// GetNowS gets unix timestamp in second
|
||||||
|
func GetNowS() int64 {
|
||||||
|
return time.Now().Unix()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNowMs gets unix timestamp in millisecond
|
||||||
|
func GetNowMs() int64 {
|
||||||
|
return time.Now().UnixNano() / int64(time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNowUs gets unix timestamp in microsecond
|
||||||
|
func GetNowUs() int64 {
|
||||||
|
return time.Now().UnixNano() / int64(time.Microsecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNowNs gets unix timestamp in nanosecond
|
||||||
|
func GetNowNs() int64 {
|
||||||
|
return time.Now().UnixNano()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNowDate gets now date in YYYY-MM-DD
|
||||||
|
func GetNowDate() string {
|
||||||
|
return time.Now().Format(DateFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNowDate gets now time in hh:mm:ss
|
||||||
|
func GetNowTime() string {
|
||||||
|
return time.Now().Format(TimeFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNowDateTimeZ gets now datetime with zone in YYYY-MM-DD hh:mm:ss Zone
|
||||||
|
// e.g. 2020-05-11 23:18:07 +08:00
|
||||||
|
func GetNowDateTimeZ() string {
|
||||||
|
return time.Now().Format("2006-01-02 15:04:05 Z07:00")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDayBeginMoment gets the starting moment of one day
|
||||||
|
func GetDayBeginMoment(t time.Time) time.Time {
|
||||||
|
y, m, d := t.Date()
|
||||||
|
n := time.Date(y, m, d, 0, 0, 0, 0, time.Local)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDayBeginMoment1 gets the starting moment of one day specified by UNIX time stamp
|
||||||
|
func GetDayBeginMoment1(uts int64) time.Time {
|
||||||
|
y, m, d := time.Unix(uts, 0).Date()
|
||||||
|
n := time.Date(y, m, d, 0, 0, 0, 0, time.Local)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDayEndMoment gets the ending moment of one day
|
||||||
|
func GetDayEndMoment(t time.Time) time.Time {
|
||||||
|
y, m, d := t.Date()
|
||||||
|
n := time.Date(y, m, d, 23, 59, 59, 999999999, time.Local)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDayEndMoment1 gets the ending moment of one day specified by UNIX time stamp
|
||||||
|
func GetDayEndMoment1(uts int64) time.Time {
|
||||||
|
y, m, d := time.Unix(uts, 0).Date()
|
||||||
|
n := time.Date(y, m, d, 23, 59, 59, 999999999, time.Local)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDayElapsedS gets the elapsed seconds since the starting moment of one day
|
||||||
|
func GetDayElapsedS(t time.Time) int64 {
|
||||||
|
return t.Unix() - GetDayBeginMoment(t).Unix()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDayElapsedMs gets the elapsed milliseconds since the starting moment of one day
|
||||||
|
func GetDayElapsedMs(t time.Time) int64 {
|
||||||
|
return (t.UnixNano() - GetDayBeginMoment(t).UnixNano()) / int64(time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDayElapsedUs gets the elapsed microseconds since the starting moment of one day
|
||||||
|
func GetDayElapsedUs(t time.Time) int64 {
|
||||||
|
return (t.UnixNano() - GetDayBeginMoment(t).UnixNano()) / int64(time.Microsecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDayElapsedNs gets the elapsed nanoseconds since the starting moment of one day
|
||||||
|
func GetDayElapsedNs(t time.Time) int64 {
|
||||||
|
return t.Unix() - GetDayBeginMoment(t).Unix()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDaysBtwTs gets the number of days between two timestamps and round down
|
||||||
|
func GetDaysBtwTs(ts0, ts1 int64) int64 {
|
||||||
|
return int64(math.Abs(float64(ts0-ts1))) / 86400
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHoursBtwTs gets the number of hours between two timestamps and round down
|
||||||
|
func GetHoursBtwTs(ts0, ts1 int64) int64 {
|
||||||
|
return int64(math.Abs(float64(ts0-ts1))) / 3600
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMinutesBtwTs gets the number of hours between two timestamps and round down
|
||||||
|
func GetMinutesBtwTs(ts0, ts1 int64) int64 {
|
||||||
|
return int64(math.Abs(float64(ts0-ts1))) / 60
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWeekday gets the weekday time
|
||||||
|
func GetWeekday(t time.Time, w time.Weekday) time.Time {
|
||||||
|
if t.Weekday() == w {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
d := w - t.Weekday()
|
||||||
|
if w == time.Sunday {
|
||||||
|
d += 7
|
||||||
|
} else if t.Weekday() == time.Sunday {
|
||||||
|
d -= 7
|
||||||
|
}
|
||||||
|
return t.AddDate(0, 0, int(d))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMonDate gets monday date in format 2006-01-02
|
||||||
|
func GetMonDate(t time.Time) string {
|
||||||
|
return GetWeekday(t, time.Monday).Format(DateFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTuesDate gets tuesday date in format 2006-01-02
|
||||||
|
func GetTuesDate(t time.Time) string {
|
||||||
|
return GetWeekday(t, time.Tuesday).Format(DateFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWedDate gets wednesday date in format 2006-01-02
|
||||||
|
func GetWedDate(t time.Time) string {
|
||||||
|
return GetWeekday(t, time.Wednesday).Format(DateFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetThursDate gets thursday date in format 2006-01-02
|
||||||
|
func GetThursDate(t time.Time) string {
|
||||||
|
return GetWeekday(t, time.Thursday).Format(DateFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFriDate gets friday date in format 2006-01-02
|
||||||
|
func GetFriDate(t time.Time) string {
|
||||||
|
return GetWeekday(t, time.Friday).Format(DateFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSatDate gets saturday date in format 2006-01-02
|
||||||
|
func GetSatDate(t time.Time) string {
|
||||||
|
return GetWeekday(t, time.Saturday).Format(DateFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSunDate gets sunday date in format 2006-01-02
|
||||||
|
func GetSunDate(t time.Time) string {
|
||||||
|
return GetWeekday(t, time.Sunday).Format(DateFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLeapYear checks the year whether is leap year
|
||||||
|
func IsLeapYear(year int) bool {
|
||||||
|
return (year%4 == 0 && year%100 != 0) || year%400 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSameYear checks the unix timestamp whether is the same year
|
||||||
|
func IsSameYear(uts1, uts2 int64) bool {
|
||||||
|
t1 := time.Unix(uts1, 0)
|
||||||
|
t2 := time.Unix(uts2, 0)
|
||||||
|
return t1.Format(YFormatNum) == t2.Format(YFormatNum)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSameMonth checks the unix timestamp whether is the same month
|
||||||
|
func IsSameMonth(uts1, uts2 int64) bool {
|
||||||
|
t1 := time.Unix(uts1, 0)
|
||||||
|
t2 := time.Unix(uts2, 0)
|
||||||
|
return t1.Format(YMFormatNum) == t2.Format(YMFormatNum)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSameDay checks the unix timestamp whether is the same day
|
||||||
|
func IsSameDay(uts1, uts2 int64) bool {
|
||||||
|
t1 := time.Unix(uts1, 0)
|
||||||
|
t2 := time.Unix(uts2, 0)
|
||||||
|
return t1.Format(DateFormatNum) == t2.Format(DateFormatNum)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSameHour checks the unix timestamp whether is the same hour
|
||||||
|
func IsSameHour(uts1, uts2 int64) bool {
|
||||||
|
t1 := time.Unix(uts1, 0)
|
||||||
|
t2 := time.Unix(uts2, 0)
|
||||||
|
return t1.Format(DateHFormatNum) == t2.Format(DateHFormatNum)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSameMinute checks the unix timestamp whether is the same minute
|
||||||
|
func IsSameMinute(uts1, uts2 int64) bool {
|
||||||
|
t1 := time.Unix(uts1, 0)
|
||||||
|
t2 := time.Unix(uts2, 0)
|
||||||
|
return t1.Format(DateHMFormatNum) == t2.Format(DateHMFormatNum)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSameWeek checks the unix timestamp whether is the same week
|
||||||
|
func IsSameWeek(uts1, uts2 int64) bool {
|
||||||
|
t1 := time.Unix(uts1, 0)
|
||||||
|
t2 := time.Unix(uts2, 0)
|
||||||
|
return GetMonDate(t1) == GetMonDate(t2)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Part 1: Common conversion about time
|
||||||
|
//
|
||||||
|
|
||||||
|
// DateTime2UTs converts datetime in YYYY-MM-DD hh:mm:ss to unix timestamp
|
||||||
|
func DateTime2UTs(dt string) int64 {
|
||||||
|
loc, _ := time.LoadLocation("Local")
|
||||||
|
t, err := time.ParseInLocation(DateTimeFormat, dt, loc)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return t.Unix()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UTs2DateTime converts unix timestamp to datetime in YYYY-MM-DD hh:mm:ss
|
||||||
|
func UTs2DateTime(uts int64) string {
|
||||||
|
return time.Unix(uts, 0).Format(DateTimeFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Part 2: A time counter to count time interval
|
||||||
|
//
|
||||||
|
|
||||||
|
// TimeCounter is used to count time interval
|
||||||
|
type TimeCounter struct {
|
||||||
|
time.Time
|
||||||
|
int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTimeCounter create a time counter
|
||||||
|
func NewTimeCounter() (t *TimeCounter) {
|
||||||
|
t = new(TimeCounter)
|
||||||
|
t.Set()
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set start timing
|
||||||
|
func (t *TimeCounter) Set() {
|
||||||
|
t.Time = time.Now()
|
||||||
|
t.int64 = t.Time.UnixNano()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetD return the time interval from the beginning to now in time.Duration
|
||||||
|
func (t *TimeCounter) GetD() time.Duration {
|
||||||
|
return time.Since(t.Time)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetS return the time interval from the beginning to now in second
|
||||||
|
func (t *TimeCounter) GetS() int64 {
|
||||||
|
return (time.Now().UnixNano() - t.int64) / int64(time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMs return the time interval from the beginning to now in millisecond
|
||||||
|
func (t *TimeCounter) GetMs() int64 {
|
||||||
|
return (time.Now().UnixNano() - t.int64) / int64(time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUs return the time interval from the beginning to now in microsecond
|
||||||
|
func (t *TimeCounter) GetUs() int64 {
|
||||||
|
return (time.Now().UnixNano() - t.int64) / int64(time.Microsecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNs return the time interval from the beginning to now in nanosecond
|
||||||
|
func (t *TimeCounter) GetNs() int64 {
|
||||||
|
return time.Now().UnixNano() - t.int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeCost count time cost
|
||||||
|
func TimeCost() func() time.Duration {
|
||||||
|
start := time.Now()
|
||||||
|
return func() time.Duration {
|
||||||
|
return time.Since(start)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The zfoo Authors
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||||
|
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
package ziputil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Zip compresses the specified files or dirs to zip archive.
|
||||||
|
// If a path is a dir don't need to specify the trailing path separator.
|
||||||
|
// For example calling Zip("archive.zip", "dir", "csv/baz.csv") will get archive.zip and the content of which is
|
||||||
|
// dir
|
||||||
|
// |-- foo.txt
|
||||||
|
// |-- bar.txt
|
||||||
|
// baz.csv
|
||||||
|
func Zip(zipPath string, paths ...string) error {
|
||||||
|
// create zip file
|
||||||
|
if err := os.MkdirAll(filepath.Dir(zipPath), os.ModePerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
archive, err := os.Create(zipPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer archive.Close()
|
||||||
|
|
||||||
|
// new zip writer
|
||||||
|
zipWriter := zip.NewWriter(archive)
|
||||||
|
defer zipWriter.Close()
|
||||||
|
|
||||||
|
// traverse the file or directory
|
||||||
|
for _, srcPath := range paths {
|
||||||
|
// remove the trailing path separator if path is a directory
|
||||||
|
srcPath = strings.TrimSuffix(srcPath, string(os.PathSeparator))
|
||||||
|
|
||||||
|
// visit all the files or directories in the tree
|
||||||
|
err = filepath.Walk(srcPath, func(path string, info fs.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a local file header
|
||||||
|
header, err := zip.FileInfoHeader(info)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// set compression
|
||||||
|
header.Method = zip.Deflate
|
||||||
|
|
||||||
|
// set relative path of a file as the header name
|
||||||
|
header.Name, err = filepath.Rel(filepath.Dir(srcPath), path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if info.IsDir() {
|
||||||
|
header.Name += string(os.PathSeparator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create writer for the file header and save content of the file
|
||||||
|
headerWriter, err := zipWriter.CreateHeader(header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
_, err = io.Copy(headerWriter, f)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unzip decompresses a zip file to specified directory.
|
||||||
|
// Note that the destination directory don't need to specify the trailing path separator.
|
||||||
|
func Unzip(zipPath, dstDir string) error {
|
||||||
|
// open zip file
|
||||||
|
reader, err := zip.OpenReader(zipPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer reader.Close()
|
||||||
|
for _, file := range reader.File {
|
||||||
|
if err := unzipFile(file, dstDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unzipFile(file *zip.File, dstDir string) error {
|
||||||
|
// create the directory of file
|
||||||
|
filePath := path.Join(dstDir, file.Name)
|
||||||
|
if file.FileInfo().IsDir() {
|
||||||
|
if err := os.MkdirAll(filePath, os.ModePerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// open the file
|
||||||
|
r, err := file.Open()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
// create the file
|
||||||
|
w, err := os.Create(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer w.Close()
|
||||||
|
|
||||||
|
// save the decompressed file content
|
||||||
|
_, err = io.Copy(w, r)
|
||||||
|
return err
|
||||||
|
}
|
||||||
Reference in new issue
Block a user