主题
📝 字符串库 - 简单易懂的文字处理指南
📖 什么是字符串?
字符串就是文字内容,比如设备名称、错误信息、用户输入等。字符串库提供了处理这些文字的工具,就像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, 可选): 开始位置,默认为1plain(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, 可选): 起始位置,默认为1j(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) -- 一次性连接
end2. 模式匹配缓存
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
end3. 安全的字符串操作
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遍历所有匹配。
验证方法:
- 运行后打印
温度 25.6 湿度 65 压力 1013.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。
验证方法:
LP12345678→true;lp12345678→false(大小写敏感,如需忽略大小写先用string.lower)。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"),不要在循环里反复..(字符串不可变,反复拼接会频繁分配内存)。
验证方法:
- 运行打印一行带时间戳的
温度=25.6 湿度=65%。 mask_phone输出138****5678。- 末尾三行
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 模拟器 / 上位机