feat[golang]: 添加一些go的常用工具类

This commit is contained in:
godotg
2022-09-16 13:18:59 +08:00
parent 71cd7ccdbe
commit 770c2f3380
15 changed files with 4571 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+213
View File
@@ -18,7 +18,9 @@ import (
"encoding/json"
"fmt"
"reflect"
"regexp"
"strconv"
"strings"
"time"
"unsafe"
)
@@ -158,3 +160,214 @@ func HexToBye(hex string) []byte {
}
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)
}
+348
View File
@@ -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
}
+69
View File
@@ -13,6 +13,7 @@ package fileutil
import (
"errors"
"go/build"
"io/ioutil"
"os"
"path"
@@ -171,3 +172,71 @@ func GetCurrentDirectory() string {
dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
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
}
+448
View File
@@ -14,12 +14,16 @@ package fileutil
import (
"bufio"
"bytes"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"path"
"path/filepath"
"regexp"
"strings"
)
/**
@@ -96,3 +100,447 @@ func Download(sourceUrl, targetPath string) error {
func Name(filePath string) string {
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
}
+247
View File
@@ -12,6 +12,7 @@
package maputil
import (
"fmt"
"reflect"
"strings"
)
@@ -95,3 +96,249 @@ func Struct2Map(obj interface{}) map[string]interface{} {
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
}
+59
View File
@@ -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
}
+104
View File
@@ -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
}
+418
View File
@@ -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())
}
+153
View File
@@ -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")
}
+2
View File
@@ -65,3 +65,5 @@ func getRandSeek() int64 {
l.Unlock()
return time.Now().UnixNano() + randSeek
}
+884
View File
@@ -13,6 +13,7 @@ package stringutil
import (
"bytes"
"fmt"
"net/url"
"regexp"
"strconv"
@@ -250,3 +251,886 @@ func UnicodeEmojiCode(s string) string {
}
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))
}
+351
View File
@@ -13,7 +13,11 @@ package dtime
import (
"bytes"
"context"
"database/sql/driver"
"math"
"math/rand"
"strconv"
"sync"
"time"
)
@@ -88,3 +92,350 @@ func TodayNightUnix() int {
tm1 := TodayTimeUnix() + 86400 - 1
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)
}
}
+145
View File
@@ -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
}