stringx

package
v0.34.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 4, 2026 License: MIT Imports: 11 Imported by: 8

Documentation

Overview

Package stringx 提供常用的字符串操作工具函数。

包含字符串查找、过滤、截取、反转、大小写转换、多模式替换 (Replacer trie 最长匹配 / NewMask 敏感词遮罩)等功能。 所有函数均安全处理 UTF-8 编码的字符串。

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidStartPosition indicates the start position is invalid.
	ErrInvalidStartPosition = errors.New("start position is invalid")
	// ErrInvalidStopPosition indicates the stop position is invalid.
	ErrInvalidStopPosition = errors.New("stop position is invalid")
	// ErrInvalidRange indicates the substring range is inverted (start > stop).
	ErrInvalidRange = errors.New("substring range is inverted")
)

Functions

func Contains

func Contains(list []string, str string) bool

Contains 检查 str 是否在 list 中。

func ContainsAny

func ContainsAny(text string, keywords ...string) bool

ContainsAny 检查 text 中是否包含 keywords 中的任意一个关键词(不区分大小写)。

用法:

stringx.ContainsAny("Security vulnerability found", "security", "vuln") // true
stringx.ContainsAny("normal text", "critical", "major")                 // false

func DeleteFunc added in v0.33.0

func DeleteFunc(s string, del func(r rune) bool) string

DeleteFunc 从 s 中删除满足 del 条件的字符(剔除命中;方向自解释,命名对齐 slices.DeleteFunc 的口径)。

这是 Filter 在 v0.32 及以前的行为(#173 语义对齐后迁移至此):

// 剔除控制字符(净化回显)
clean := stringx.DeleteFunc(s, unicode.IsControl)

func Filter

func Filter(s string, filter func(r rune) bool) string

Filter 返回 s 中满足 filter 条件的字符(保留命中)。

⚠️ 语义变更(#173,破坏性):v0.32 及以前本函数是「剔除命中」(命中即删), 与同库 slicex.Filter(保留命中)同名反方向——谓词写反零报错,只是结果里 多/少了字符(实测事故:净化函数按「保留命中」的肌肉记忆写谓词,控制字符 被留在回显里)。v0.33.0 起与 slicex.Filter / 主流 filter 语义对齐(JS Array.filter、Java Stream.filter、Rust Iterator::filter 皆保留命中)。

迁移(按意图二选一):

  • 原「剔除命中」(filter(x)=true 的字符被删):改用 DeleteFunc;
  • 原谓词写的是「保留条件」却以为在删:改用 Filter/KeepFunc(结果反转)。

方向要在调用点自解释时用 KeepFunc(保留命中)/ DeleteFunc(剔除命中)对偶入口。

func FirstN

func FirstN(s string, n int, ellipsis ...string) string

FirstN 返回 s 的前 n 个字符,可选添加省略号。

func FormatTemplate

func FormatTemplate(template string, vars map[string]any) string

FormatTemplate 用 vars 替换 template 里的 {key} 占位符。

模板语法: `{key}` 形式,key 在 vars 里查找。

  • 未找到的 key 保留原 `{key}` 不替换(避免静默替换空字符串导致歧义)
  • 嵌套 key 不支持(只 1 层)
  • 业务场景: 错误信息模板("user {user_id} not found in tenant {tenant_id}") SQL 模板("SELECT * FROM users WHERE id = {id}"),log 模板

用法:

msg := stringx.FormatTemplate("user {user_id} not found", map[string]any{
    "user_id": 42,
})
// → "user 42 not found"

注意: 这个函数不转义;如果业务需要把用户输入嵌入 SQL/HTML,先做 业务自己的转义/参数化(否则是注入漏洞)。

func GenerateID

func GenerateID() string

GenerateID 生成基于时间戳的唯一 ID,格式: 20060102150405-<8位hex>。 适用于非严格唯一性要求的场景(如日志关联、临时标识)。 如需全局唯一,请使用 uuid。

func HasEmpty

func HasEmpty(args ...string) bool

HasEmpty 检查 args 中是否存在空字符串。

func IndentText

func IndentText(text, prefix string) string

IndentText 为多行文本的第二行及之后每行添加缩进前缀。 第一行不缩进,适用于续行对齐场景。

用法:

stringx.IndentText("line1\nline2\nline3", "  ")
// => "line1\n  line2\n  line3"

func Join

func Join(sep byte, elem ...string) string

Join 将多个字符串用 sep 连接,忽略空字符串。

func JoinByComma

func JoinByComma(str []string) string

JoinByComma 将字符串切片用逗号连接。

func JoinWith

func JoinWith(elem []string, sep string) string

JoinWith 将字符串切片用指定分隔符连接。

func KeepFunc added in v0.33.0

func KeepFunc(s string, keep func(r rune) bool) string

KeepFunc 返回 s 中满足 keep 条件的字符(保留命中;方向自解释,命名对齐 slices.KeepFunc 的口径)。与 Filter 同语义——Filter 与 slicex.Filter 对齐后即「保留命中」(#173)。

func LastN added in v0.32.0

func LastN(s string, n int, ellipsis ...string) string

LastN 返回 s 的最后 n 个字符(保尾截断:丢前缀、留后缀——文件名限长保 扩展名、路径限长保末段这类「尾部比头部更有信息量」的场景),可选在头部 添加省略号。与 FirstN 互为镜像:FirstN 保头、省略号拼尾;LastN 保尾、 省略号拼头。未发生截断时原样返回(不拼省略号)。

边界口径:n <= 0 一律按「保留 0 个」处理(返回空串+省略号)。与 FirstN 的 n<0 现状(其循环永不命中截断点 ⇒ 原样返回整串)有意不同——那是实现 怪癖不是语义,本函数按 TruncRunes 的「非法上限=全截断」口径走,不 panic。

func NotEmpty

func NotEmpty(args ...string) bool

NotEmpty 检查 args 中所有字符串是否都不为空。

func Pluralize

func Pluralize(count int, word string) string

Pluralize 返回英文单词的复数形式。 规则:

  • count == 1 → "1 word"
  • 以 "y" 结尾且前一个字符为辅音 → 去 "y" 加 "ies" (story → stories)
  • 以 "s", "sh", "ch", "x", "z" 结尾 → 加 "es" (box → boxes)
  • 其他 → 加 "s" (file → files)
  • word 为空 → "<count> "(计数仍然保留)

用法:

stringx.Pluralize(3, "file")   // "3 files"
stringx.Pluralize(1, "file")   // "1 file"
stringx.Pluralize(5, "story")  // "5 stories"

func Remove

func Remove(list []string, strs ...string) []string

Remove 从 strings 中移除指定的 strs。

func Reverse

func Reverse(s string) string

Reverse 反转字符串。

func SplitByComma

func SplitByComma(str string) []string

SplitByComma 将字符串按逗号分割。

func SplitCSVNonEmpty added in v0.22.0

func SplitCSVNonEmpty(s string) []string

SplitCSVNonEmpty 按逗号拆分字符串,逐项去掉首尾空白并丢弃空项。

与 SplitByComma 的区别:SplitByComma 就是 strings.Split(s, ",")——不 trim、 不过滤空项、空串返回 [""](1 个空元素),名字叫「按逗号拆」但实际把 「拆完顺手清干净」这层留给每个消费方各写一遍(典型样板:strings.Split → 逐项 TrimSpace → 跳过空项)。本原语把那层样板一次收进去。

与 SplitWith 的区别(不要拿 SplitWith 顶替):SplitWith 走 strings.Fields, 按**空白**切分,值里含空格时(如 glob 列表 "my glob")会被静默拆成两项, 改变语义。CSV 值的空白是有意义的("a b" 是一个值,不是两个),所以这条 面需要按逗号切的专门原语,而不是拿现有函数拼。

口径(写死在本函数契约里):

  • 逐项去掉**首尾**空白(strings.TrimSpace,含 Unicode 空白);
  • 去掉空白后为空的项**丢弃**("a,,b" → ["a","b"]);
  • **空串返回 nil**(不是空切片)——调用方普遍用 != nil / len()>0 区分 「没配」与「配了空」,返回空切片会把两者混同;
  • 全是空白或空项的输入(""、" "、",,")同样返回 nil。

用法:

stringx.SplitCSVNonEmpty("a, b,,c")  // ["a" "b" "c"]
stringx.SplitCSVNonEmpty("a,,b")     // ["a" "b"]
stringx.SplitCSVNonEmpty("")         // nil
stringx.SplitCSVNonEmpty("  ,  ")     // nil

func SplitWith

func SplitWith(str string, sep []string) []string

SplitWith 将字符串按指定分隔符列表分割后返回字段切片。

func Substr

func Substr(str string, start, stop int) (string, error)

Substr 返回 [start, stop) 范围内的子字符串,支持 UTF-8。

func TakeOne

func TakeOne(valid, or string) string

TakeOne 返回非空的字符串,如果 valid 非空则返回 valid,否则返回 or。

func TakeWithPriority

func TakeWithPriority(fns ...func() string) string

TakeWithPriority 返回第一个非空的函数返回值。

func ToCamelCase

func ToCamelCase(s string) string

ToCamelCase 将字符串首字母转为小写。

按 rune 处理(多字节首字符不被截断)。非法 UTF-8 字节不 panic:range 会把 非法字节解成 U+FFFD,其**实际**宽度是 1 字节,而 utf8.RuneLen(U+FFFD) 是 3 ⇒ 此前用 RuneLen 切片会越界(slice bounds out of range [3:1])。

func TruncBytes

func TruncBytes(s string, budget int) (string, bool)

TruncBytes 按字节预算截断(rune 对齐:预算落点切进多字节字符时回退到 RuneStart 边界,不产生非法 UTF-8 尾字节)。第二个返回值 = 是否真发生了 截断。budget<0 视为非法上限,按"全部截断"处理(返回空串),不 panic。

func TruncEllipsis

func TruncEllipsis(s string, n int) string

TruncEllipsis 截断并追加省略号(列表/标题/事件载荷展示面统一语义)。 n<=0 返回省略号;无需截断时原样返回。

**n 的语义是「截断到 n 个 rune」,不含省略号**:返回值长度为 n(未截断)或 n+1(发生截断,省略号算 1 个 rune)。若调用方的 n 是"总长度预算" (如提交状态描述的 140 字符上限),本函数会超出预算 1 个字符——那种场景用 TruncRunes 自行拼接,或先按预算减 1 再调用。

func TruncRunes

func TruncRunes(s string, n int) (string, bool)

TruncRunes 按 rune 截断,避免多字节字符被腰斩产生非法 UTF-8。 第二个返回值 = 是否真发生了截断。n<0 视为非法上限,按"全部截断"处理 (返回空串),不 panic。

func Union

func Union(first, second []string) []string

Union 合并两个字符串切片并去重。

Types

type Masker added in v0.16.0

type Masker struct {
	// contains filtered or unexported fields
}

Masker 形态脱敏器。

构造后只读,可安全并发使用。

func NewMasker added in v0.16.0

func NewMasker(rules []Rule) (*Masker, error)

NewMasker 创建形态脱敏器。

规则表由调用方完全提供,库不内置任何业务词表。返回 error 的情形:

  • Re == nil;
  • Name 为空;
  • Name 重复(形态标签重复会让审计结果有歧义);
  • Name 含定界符 '«' / '»' 或控制字符(输出将无法稳定解析);
  • Keep 为负数。

规则表为空(nil)时返回可用的空脱敏器,Mask 原样返回输入。

Example
rules := []Rule{
	{Name: "kv-secret", Re: regexp.MustCompile(`token=([^\s]+)`), Keep: 2},
}
m, err := NewMasker(rules)
if err != nil {
	fmt.Println("error:", err)
	return
}
fmt.Println(m.Mask("call api with token=abcdef123456"))
Output:
call api with to«kv-secret:****»56

func (*Masker) Mask added in v0.16.0

func (m *Masker) Mask(text string) string

Mask 对 text 做形态脱敏,返回打码后的文本。

打码是单次扫描合并:命中某条规则后,该段文本被替换为 head + «name:****» + tail(命中长度不足 Keep*4 时退化为 «name:****»), 替换结果不再参与任何规则的匹配。 未命中任何规则的文本原样保留。

type Replacer added in v0.7.0

type Replacer struct {
	// contains filtered or unexported fields
}

Replacer 多模式字符串替换器。 与 strings.Replacer 的差异:按 rune 对齐匹配(不撕裂 UTF-8), 且同一起点恒取最长匹配。

func NewMask added in v0.7.0

func NewMask(words []string, mask rune) (*Replacer, error)

NewMask 创建遮罩替换器:把 words 中每个词替换为等 rune 数的 mask 串。 用于敏感词遮罩场景,是 NewReplacer 的薄壳。

func NewReplacer added in v0.7.0

func NewReplacer(patterns, repls []string) (*Replacer, error)

NewReplacer 创建多模式替换器:patterns[i] 命中的子串替换为 repls[i]。 两者长度必须一致;空 pattern 或 nil patterns(不替换任何词)的处理见 返回值。同名 pattern 后写覆盖先写。

func (*Replacer) Replace added in v0.7.0

func (r *Replacer) Replace(text string) string

Replace 按 trie 最长匹配扫描 text 并替换。 按 rune 边界迭代:未命中的 rune 原字节保留(非法 UTF-8 序列不被 改写为 U+FFFD),命中的词整体替换。

type Rule added in v0.16.0

type Rule struct {
	// Name 形态标签,如 "url-userinfo";会原样出现在掩码里供审计读。
	// 不能为空、不能与同一规则表内其它规则的 Name 重复,也不能含定界符
	// '«' / '»' 或控制字符(否则输出无法被稳定解析)。
	Name string
	// Re 匹配待脱敏内容的正则。命中的**整段**文本被替换为掩码
	// (本库不区分捕获组,捕获组只影响匹配范围,不影响替换范围)。
	// 为 nil 时 NewMasker 报错。零宽匹配(空匹配)会被忽略。
	Re *regexp.Regexp
	// Keep 掩码保留首尾各几个字符作定位线索;0 = 全掩。
	// 语义是**封顶**:保留的字符数不随原值长度变化(见包内说明)。
	//
	// **命中长度不足 Keep*4 时整段全量打码(不做头尾保留)**——短值的头尾
	// 保留会首尾相接覆盖整段原文,原值会完整留在输出里,掩码形同虚设。
	// 调用方不必自己判断命中长度。
	//
	// 负数视为非法,NewMasker 报错。
	Keep int
}

Rule 一条脱敏规则。

规则表由调用方完全提供,库不内置任何词表:Re 负责「按形态认出」待脱敏 内容,Name 是形态标签(会出现在掩码里供审计读),Keep 控制保留的定位 线索位数。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL