Skip to content

📝 字符串库 - 简单易懂的文字处理指南 ​

📖 什么是字符串? ​

字符串就是文字内容,比如设备名称、错误信息、用户输入等。字符串库提供了处理这些文字的工具,就像Word软件里的查找、替换、格式化功能一样。

能做什么:

  • 🔍 查找文字(比如在日志中找"错误")
  • ✂️ 截取文字(比如从"温度:25度"中取出"25")
  • 🔄 替换文字(比如把"开"改成"关")
  • 📏 测量长度(比如检查密码是否够长)
  • 🔤 大小写转换(比如把"hello"变成"HELLO")

实际应用:

  • 处理设备返回的数据字符串
  • 格式化显示信息
  • 验证用户输入
  • 解析配置文件
  • 生成报表内容

🚀 3分钟快速上手 ​

示例1: 基本字符串操作 ​

lua
-- 获取字符串长度
local text = "Hello World"
print("字符串长度:", string.len(text))  -- 11
print("字符串长度:", #text)              -- 11 (更简洁的写法)

-- 大小写转换
local message = "Hello World"
print("大写:", string.upper(message))    -- HELLO WORLD
print("小写:", string.lower(message))    -- hello world

-- 反转字符串
print("反转:", string.reverse("Hello"))   -- olleH

示例2: 字符串截取和查找 ​

lua
-- 截取子字符串
local data = "温度:25.6度,湿度:65%"
local temp = string.sub(data, 4, 8)  -- 截取"25.6"
print("温度值:", temp)

-- 查找字符串
local log_entry = "[ERROR] 设备连接失败"
local start_pos, end_pos = string.find(log_entry, "ERROR")
if start_pos then
    print("找到错误标记,位置:", start_pos, "-", end_pos)
end

-- 从右侧开始查找
local path = "/spiffs/config/app_config.txt"
local last_slash = string.find(path, "/", -1, true)  -- 从右侧查找最后一个/
local filename = string.sub(path, last_slash + 1)
print("文件名:", filename)  -- app_config.txt

示例3: 字符串格式化 ​

lua
-- 格式化输出
local name = "张三"
local age = 25
local salary = 8500.50

print(string.format("姓名: %s, 年龄: %d", name, age))
print(string.format("薪资: %.2f元", salary))

-- 对齐和填充
print(string.format("|%10s|%5d|%8.2f|", "产品A", 100, 29.99))
print(string.format("|%-10s|%5d|%8.2f|", "产品B", 50, 15.50))

-- 十六进制显示
local color_red = 255
local color_green = 128
local color_blue = 64
print(string.format("RGB颜色: #%02X%02X%02X", color_red, color_green, color_blue))

示例4: 模式匹配和替换 ​

lua
-- 提取数字
local sensor_data = "温度:23.5℃,湿度:68%,压力:1013.2hPa"
for value in string.gmatch(sensor_data, "%d+%.?%d*") do
    print("传感器数值:", value)
end

-- 验证手机号
local function is_valid_phone(phone)
    return string.match(phone, "^1[3-9]%d%d%d%d%d%d%d%d%d$") ~= nil
end

print(is_valid_phone("13812345678"))  -- true
print(is_valid_phone("12345678901"))  -- false

-- 批量替换
local template = "Hello {{name}}, welcome to {{place}}!"
local result = string.gsub(template, "{{(.-)}}", {
    name = "张三",
    place = "LPPLC系统"
})
print(result)  -- Hello 张三, welcome to LPPLC系统!

📚 API参考 ​

📋 函数列表 ​

函数名功能描述示例
📏 string.len(s)获取字符串长度string.len("Hello") → 5
✂️ string.sub(s, start, [end])提取子字符串string.sub("Hello", 1, 3) → "Hel"
🔍 string.find(s, pattern, [init, plain])查找字符串模式string.find("Hello", "l") → 3
🎯 string.match(s, pattern, [init])匹配字符串模式string.match("123", "%d+") → "123"
🔁 string.gmatch(s, pattern)全局匹配迭代器string.gmatch("a b", "%w")
🔄 string.gsub(s, pattern, repl, [n])全局替换string.gsub("Hello", "l", "L") → "HeLLo"
🎨 string.format(formatstring, ...)格式化字符串string.format("%d", 123) → "123"
🔠 string.upper(s)转换为大写string.upper("hello") → "HELLO"
🔡 string.lower(s)转换为小写string.lower("HELLO") → "hello"
🔁 string.reverse(s)反转字符串string.reverse("Hello") → "olleH"
🔁 string.rep(s, n, [sep])重复字符串string.rep("a", 3) → "aaa"
#️⃣ string.char(...)字符码转字符string.char(65) → "A"
#️⃣ string.byte(s, [i, j])字符转字符码string.byte("A") → 65
🔢 string.tohex(hexstr)十六进制文本 → 字节串string.tohex("0103") → "\x01\x03"
📤 string.fromhex(s)字节串 → 大写十六进制文本string.fromhex("\x01\x03") → "0103"

📏 string.len(s) ​

获取字符串长度

参数:

  • s (string): 目标字符串

返回值:

  • number: 字符串长度(字节数)

示例:

lua
print(string.len("Hello"))      -- 5
print(string.len("你好"))       -- 6 (UTF-8编码)
print(string.len(""))           -- 0

-- 也可以使用#操作符
print(#"Hello")                 -- 5

✂️ string.sub(s, start, [end]) ​

提取子字符串

参数:

  • s (string): 源字符串
  • start (number): 起始位置(1开始)
  • end (number, 可选): 结束位置,默认为字符串末尾

返回值:

  • string: 子字符串

示例:

lua
local text = "Hello World"

print(string.sub(text, 1, 5))   -- "Hello"
print(string.sub(text, 7))      -- "World"
print(string.sub(text, -5))     -- "World" (负数从末尾计算)
print(string.sub(text, -5, -1)) -- "World"

🔠 string.upper(s) / 🔡 string.lower(s) ​

大小写转换

参数:

  • s (string): 目标字符串

返回值:

  • string: 转换后的字符串

示例:

lua
print(string.upper("hello"))    -- "HELLO"
print(string.lower("WORLD"))    -- "world"
print(string.upper("Hello World")) -- "HELLO WORLD"

🔍 string.find(s, pattern, [init, plain]) ​

查找字符串

参数:

  • s (string): 源字符串
  • pattern (string): 查找模式
  • init (number, 可选): 开始位置,默认为1
  • plain (boolean, 可选): 是否纯文本搜索,默认false

返回值:

  • number, number: 找到的起始和结束位置,未找到返回nil

示例:

lua
local text = "Hello World Hello"

-- 基本查找
local start, end_pos = string.find(text, "World")
print(start, end_pos)  -- 7 11

-- 从指定位置开始查找
local start2 = string.find(text, "Hello", 2)
print(start2)  -- 13

-- 纯文本搜索(忽略模式字符)
local pos = string.find("a.b", ".", 1, true)
print(pos)  -- 2

🎯 string.match(s, pattern, [init]) ​

模式匹配

参数:

  • s (string): 源字符串
  • pattern (string): 匹配模式
  • init (number, 可选): 开始位置

返回值:

  • string: 匹配的字符串,未匹配返回nil

示例:

lua
-- 提取数字
local text = "价格是123.45元"
local price = string.match(text, "%d+%.%d+")
print(price)  -- "123.45"

-- 提取邮箱
local email_text = "联系邮箱:user@example.com"
local email = string.match(email_text, "[%w%.]+@[%w%.]+")
print(email)  -- "user@example.com"

🔄 string.gsub(s, pattern, repl, [n]) ​

全局替换

参数:

  • s (string): 源字符串
  • pattern (string): 匹配模式
  • repl (string/function/table): 替换内容
  • n (number, 可选): 最大替换次数

返回值:

  • string, number: 替换后的字符串和替换次数

示例:

lua
-- 基本替换
local text = "Hello World Hello"
local result = string.gsub(text, "Hello", "Hi")
print(result)  -- "Hi World Hi"

-- 限制替换次数
local result2 = string.gsub(text, "Hello", "Hi", 1)
print(result2)  -- "Hi World Hello"

-- 使用函数替换
local result3 = string.gsub("abc123def456", "%d+", function(num)
    return tostring(tonumber(num) * 2)
end)
print(result3)  -- "abc246def912"

🎨 string.format(formatstring, ...) ​

格式化字符串

参数:

  • formatstring (string): 格式字符串
  • ... (any): 格式化参数

返回值:

  • string: 格式化后的字符串

格式说明符:

  • %d: 整数
  • %f: 浮点数
  • %s: 字符串
  • %x: 十六进制
  • %o: 八进制
  • %c: 字符

示例:

lua
-- 基本格式化
print(string.format("姓名: %s, 年龄: %d", "张三", 25))
-- 输出: 姓名: 张三, 年龄: 25

-- 数字格式化
print(string.format("价格: %.2f元", 123.456))
-- 输出: 价格: 123.46元

-- 十六进制
print(string.format("颜色: #%02X%02X%02X", 255, 128, 0))
-- 输出: 颜色: #FF8000

-- 对齐和填充
print(string.format("|%10s|%-10s|", "右对齐", "左对齐"))
-- 输出: |      右对齐|左对齐      |

实用例程 ​

lua
--开启mc协议服务器,端口为5000
mcs=mc_server.start(5000,4,1024)
--读取电表
state=data.creat(0)
dlt=com_dlt645.creat('<设备ID或凭据>',state)--建立电表驱动参数为表号12位表号2007广播表号
kwh=data.creat(0)
kw=data.creat(0)
dlt:add_channel(com_dlt645.KW_2007,kw);--读取功率
dlt:add_channel(com_dlt645.KWH_2007,kwh);--读取正向有功总电量到D0
com1=com.setup(1,2400,com.EVEN,8,1)--打开串口父设备2007表默认2400BPS
com1:add_device(dlt)--在串口父设备中加入电表子设备
com1:start()--启动串口父设备
local 倍率=1

  dssc=timer.start(5000,function()--启动定时器 5 秒发布一个数据
 local kwhreal=kwh:get_num()*倍率
 --local kwhreal = string.format("%.2f",kwh:get_num()*倍率)		
  local jsontable =
{
	name="<名称>",
	kw=kw:get_num()*倍率,
	kwh = kwhreal,
}
plc.df(200,kwhreal)--把电能浮点值保存到D200
local jsonstring = json.encode(jsontable)
--print(jsonstring)
print("<名称>",plc.df(200),"<名称>",plc.dd(204))	
end
)

把上面的地址、主题、引脚替换成你项目里的即可。

🔁 string.gmatch(s, pattern) ​

全局匹配迭代器

参数:

  • s (string): 源字符串
  • pattern (string): 匹配模式

返回值:

  • function: 迭代器函数

示例:

lua
-- 提取所有数字
local text = "价格123元,数量45个,总计5670元"
for num in string.gmatch(text, "%d+") do
    print("数字:", num)
end
-- 输出:
-- 数字: 123
-- 数字: 45
-- 数字: 5670

-- 提取单词
local sentence = "Hello World Lua Programming"
for word in string.gmatch(sentence, "%w+") do
    print("单词:", word)
end

🔁 string.rep(s, n, [sep]) ​

重复字符串

参数:

  • s (string): 要重复的字符串
  • n (number): 重复次数
  • sep (string, 可选): 分隔符

返回值:

  • string: 重复后的字符串

示例:

lua
print(string.rep("*", 10))           -- "**********"
print(string.rep("abc", 3))          -- "abcabcabc"
print(string.rep("Hello", 3, "-"))   -- "Hello-Hello-Hello"

-- 创建分隔线
local separator = string.rep("=", 50)
print(separator)

🔁 string.reverse(s) ​

反转字符串

参数:

  • s (string): 目标字符串

返回值:

  • string: 反转后的字符串

示例:

lua
print(string.reverse("Hello"))       -- "olleH"
print(string.reverse("12345"))       -- "54321"

-- 检查回文
local function is_palindrome(s)
    local clean = string.lower(string.gsub(s, "%W", ""))
    return clean == string.reverse(clean)
end

print(is_palindrome("A man a plan a canal Panama"))  -- true

#️⃣ string.char(...) ​

字符码转字符

参数:

  • ... (number): 字符码

返回值:

  • string: 对应的字符

示例:

lua
print(string.char(65))          -- "A"
print(string.char(66, 67, 68))  -- "BCD"
print(string.char(20320, 22909)) -- "你好" (UTF-8编码)

-- 创建特殊字符
print(string.char(10))          -- 换行符
print(string.char(9))           -- 制表符

实用例程 ​

lua
local GatewaySn="<设备ID或凭据>"
local deviceSn="<设备ID或凭据>"

serial=com.setup(2,9600,com.NONE,8,1)--建立串口1设备 
serial:set_timeout(1000)--设定接收超时时间 
function HexStringToBytes(str)
    local bytes = {}
    for i = 1, #str, 2 do
        local hex = str:sub(i, i + 1)
        bytes[#bytes + 1] = string.char(tonumber(hex, 16))
	end
    return table.concat(bytes)
end

jsj_data=HexStringToBytes("<设备ID或凭据>")
local function isYearGreaterOrEqualTo2024()
    local currentYear = tonumber(os.date("%Y", os.time())) -- 提取当前年份
    return currentYear >= 2024
end
mqttClient=mqtt.creat("192.168.1.100", 1883, deviceSn, "<名称>", "<名称>")--建立MQTT对象
mqtt.reg(mqttClient,mqtt.CONNECTED,function()--注册登陆成功回调函数
		print("连接成功")
		local onlineStatus = {
			device_id = deviceSn,
			device_type = "JSJ",
			online = true
		}
		pushData(onlineStatus)
	end

把上面的地址、主题、引脚替换成你项目里的即可。

#️⃣ string.byte(s, [i, j]) ​

字符转字符码

参数:

  • s (string): 源字符串
  • i (number, 可选): 起始位置,默认为1
  • j (number, 可选): 结束位置,默认为i

返回值:

  • number...: 字符码

示例:

lua
print(string.byte("A"))         -- 65
print(string.byte("ABC", 1))    -- 65
print(string.byte("ABC", 1, 3)) -- 65 66 67

-- 获取字符串所有字符码
local text = "Hello"
for i = 1, #text do
    print(string.byte(text, i))
end

实用例程 ​

lua
--有符号数字转转无符号数字
function Unsigned(i,j)  --i数值,j数据长度
	if i<0  then 
		if j==16 or j==nil then   k=i+65536  end
		if j==32 then    k=i+4294967296  end
	else
		k=i 
    end
	return k
end	

--CRC校验
function crc16(buf)
    local init = 0xFFFF;
    local poly = 0xA001;
    local len=string.len(buf);
    local ret = init;
    local byte=0;
 
    for j=1,len,1 do
        byte = string.byte(buf, j);
        ret=((ret ~ byte) & 0xFFFF); --符号“~”是异或运算
 
        for i=1,8,1 do
            if((ret & 0x0001)>0) then
                ret = (ret >> 1);
                ret = ((ret ~ poly) & 0xFFFF);
            else
                ret= (ret >> 1);
            end;
        end
    end
    local hi = ((ret >> 8) & 0xFF);
    local lo = (ret & 0xFF);
    ret = ( (hi << 8) | lo ); --IO收发crc检验是先高后低
    return string.format("%04X",ret);
end


--程序运行

把上面的地址、主题、引脚替换成你项目里的即可。

🔢 string.tohex(hexstr) ​

把十六进制文本转成真正的字节串。发报文给串口设备之前用它。

参数:

  • hexstr (string): 十六进制文本,如 "010300000001840A"。大小写都可以;空格、逗号、横线等非 0-9/A-F 的字符会被直接忽略

返回值:

  • string: 转换后的二进制字节串(长度是有效 hex 字符数的一半)

示例:

lua
local frame = string.tohex("010300000001840A")   -- 8 个字节
print(#frame)                                     -- 8

-- 忽略分隔符:下面两种写法结果相同
print(string.tohex("01 03 00"))
print(string.tohex("010300"))

-- 也可以写成冒号形式(字符串元表里注册了本库函数)
print(("0103"):tohex())

注意:

  • 有效 hex 字符个数是奇数时,最后多出来的那半个会被丢掉,返回长度向下取整。少写一位不会报错,只会得到一条缺字节的报文,所以拼串时最好先 print(#frame) 确认长度。
  • 这个函数会就地把传入的那段字符串转成大写。Lua 里内容相同的字符串是同一个对象, 所以这一改写会波及脚本里所有同样内容的字符串:
lua
local s = "abcd"
local t = "abcd"          -- 和 s 是同一个对象
string.tohex(s)
print(s, t)              -- ABCD  ABCD

发报文时传的都是纯 hex 文本,大小写不影响语义,正常用法不会出问题; 但不要拿它去处理普通文本变量(比如还要原样发给设备的 ASCII 字符串)。

📤 string.fromhex(s) ​

把字节串转成大写十六进制文本,用于打印 / 记录收到的报文。

参数:

  • s (string): 任意字节串(通常是 serial:recv() 的返回值)

返回值:

  • string: 大写十六进制文本,长度为入参字节数的 2 倍

示例:

lua
local y = serial:recv()
if y then print("收到", y:fromhex()) end      -- 01 03 00 01 ... → "01030001..."

print(string.fromhex("\x0a\xff"))              -- "0AFF"

这两个名字的方向和直觉是反的

tohex = 转成字节(hex 文本进、字节出),fromhex = 从字节解出 hex 文本。 而 encoder 里同名函数方向恰好相反(encoder.tohex 是字节 → hex 文本)。 两条规则不要混着记,串口收发例程里的对照表见 综合例程:串口裸报文收发。

🎨 模式匹配 ​

字符类 ​

  • .: 任意字符
  • %a: 字母
  • %d: 数字
  • %l: 小写字母
  • %u: 大写字母
  • %w: 字母数字
  • %s: 空白字符
  • %p: 标点符号
  • %c: 控制字符
  • %x: 十六进制数字

修饰符 ​

  • *: 0次或多次
  • +: 1次或多次
  • ?: 0次或1次
  • ^: 行首
  • $: 行尾
  • []: 字符集
  • (): 捕获组

示例:

lua
-- 验证邮箱格式
local function validate_email(email)
    local pattern = "^[%w%.]+@[%w%.]+%.%w+$"
    return string.match(email, pattern) ~= nil
end

print(validate_email("user@example.com"))  -- true
print(validate_email("invalid.email"))     -- false

-- 提取URL组件
local url = "https://www.example.com:8080/path?query=value"
local protocol, host, port, path = string.match(url, 
    "^(%w+)://([^:/]+):?(%d*)(/.*)")
print("协议:", protocol)  -- https
print("主机:", host)      -- www.example.com
print("端口:", port)      -- 8080
print("路径:", path)      -- /path?query=value

🚀 使用示例 ​

示例1: 文本处理工具 ​

lua
-- 文本处理工具示例
function text_processing_example()
    print("=== 文本处理工具示例 ===")
    
    -- 文本清理函数
    local function clean_text(text)
        -- 移除多余空白
        text = string.gsub(text, "%s+", " ")
        -- 移除首尾空白
        text = string.gsub(text, "^%s*(.-)%s*$", "%1")
        return text
    end
    
    -- 单词统计函数
    local function word_count(text)
        local count = 0
        for word in string.gmatch(text, "%w+") do
            count = count + 1
        end
        return count
    end
    
    -- 提取数字函数
    local function extract_numbers(text)
        local numbers = {}
        for num in string.gmatch(text, "%d+%.?%d*") do
            table.insert(numbers, tonumber(num))
        end
        return numbers
    end
    
    -- 测试文本
    local sample_text = [[
        这是一个   测试文本。
        包含123个数字,45.67是小数。
        总共有   多个    空格需要清理。
        价格是99.99元。
    ]]
    
    print("原始文本:")
    print(sample_text)
    
    -- 清理文本
    local cleaned = clean_text(sample_text)
    print("\n清理后文本:")
    print(cleaned)
    
    -- 统计单词
    local words = word_count(cleaned)
    print("\n单词数量:", words)
    
    -- 提取数字
    local numbers = extract_numbers(cleaned)
    print("\n提取的数字:")
    for i, num in ipairs(numbers) do
        print(string.format("  %d: %g", i, num))
    end
    
    -- 文本分析
    local char_count = string.len(cleaned)
    local line_count = 0
    for line in string.gmatch(cleaned, "[^\n]+") do
        line_count = line_count + 1
    end
    
    print("\n文本统计:")
    print("字符数:", char_count)
    print("行数:", line_count)
    print("单词数:", words)
    print("数字个数:", #numbers)
end

text_processing_example()

示例2: 数据验证 ​

lua
-- 数据验证示例
function data_validation_example()
    print("\n=== 数据验证示例 ===")
    
    -- 验证函数集合
    local validators = {
        -- 手机号验证
        phone = function(phone)
            local pattern = "^1[3-9]%d%d%d%d%d%d%d%d%d$"
            return string.match(phone, pattern) ~= nil
        end,
        
        -- 邮箱验证
        email = function(email)
            local pattern = "^[%w%.%-_]+@[%w%.%-_]+%.%w+$"
            return string.match(email, pattern) ~= nil
        end,
        
        -- 身份证验证(简化版)
        id_card = function(id)
            local pattern = "^%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d[%dXx]?%d?%d?$"
            return string.len(id) == 18 and string.match(id, pattern) ~= nil
        end,
        
        -- 密码强度验证
        password = function(pwd)
            if string.len(pwd) < 8 then return false, "密码长度至少8位" end
            if not string.match(pwd, "%d") then return false, "密码必须包含数字" end
            if not string.match(pwd, "%l") then return false, "密码必须包含小写字母" end
            if not string.match(pwd, "%u") then return false, "密码必须包含大写字母" end
            return true, "密码强度合格"
        end,
        
        -- URL验证
        url = function(url)
            local pattern = "^https?://[%w%.%-_]+[%w%.%-_/]*$"
            return string.match(url, pattern) ~= nil
        end
    }
    
    -- 测试数据
    local test_data = {
        {type = "phone", value = "13812345678", expected = true},
        {type = "phone", value = "12345678901", expected = false},
        {type = "email", value = "user@example.com", expected = true},
        {type = "email", value = "invalid.email", expected = false},
        {type = "password", value = "Abc12345", expected = true},
        {type = "password", value = "123456", expected = false},
        {type = "url", value = "https://www.example.com", expected = true},
        {type = "url", value = "not-a-url", expected = false}
    }
    
    print("数据验证测试:")
    for i, test in ipairs(test_data) do
        local validator = validators[test.type]
        local result, message = validator(test.value)
        local status = result and "✓" or "✗"
        local expected_status = test.expected and "✓" or "✗"
        
        print(string.format("%d. %s: %s %s (期望: %s) %s", 
              i, test.type, test.value, status, expected_status,
              message or ""))
    end
end

data_validation_example()

示例3: 模板引擎 ​

lua
-- 简单模板引擎示例
function template_engine_example()
    print("\n=== 模板引擎示例 ===")
    
    -- 简单模板引擎
    local TemplateEngine = {}
    
    function TemplateEngine:new()
        local obj = {
            variables = {}
        }
        setmetatable(obj, {__index = self})
        return obj
    end
    
    function TemplateEngine:set(key, value)
        self.variables[key] = value
    end
    
    function TemplateEngine:render(template)
        local result = template
        
        -- 替换变量 {{variable}}
        result = string.gsub(result, "{{%s*([%w_]+)%s*}}", function(var)
            return tostring(self.variables[var] or "")
        end)
        
        -- 处理条件 {{if condition}}...{{end}}
        result = string.gsub(result, "{{%s*if%s+([%w_]+)%s*}}(.-){{%s*end%s*}}", 
            function(condition, content)
                if self.variables[condition] then
                    return content
                else
                    return ""
                end
            end)
        
        -- 处理循环 {{for item in list}}...{{end}}
        result = string.gsub(result, "{{%s*for%s+([%w_]+)%s+in%s+([%w_]+)%s*}}(.-){{%s*end%s*}}", 
            function(item_var, list_var, content)
                local list = self.variables[list_var]
                if type(list) == "table" then
                    local output = {}
                    for _, item in ipairs(list) do
                        local item_content = string.gsub(content, "{{%s*" .. item_var .. "%s*}}", 
                            tostring(item))
                        table.insert(output, item_content)
                    end
                    return table.concat(output)
                end
                return ""
            end)
        
        return result
    end
    
    -- 创建模板引擎实例
    local engine = TemplateEngine:new()
    
    -- 设置变量
    engine:set("title", "用户列表")
    engine:set("show_header", true)
    engine:set("users", {"张三", "李四", "王五"})
    engine:set("total", 3)
    
    -- HTML模板
    local html_template = [[
<!DOCTYPE html>
<html>
<head>
    <title>{{title}}</title>
</head>
<body>
    {{if show_header}}
    <h1>{{title}}</h1>
    {{end}}
    
    <ul>
    {{for user in users}}
        <li>用户: {{user}}</li>
    {{end}}
    </ul>
    
    <p>总计: {{total}} 个用户</p>
</body>
</html>
    ]]
    
    -- 渲染模板
    local rendered = engine:render(html_template)
    print("渲染的HTML:")
    print(rendered)
    
    -- 邮件模板示例
    engine:set("recipient", "张三")
    engine:set("sender", "系统管理员")
    engine:set("date", os.date("%Y-%m-%d"))
    
    local email_template = [[
亲爱的 {{recipient}}:

您好!这是来自{{sender}}的通知邮件。

发送日期:{{date}}

祝好!
{{sender}}
    ]]
    
    local email = engine:render(email_template)
    print("\n渲染的邮件:")
    print(email)
end

template_engine_example()

🔧 最佳实践 ​

1. 字符串拼接优化 ​

lua
-- 避免在循环中使用字符串连接
-- 错误方式
local function bad_concat(strings)
    local result = ""
    for _, str in ipairs(strings) do
        result = result .. str  -- 每次都创建新字符串
    end
    return result
end

-- 正确方式
local function good_concat(strings)
    return table.concat(strings)  -- 一次性连接
end

2. 模式匹配缓存 ​

lua
-- 预编译常用模式
local patterns = {
    email = "^[%w%.%-_]+@[%w%.%-_]+%.%w+$",
    phone = "^1[3-9]%d%d%d%d%d%d%d%d%d$",
    number = "%d+%.?%d*"
}

local function validate_with_cache(text, pattern_name)
    local pattern = patterns[pattern_name]
    return pattern and string.match(text, pattern) ~= nil
end

3. 安全的字符串操作 ​

lua
-- 安全的字符串分割
local function safe_split(str, delimiter)
    if not str or not delimiter then return {} end
    
    local result = {}
    local pattern = "([^" .. delimiter .. "]*)" .. delimiter .. "?"
    
    for match in string.gmatch(str .. delimiter, pattern) do
        if match ~= "" then
            table.insert(result, match)
        end
    end
    
    return result
end

⚠️ 注意事项 ​

1. 字符编码 ​

  • LUA字符串是字节序列,不是字符序列
  • UTF-8中文字符占用多个字节
  • string.len()返回字节数,不是字符数

2. 模式匹配 ​

  • LUA模式不是正则表达式
  • 特殊字符需要用%转义
  • 贪婪匹配可能导致意外结果

3. 性能考虑 ​

  • 避免在循环中进行字符串连接
  • 大量替换操作考虑使用table.concat
  • 复杂模式匹配可能影响性能

4. 内存使用 ​

  • 字符串是不可变的
  • 每次修改都会创建新字符串
  • 注意大字符串的内存占用

例程 ​

字符串库(string.*)是 Lua 自带模块,处理「设备返回的文本、日志、配置文件、上报报文」都离不开它。下面例程都是可直接下载运行的完整脚本:顶层执行完即结束,不阻塞主循环。核心难点是模式匹配(Lua 的模式不是正则,但够用)。

例程 1:从传感器文本里抓出数值 ​

适用:全部机型 / 软PLC模拟器 / 上位机 | 依赖模块:string | 难度:★☆☆

场景:串口/云下发 "温度:25.6℃,湿度:65%,压力:1013.2hPa",要把三个数取出来算均值或上报。

完整代码(可直接下载运行):

lua
local s = "温度:25.6℃,湿度:65%,压力:1013.2hPa"

local temp = tonumber(string.match(s, "温度:([%d%.]+)"))
local humi = tonumber(string.match(s, "湿度:(%d+)"))
local pres = tonumber(string.match(s, "压力:([%d%.]+)"))

print("温度", temp, "湿度", humi, "压力", pres)

-- 抓所有数值(不关心含义时)
local nums = {}
for v in string.gmatch(s, "%d+%.?%d*") do
    table.insert(nums, tonumber(v))
end
print("全部数值:", table.concat(nums, ", "))

要点:

  • string.match(s, "温度:([%d%.]+)") 用小括号捕获想要的部分,只返回捕获内容。
  • %d 数字、%. 字面点、+ 一个或多个;[%d%.]+ 匹配「一串数字和点」。
  • gmatch 是迭代器,配合 for ... in 遍历所有匹配。

验证方法:

  1. 运行后打印 温度 25.6 湿度 65 压力 1013.2。
  2. 末尾「全部数值」应为 25.6, 65, 1013.2。

注意事项:

  • Lua 模式不是正则表达式:() 是捕获、% 转义特殊字符,没有 | 或 \d 这种写法。
  • 小数点 . 在模式里是任意字符,要匹配真点必须写成 %.。
  • string.match 没匹配到返回 nil,记得 tonumber(nil) 会得到 nil 而非 0。

例程 2:校验设备编号 / 手机号格式 ​

适用:同上 | 依赖模块:string | 难度:★☆☆

场景:上位机录入设备序列号或运维手机号,下发给脚本做合法性初筛,格式不对直接拒绝,避免脏数据进 PLC。

完整代码(可直接下载运行):

lua
local function is_sn(sn)
    -- 例:LP 开头 + 8 位字母数字
    return string.match(sn, "^LP%w%w%w%w%w%w%w%w$") ~= nil
end

local function is_phone(p)
    -- 1 开头 + 11 位数字
    return string.match(p, "^1%d%d%d%d%d%d%d%d%d%d$") ~= nil
end

print(is_sn("LP12345678"))       -- true
print(is_sn("lp12345678"))       -- false(大小写敏感)
print(is_phone("13812345678"))   -- true

要点:

  • ^ 行首、$ 行尾,保证整个字符串都匹配,而不是部分包含。
  • %w 字母或数字;%d 数字。
  • 匹配成功返回捕获(无捕获则返回整个串),失败返回 nil。

验证方法:

  1. LP12345678 → true;lp12345678 → false(大小写敏感,如需忽略大小写先用 string.lower)。
  2. 13812345678 → true;12345 → false。

注意事项:

  • 模式默认区分大小写;要忽略大小写先 string.lower(s) 再比。
  • string.find 和 string.match 都会按模式解析;若只想做纯文本查找,记得传 plain=true 第 4 参,否则 . * 等会被当元字符。

例程 3:生成上报报文 + 日志脱敏 ​

适用:同上 | 依赖模块:string json(可选)| 难度:★★☆

场景:把采集值拼成固定格式的日志/上报串;同时运维日志里手机号、密码要打码,不能直接明文落盘。

完整代码(可直接下载运行):

lua
-- 1) 拼上报串
local msg = string.format("[%s] 温度=%.1f 湿度=%d%%",
                          os.date("%H:%M:%S"), 25.6, 65)
print(msg)   -- [14:03:05] 温度=25.6 湿度=65%

-- 2) 日志脱敏:手机号中间 4 位打星
local function mask_phone(p)
    return string.gsub(p, "^(%d%d%d)%d%d%d%d(%d%d%d%d)$", "%1****%2")
end
print(mask_phone("13812345678"))   -- 138****5678

-- 3) 用表格批量拼多行(比 .. 拼接高效)
local lines = {}
for i = 1, 3 do
    lines[i] = string.format("ch%d=%.2f", i, i * 1.5)
end
print(table.concat(lines, "\n"))

要点:

  • string.format 的 %s %d %.1f %%% 占位符,拼日志/报文最稳,比一堆 .. 清晰。
  • gsub 用捕获组重排:%1/%2 引用第 1/2 个捕获,实现打码。
  • 多行拼接用 table.concat(lines, "\n"),不要在循环里反复 ..(字符串不可变,反复拼接会频繁分配内存)。

验证方法:

  1. 运行打印一行带时间戳的 温度=25.6 湿度=65%。
  2. mask_phone 输出 138****5678。
  3. 末尾三行 ch1=1.50 / ch2=3.00 / ch3=4.50。

注意事项:

  • string.format 里要输出一个百分号得写 %%(如 65%%)。
  • gsub 的替换串里 %1 是捕获引用,%% 才是字面 %;在替换函数里返回值则直接用普通字符串。
  • 中文在 UTF-8 下占多字节,string.sub/string.len 按字节算,截中文要用 utf8 库。

最后更新: 2026-04-26版本: 1.0

函数速查表 ​

下表列出本库全部函数与方法(含尚未单独成节的),点「说明」可跳转到对应小节。签名按通用记法:括号内按顺序列全参数,方括号里的可以省略(不传走默认值),... 表示还可继续传同类参数;h: 开头的是对象方法,先用库级函数拿回句柄再冒号调用。

方法与签名作用说明
string.byte(…)取字符串指定位置的字节值本文开头
string.char(...)按字节值拼出字符串本文开头
string.dump(…)把一个函数导出为二进制跳转
string.find(…)查找子串或模式,返回起止位置本文开头
string.format(...)按格式拼装字符串(同 C 的 printf)本文开头
string.fromhex(…)将hex转换成数字显示的字符本文开头
string.gmatch(…)返回逐个匹配模式的迭代器本文开头
string.gsub(…)全局替换,返回新串和替换次数本文开头
string.len(…)取字符串的字节长度本文开头
string.lower(…)转小写本文开头
string.match(…)按模式匹配一次并取出捕获组本文开头
string.pack(string)按格式把数值打包成二进制字符串跳转
string.packsize(string)算某个打包格式占多少字节跳转
string.rep(integer)把字符串重复 n 次本文开头
string.reverse(…)反转字符串本文开头
string.sub(integer)取子串(下标支持负数,-1 表示最后一个字符)本文开头
string.tohex(…)תhex本文开头
string.unpack(string)按格式从二进制字符串取出数值跳转
string.upper(…)转大写本文开头

string.dump ​

  • 作用:把一个函数导出为二进制
  • 调用形态:string.dump(…)(库级函数)
  • 参数:无参数
  • 返回:1 个值
  • 可用机型:ESP32 机型 · 软 PLC 模拟器 / 上位机

string.pack ​

  • 作用:按格式把数值打包成二进制字符串
  • 调用形态:string.pack(string)(库级函数)
  • 参数:1 个参数:第 1 个为字符串(必填)
  • 返回:1 个值
  • 可用机型:ESP32 机型 · 软 PLC 模拟器 / 上位机

string.packsize ​

  • 作用:算某个打包格式占多少字节
  • 调用形态:string.packsize(string)(库级函数)
  • 参数:1 个参数:第 1 个为字符串(必填)
  • 返回:1 个值
  • 可用机型:ESP32 机型 · 软 PLC 模拟器 / 上位机

string.unpack ​

  • 作用:按格式从二进制字符串取出数值
  • 调用形态:string.unpack(string)(库级函数)
  • 参数:1 个参数:第 1 个为字符串(必填)
  • 可用机型:ESP32 机型 · 软 PLC 模拟器 / 上位机

LPPLC 工业物联网云平台 · 软硬件与云平台全栈自研