Upstream gookit/goutil state of the touched files before 54004efbfe
This commit is contained in:
1 parent
e2e5b5495f
commit
d489e58561
2 files changed
+391
No files matched your search
@@ -0,0 +1,200 @@
|
||||
package strutil
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BeforeFirst get substring before first sep.
|
||||
func BeforeFirst(s, sep string) string {
|
||||
if i := strings.Index(s, sep); i >= 0 {
|
||||
return s[:i]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// AfterFirst get substring after first sep.
|
||||
func AfterFirst(s, sep string) string {
|
||||
if i := strings.Index(s, sep); i >= 0 {
|
||||
return s[i+len(sep):]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// BeforeLast get substring before last sep.
|
||||
func BeforeLast(s, sep string) string {
|
||||
if i := strings.LastIndex(s, sep); i >= 0 {
|
||||
return s[:i]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// AfterLast get substring after last sep.
|
||||
func AfterLast(s, sep string) string {
|
||||
if i := strings.LastIndex(s, sep); i >= 0 {
|
||||
return s[i+len(sep):]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* String split operation
|
||||
*************************************************************/
|
||||
|
||||
// Cut alias of the strings.Cut
|
||||
func Cut(s, sep string) (before string, after string, found bool) {
|
||||
if i := strings.Index(s, sep); i >= 0 {
|
||||
return s[:i], s[i+len(sep):], true
|
||||
}
|
||||
return s, "", false
|
||||
}
|
||||
|
||||
// QuietCut always returns two substring.
|
||||
func QuietCut(s, sep string) (before string, after string) {
|
||||
before, after, _ = Cut(s, sep)
|
||||
return
|
||||
}
|
||||
|
||||
// MustCut always returns two substring.
|
||||
func MustCut(s, sep string) (before string, after string) {
|
||||
var ok bool
|
||||
before, after, ok = Cut(s, sep)
|
||||
if !ok {
|
||||
panic("cannot split input string to two nodes")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TrimCut always returns two substring and trim space for items.
|
||||
func TrimCut(s, sep string) (string, string) {
|
||||
before, after, _ := Cut(s, sep)
|
||||
return strings.TrimSpace(before), strings.TrimSpace(after)
|
||||
}
|
||||
|
||||
// SplitKV split string to key and value.
|
||||
func SplitKV(s, sep string) (string, string) { return TrimCut(s, sep) }
|
||||
|
||||
// SplitValid string to slice. will trim each item and filter empty string node.
|
||||
func SplitValid(s, sep string) (ss []string) { return Split(s, sep) }
|
||||
|
||||
// Split string to slice. will trim each item and filter empty string node.
|
||||
func Split(s, sep string) (ss []string) {
|
||||
if s = strings.TrimSpace(s); s == "" {
|
||||
return
|
||||
}
|
||||
|
||||
for _, val := range strings.Split(s, sep) {
|
||||
if val = strings.TrimSpace(val); val != "" {
|
||||
ss = append(ss, val)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SplitNValid string to slice. will filter empty string node.
|
||||
func SplitNValid(s, sep string, n int) (ss []string) { return SplitN(s, sep, n) }
|
||||
|
||||
// SplitN string to slice. will filter empty string node.
|
||||
func SplitN(s, sep string, n int) (ss []string) {
|
||||
if s = strings.TrimSpace(s); s == "" {
|
||||
return
|
||||
}
|
||||
|
||||
rawList := strings.Split(s, sep)
|
||||
for i, val := range rawList {
|
||||
if val = strings.TrimSpace(val); val != "" {
|
||||
if len(ss) == n-1 {
|
||||
ss = append(ss, strings.TrimSpace(strings.Join(rawList[i:], sep)))
|
||||
break
|
||||
}
|
||||
|
||||
ss = append(ss, val)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SplitTrimmed split string to slice.
|
||||
// will trim space for each node, but not filter empty
|
||||
func SplitTrimmed(s, sep string) (ss []string) {
|
||||
if s = strings.TrimSpace(s); s == "" {
|
||||
return
|
||||
}
|
||||
|
||||
for _, val := range strings.Split(s, sep) {
|
||||
ss = append(ss, strings.TrimSpace(val))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SplitNTrimmed split string to slice.
|
||||
// will trim space for each node, but not filter empty
|
||||
func SplitNTrimmed(s, sep string, n int) (ss []string) {
|
||||
if s = strings.TrimSpace(s); s == "" {
|
||||
return
|
||||
}
|
||||
|
||||
for _, val := range strings.SplitN(s, sep, n) {
|
||||
ss = append(ss, strings.TrimSpace(val))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 根据空白字符(空格,TAB,换行等)分隔字符串
|
||||
var whitespaceRegexp = regexp.MustCompile(`\s+`)
|
||||
|
||||
// SplitByWhitespace Separate strings by whitespace characters (space, TAB, newline, etc.)
|
||||
func SplitByWhitespace(s string) []string {
|
||||
return whitespaceRegexp.Split(s, -1)
|
||||
}
|
||||
|
||||
// Substr for a string. NOTE: strLn := len(runes)
|
||||
// if length <= 0, return pos to end.
|
||||
func Substr(s string, pos, length int) string {
|
||||
runes := []rune(s)
|
||||
strLn := len(runes)
|
||||
|
||||
// pos is too large
|
||||
if pos >= strLn {
|
||||
return ""
|
||||
}
|
||||
|
||||
stopIdx := pos + length
|
||||
if length == 0 || stopIdx > strLn {
|
||||
stopIdx = strLn
|
||||
} else if length < 0 {
|
||||
stopIdx = strLn + length
|
||||
}
|
||||
|
||||
return string(runes[pos:stopIdx])
|
||||
}
|
||||
|
||||
// SplitInlineComment for an inline text string. default is strict mode.
|
||||
func SplitInlineComment(val string, strict ...bool) (string, string) {
|
||||
// strict check: must with a space
|
||||
if len(strict) == 0 || strict[0] {
|
||||
if pos := strings.Index(val, " #"); pos > -1 {
|
||||
return strings.TrimRight(val[0:pos], " "), val[pos+1:]
|
||||
}
|
||||
if pos := strings.Index(val, " //"); pos > -1 {
|
||||
return strings.TrimRight(val[0:pos], " "), val[pos+1:]
|
||||
}
|
||||
return val, ""
|
||||
}
|
||||
|
||||
if pos := strings.IndexByte(val, '#'); pos > -1 {
|
||||
return strings.TrimRight(val[0:pos], " "), val[pos:]
|
||||
}
|
||||
if pos := strings.Index(val, "//"); pos > -1 {
|
||||
return strings.TrimRight(val[0:pos], " "), val[pos:]
|
||||
}
|
||||
return val, ""
|
||||
}
|
||||
|
||||
// FirstLine from command output
|
||||
func FirstLine(output string) string {
|
||||
if i := strings.IndexByte(output, '\n'); i >= 0 {
|
||||
return output[0:i]
|
||||
}
|
||||
return output
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package strutil_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/gookit/goutil/strutil"
|
||||
"github.com/gookit/goutil/x/assert"
|
||||
)
|
||||
|
||||
func TestBeforeAfter(t *testing.T) {
|
||||
// BeforeFirst
|
||||
assert.Eq(t, "abc", strutil.BeforeFirst("abc", ":"))
|
||||
assert.Eq(t, "abc", strutil.BeforeFirst("abc:123", ":"))
|
||||
assert.Eq(t, "abc", strutil.BeforeFirst("abc:123:456", ":"))
|
||||
|
||||
// AfterFirst
|
||||
assert.Eq(t, "", strutil.AfterFirst("abc", ":"))
|
||||
assert.Eq(t, "123", strutil.AfterFirst("abc:123", ":"))
|
||||
assert.Eq(t, "123:456", strutil.AfterFirst("abc:123:456", ":"))
|
||||
|
||||
// BeforeLast
|
||||
assert.Eq(t, "abc", strutil.BeforeLast("abc", ":"))
|
||||
assert.Eq(t, "abc", strutil.BeforeLast("abc:123", ":"))
|
||||
assert.Eq(t, "abc:123", strutil.BeforeLast("abc:123:456", ":"))
|
||||
|
||||
// AfterLast
|
||||
assert.Eq(t, "", strutil.AfterLast("abc", ":"))
|
||||
assert.Eq(t, "123", strutil.AfterLast("abc:123", ":"))
|
||||
assert.Eq(t, "456", strutil.AfterLast("abc:123:456", ":"))
|
||||
}
|
||||
|
||||
func TestCut(t *testing.T) {
|
||||
str := "hi,inhere"
|
||||
|
||||
b, a, ok := strutil.Cut(str, ",")
|
||||
assert.True(t, ok)
|
||||
assert.Eq(t, "hi", b)
|
||||
assert.Eq(t, "inhere", a)
|
||||
|
||||
b, a = strutil.MustCut(str, ",")
|
||||
assert.Eq(t, "hi", b)
|
||||
assert.Eq(t, "inhere", a)
|
||||
|
||||
assert.Panics(t, func() {
|
||||
strutil.MustCut("abc", ",")
|
||||
})
|
||||
|
||||
b, a = strutil.QuietCut(str, ",")
|
||||
assert.Eq(t, "hi", b)
|
||||
assert.Eq(t, "inhere", a)
|
||||
|
||||
b, a = strutil.TrimCut(str, ",")
|
||||
assert.Eq(t, "hi", b)
|
||||
assert.Eq(t, "inhere", a)
|
||||
|
||||
b, a = strutil.TrimCut(" hi , inhere \n", ",")
|
||||
assert.Eq(t, "hi", b)
|
||||
assert.Eq(t, "inhere", a)
|
||||
|
||||
b, a = strutil.SplitKV(" name = inhere \n", "=")
|
||||
assert.Eq(t, "name", b)
|
||||
assert.Eq(t, "inhere", a)
|
||||
|
||||
b, a, ok = strutil.Cut(str, "-")
|
||||
assert.False(t, ok)
|
||||
assert.Eq(t, str, b)
|
||||
assert.Eq(t, "", a)
|
||||
}
|
||||
|
||||
func TestSplit(t *testing.T) {
|
||||
ss := strutil.Split("a, , b,c", ",")
|
||||
assert.Eq(t, `[]string{"a", "b", "c"}`, fmt.Sprintf("%#v", ss))
|
||||
|
||||
ss = strutil.SplitValid("a, , b,c", ",")
|
||||
assert.Eq(t, `[]string{"a", "b", "c"}`, fmt.Sprintf("%#v", ss))
|
||||
|
||||
ss = strutil.SplitN("a, , b,c", ",", 3)
|
||||
assert.Eq(t, `[]string{"a", "b", "c"}`, fmt.Sprintf("%#v", ss))
|
||||
|
||||
ss = strutil.SplitN("a, , b,c", ",", 2)
|
||||
assert.Eq(t, `[]string{"a", "b,c"}`, fmt.Sprintf("%#v", ss))
|
||||
|
||||
ss = strutil.SplitNValid(" ", ",", 2)
|
||||
assert.Empty(t, ss)
|
||||
|
||||
ss = strutil.SplitN("origin https://github.com/gookit/gitw (push)", " ", 3)
|
||||
assert.Len(t, ss, 3)
|
||||
|
||||
ss = strutil.Split(" ", ",")
|
||||
assert.Nil(t, ss)
|
||||
}
|
||||
|
||||
func TestSplitTrimmed(t *testing.T) {
|
||||
ss := strutil.SplitTrimmed("a, , b,c", ",")
|
||||
assert.Eq(t, `[]string{"a", "", "b", "c"}`, fmt.Sprintf("%#v", ss))
|
||||
|
||||
ss = strutil.SplitTrimmed("", ",")
|
||||
assert.Empty(t, ss)
|
||||
|
||||
ss = strutil.SplitTrimmed(",", ",")
|
||||
assert.NotEmpty(t, ss)
|
||||
|
||||
ss = strutil.SplitNTrimmed("a, , b,c", ",", 2)
|
||||
assert.Eq(t, `[]string{"a", ", b,c"}`, fmt.Sprintf("%#v", ss))
|
||||
}
|
||||
|
||||
func TestSplitByWhitespace(t *testing.T) {
|
||||
ss := strutil.SplitByWhitespace("a b c")
|
||||
assert.Eq(t, `[]string{"a", "b", "c"}`, fmt.Sprintf("%#v", ss))
|
||||
|
||||
ss = strutil.SplitByWhitespace("a\r\nb\tc d")
|
||||
assert.Eq(t, `[]string{"a", "b", "c", "d"}`, fmt.Sprintf("%#v", ss))
|
||||
}
|
||||
|
||||
func TestSubstr(t *testing.T) {
|
||||
assert.Eq(t, "abc", strutil.Substr("abcDef", 0, 3))
|
||||
assert.Eq(t, "cD", strutil.Substr("abcDef", 2, 2))
|
||||
assert.Eq(t, "cDef", strutil.Substr("abcDef", 2, 0))
|
||||
assert.Eq(t, "", strutil.Substr("abcDEF", 23, 5))
|
||||
assert.Eq(t, "cDEF12", strutil.Substr("abcDEF123", 2, -1))
|
||||
assert.Eq(t, "cDEF", strutil.Substr("abcDEF123", 2, -3))
|
||||
}
|
||||
|
||||
func TestSplitInlineComment(t *testing.T) {
|
||||
tests := []struct {
|
||||
str string
|
||||
val string
|
||||
cmt string
|
||||
strict bool
|
||||
}{
|
||||
{
|
||||
str: "value",
|
||||
val: "value",
|
||||
},
|
||||
{
|
||||
str: "value // comments at end",
|
||||
val: "value",
|
||||
cmt: "// comments at end",
|
||||
},
|
||||
{
|
||||
str: "value // comments at end",
|
||||
val: "value",
|
||||
cmt: "// comments at end",
|
||||
strict: true,
|
||||
},
|
||||
{
|
||||
str: "value// comments at end",
|
||||
val: "value",
|
||||
cmt: "// comments at end",
|
||||
},
|
||||
{
|
||||
str: "value// comments at end",
|
||||
val: "value// comments at end",
|
||||
strict: true,
|
||||
},
|
||||
{
|
||||
str: "value # comments at end",
|
||||
val: "value",
|
||||
cmt: "# comments at end",
|
||||
},
|
||||
{
|
||||
str: "value # comments at end",
|
||||
val: "value",
|
||||
cmt: "# comments at end",
|
||||
strict: true,
|
||||
},
|
||||
{
|
||||
str: "value# comments at end",
|
||||
val: "value",
|
||||
cmt: "# comments at end",
|
||||
},
|
||||
{
|
||||
str: "value# comments at end",
|
||||
val: "value# comments at end",
|
||||
strict: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
idx := strutil.QuietString(i)
|
||||
val, comment := strutil.SplitInlineComment(tt.str+idx, tt.strict)
|
||||
if comment == "" {
|
||||
assert.Eq(t, tt.val+idx, val)
|
||||
assert.Eq(t, tt.cmt, comment)
|
||||
} else {
|
||||
assert.Eq(t, tt.val, val)
|
||||
assert.Eq(t, tt.cmt+idx, comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user