先添加一个string类的引用(可访问性的区别 主要代码高亮看起来和全局变量不同)

local string = string
---@param str string
---@param delim string
---@return string[]
function string.split(str, delim)
  local result_str_list = {}
  str:gsub('[^' .. delim .. ']+', function(w)
    table.insert(result_str_list, w)
  end)
  return result_str_list
end
---@param str string
---@param char string? @trim space and newline when 'char' is nil
---@return string, number @result and trim count
function string.trim(str, pattern)
  pattern = pattern or '%t+%r+%n+%s+'
  return str:gsub(pattern, '')
end

---裁剪空格
function string.trim_space(str)
  return str:trim('%s+')
end

---裁剪长度
function string.trim_length(str, max_length)
  local length = 0
  return str:gsub(utf8.charpattern, function(char)
    length = length + #char
    return length <= max_length and char or ''
  end)
end
---@return boolean
function string.start_with(str, header)
  return str:sub(1, #header) == header
end

---@return boolean
function string.end_with(str, header)
  return str:sub(#str - #header + 1) == header
end

url的参数以问号后接kv的格式:url?k1=v1&k2=v2,目标是分割URL并提取多组kv值:

---@return string, table<string, string>
function string.parse_url(url)
  local params = {}
  local segs = url:split('?')
  local host = segs[1]
  if #segs > 1 then
      local param = segs[2]
      local param_contents = param:split('&')
      for i = 1, #param_contents do
          local temp_strs = param_contents[i]:split('=')
          if #temp_strs > 1 then
              params[temp_strs[1]] = temp_strs[2]
          end
      end
  end
  return host, params
end

例如,一个英文字符和一个中文字符都以1来计算。此方法适用于UTF-8编码的字符串:

---@return number
function string.char_count(content)
  if not content or type(content) ~= "string" or #content == 0 then
      return 0
  end
  local length = 0
  local index = 1
  while true do
    local cur_byte = content:byte(index)
    local byte_count = 1
    if cur_byte > 239 then byte_count = 4
    elseif cur_byte > 223 then byte_count = 3
    elseif cur_byte > 127 then byte_count = 2
    end
    index = index + byte_count
    length = length + 1
    if index > #content then
      break
    end
  end
  return length
end

local s = "你好,世界"
print(s:len())
print(s:char_count())

-- output --
15
5

中文字符在 Unicode 编码中位于 0xE0xx~0xEFxx,即 244~239,这一段在 UTF8 编码中占用 3 个字节。附上 UTF-8 编码的字节数对应表:

字节数 编码位(高位) 编码位(低位) 高位对应十进制
1 00~7F - 0~127
2 C0~DF 80~BF 128~223
3 E0~EF 80~BF 224~239
4 F0~FF 80~BF 240~255

Emoji 虽然也是 Unicode 编码,但是在很多场景中无法正常显示。为了保证内容有时需要进行裁剪。裁剪的方式也很简单,即排除掉 Unicode 中 Emoji 编码位的字符即可。

function string.filter_emoji(str)
  return str:gsub(utf8.charpattern, function(char)
    local codepoint = utf8.codepoint(char)
    if codepoint >= 0x231A and codepoint <= 0x27BF then return '' end
    if codepoint >= 0x2B1B and codepoint <= 0x2B55 then return '' end
    if codepoint >= 0x1F1E6 and codepoint <= 0x1F1FF then return '' end
    if codepoint >= 0x1F300 and codepoint <= 0x1F6FF then return '' end
    if codepoint >= 0x1F900 and codepoint <= 0x1FAFF then return '' end
    return char
  end)
end

当然了由于 Unicode 每个版本都会添加新的 Emoji,编码位也会随之新增,因此通常在版本更新后最好可以将新的编码位剔除。(这是一个年更的 function~)