<返回目录     Powered by claud/xia兄

第7课: 字符串处理

字符串基础操作

字符串内部实现原理:

Lua中的字符串是不可变的,这意味着一旦创建就不能修改。字符串在Lua中是值类型,但内部使用引用计数和字符串驻留(string interning)来优化内存使用。相同的字符串字面量会共享同一个内存对象,这提高了比较效率和内存利用率。字符串长度信息存储在字符串对象中,因此获取长度是O(1)操作。

-- 字符串长度
local str = "Hello Lua"
print(#str)              -- 9
print(string.len(str))   -- 9

-- 字符串连接
local s1 = "Hello"
local s2 = "World"
print(s1 .. " " .. s2)   -- Hello World

-- 字符串重复
print(string.rep("Lua", 3))  -- LuaLuaLua
print(string.rep("-", 20))   -- --------------------

-- 字符串比较
local str1 = "apple"
local str2 = "banana"
print(str1 < str2)   -- true (按字典序比较)
print(str1 == str1)  -- true

-- 多行字符串
local multiline = [[
第一行
第二行
第三行
]]
print(multiline)

-- 字符串转义序列
local escaped = "Hello\nWorld\t\"Lua\""
print(escaped)

-- UTF-8字符处理(Lua 5.3+)
local utf8str = "你好世界"
print(#utf8str)  -- 12 (字节数)

-- 使用utf8库处理Unicode字符
if utf8 then
    print(utf8.len(utf8str))  -- 4 (字符数)
    for i, c in utf8.codes(utf8str) do
        print(i, utf8.char(c))
    end
end

大小写转换

local str = "Hello Lua"

-- 转大写
print(string.upper(str))  -- HELLO LUA

-- 转小写
print(string.lower(str))  -- hello lua

-- 首字母大写
function capitalize(s)
    return string.upper(string.sub(s, 1, 1)) .. string.sub(s, 2)
end

print(capitalize("lua"))  -- Lua

字符串查找

local text = "Hello Lua, Lua is great!"

-- string.find(str, pattern, [init, [plain]])
local start, finish = string.find(text, "Lua")
print(start, finish)  -- 7  9

-- 从指定位置开始查找
local start2, finish2 = string.find(text, "Lua", 10)
print(start2, finish2)  -- 12  14

-- 纯文本查找(不使用模式)
local s, f = string.find(text, ".", 1, true)
print(s, f)  -- 查找点号

-- 检查字符串是否包含子串
if string.find(text, "great") then
    print("找到了great")
end

字符串截取

local str = "Hello Lua"

-- string.sub(str, start, [end])
print(string.sub(str, 1, 5))   -- Hello
print(string.sub(str, 7))      -- Lua
print(string.sub(str, -3))     -- Lua (负数从末尾开始)
print(string.sub(str, 1, -5))  -- Hello

-- 获取单个字符
print(string.sub(str, 1, 1))   -- H
print(string.sub(str, -1))     -- a

字符串替换

local text = "I love Lua, Lua is great"

-- string.gsub(str, pattern, repl, [n])
local result, count = string.gsub(text, "Lua", "Python")
print(result)  -- I love Python, Python is great
print(count)   -- 2 (替换次数)

-- 限制替换次数
local result2 = string.gsub(text, "Lua", "Python", 1)
print(result2)  -- I love Python, Lua is great

-- 使用函数替换
local str = "hello world"
local result3 = string.gsub(str, "%w+", string.upper)
print(result3)  -- HELLO WORLD

字符串分割

-- 分割字符串
function split(str, delimiter)
    local result = {}
    local pattern = string.format("([^%s]+)", delimiter)
    for word in string.gmatch(str, pattern) do
        table.insert(result, word)
    end
    return result
end

local text = "apple,banana,orange"
local fruits = split(text, ",")
for i, fruit in ipairs(fruits) do
    print(i, fruit)
end
-- 输出:
-- 1  apple
-- 2  banana
-- 3  orange

模式匹配

模式匹配原理:

Lua的模式匹配系统虽然不如正则表达式强大,但更轻量且高效。模式使用%作为转义字符,而不是\。模式匹配是基于有限状态机的实现,对于大多数常见任务已经足够。Lua 5.3+还引入了utf8模式,可以正确处理Unicode字符。

-- 模式匹配字符类
-- %a  字母
-- %d  数字
-- %s  空白字符
-- %w  字母和数字
-- %p  标点符号
-- %c  控制字符
-- %x  十六进制数字
-- %z  表示0的字符

local str = "Phone: 123-456-7890"

-- 提取数字
for num in string.gmatch(str, "%d+") do
    print(num)
end
-- 输出: 123  456  7890

-- 提取单词
local text = "Hello Lua World"
for word in string.gmatch(text, "%a+") do
    print(word)
end
-- 输出: Hello  Lua  World

-- 匹配邮箱
local email = "user@example.com"
if string.match(email, "%w+@%w+%.%w+") then
    print("有效的邮箱")
end

-- 模式修饰符
-- +   匹配前一字符1次或多次
-- *   匹配前一字符0次或多次
-- -   匹配前一字符0次或多次(最短匹配)
-- ?   匹配前一字符0次或1次

-- 最短匹配示例
local html = "
Content
More
" local short = string.match(html, "
.-
") print(short) --
Content
local long = string.match(html, "
.*
") print(long) --
Content
More
-- 捕获组 local date = "2024-01-15" local year, month, day = string.match(date, "(%d+)-(%d+)-(%d+)") print(year, month, day) -- 2024 01 15 -- 使用string.match提取信息 local url = "https://www.example.com/path/to/page" local protocol, domain, path = string.match(url, "^(%w+)://([^/]+)(.*)$") print(protocol) -- https print(domain) -- www.example.com print(path) -- /path/to/page -- 匹配IP地址 local ip = "192.168.1.1" if string.match(ip, "^%d+%.%d+%.%d+%.%d+$") then print("有效的IP地址格式") end -- 匹配十六进制颜色 local color = "#FF5733" if string.match(color, "^#%x%x%x%x%x%x$") then print("有效的十六进制颜色") end -- 匹配日期(YYYY-MM-DD) local dateStr = "2024-01-15" if string.match(dateStr, "^%d%d%d%d-%d%d-%d%d$") then print("有效的日期格式") end

字符串格式化

-- string.format(formatstring, ...)
local name = "张三"
local age = 25
local score = 95.5

-- 基本格式化
print(string.format("姓名: %s, 年龄: %d", name, age))

-- 数字格式化
print(string.format("%.2f", score))      -- 95.50 (保留2位小数)
print(string.format("%d", 123))          -- 123
print(string.format("%5d", 123))         -- "  123" (宽度5)
print(string.format("%05d", 123))        -- "00123" (补零)

-- 十六进制
print(string.format("%x", 255))          -- ff
print(string.format("%X", 255))          -- FF

-- 科学计数法
print(string.format("%e", 1000))         -- 1.000000e+03

字符与ASCII码转换

-- string.byte(str, [i, [j]])
local str = "ABC"
print(string.byte(str, 1))     -- 65 (A的ASCII码)
print(string.byte(str, 2))     -- 66 (B的ASCII码)

local a, b, c = string.byte(str, 1, 3)
print(a, b, c)  -- 65  66  67

-- string.char(...)
print(string.char(65, 66, 67))  -- ABC
print(string.char(72, 101, 108, 108, 111))  -- Hello

字符串反转与去空格

-- 字符串反转
function reverse(str)
    return string.reverse(str)
end

print(reverse("Hello"))  -- olleH

-- 去除首尾空格
function trim(str)
    return string.gsub(str, "^%s*(.-)%s*$", "%1")
end

print(trim("  Hello Lua  "))  -- "Hello Lua"

-- 去除所有空格
function removeSpaces(str)
    return string.gsub(str, "%s+", "")
end

print(removeSpaces("H e l l o"))  -- "Hello"

-- 去除左侧空格
function ltrim(str)
    return string.gsub(str, "^%s+", "")
end

-- 去除右侧空格
function rtrim(str)
    return string.gsub(str, "%s+$", "")
end

-- 字符串填充
function padLeft(str, len, char)
    char = char or " "
    return string.rep(char, len - #str) .. str
end

function padRight(str, len, char)
    char = char or " "
    return str .. string.rep(char, len - #str)
end

print(padLeft("42", 5, "0"))   -- 00042
print(padRight("Lua", 10, "-")) -- Lua-------

-- 字符串居中
function center(str, len, char)
    char = char or " "
    local padLen = len - #str
    local leftPad = math.floor(padLen / 2)
    local rightPad = padLen - leftPad
    return string.rep(char, leftPad) .. str .. string.rep(char, rightPad)
end

print(center("Lua", 10, "*"))  -- ***Lua****

高级字符串处理

-- 判断字符串是否为回文
function isPalindrome(str)
    -- 移除非字母字符并转为小写
    local clean = string.gsub(string.lower(str), "%W", "")
    return clean == string.reverse(clean)
end

print(isPalindrome("A man, a plan, a canal: Panama"))  -- true
print(isPalindrome("Hello"))  -- false

-- 统计字符频率
function charFrequency(str)
    local freq = {}
    for char in string.gmatch(str, ".") do
        freq[char] = (freq[char] or 0) + 1
    end
    return freq
end

local freq = charFrequency("Hello World")
for char, count in pairs(freq) do
    print(string.format("'%s': %d", char, count))
end

-- 驼峰命名转下划线命名
function camelToSnake(str)
    return string.gsub(str, "%u", function(c)
        return "_" .. string.lower(c)
    end)
end

print(camelToSnake("camelCaseString"))  -- camel_case_string

-- 下划线命名转驼峰命名
function snakeToCamel(str)
    return string.gsub(str, "_%w", function(c)
        return string.upper(string.sub(c, 2))
    end)
end

print(snakeToCamel("snake_case_string"))  -- snakeCaseString

-- 字符串压缩(简单的RLE压缩)
function compress(str)
    local result = {}
    local count = 1
    local prev = string.sub(str, 1, 1)
    
    for i = 2, #str do
        local char = string.sub(str, i, i)
        if char == prev and count < 9 then
            count = count + 1
        else
            table.insert(result, count .. prev)
            prev = char
            count = 1
        end
    end
    table.insert(result, count .. prev)
    
    return table.concat(result)
end

function decompress(str)
    local result = {}
    for count, char in string.gmatch(str, "(%d)(.)") do
        table.insert(result, string.rep(char, tonumber(count)))
    end
    return table.concat(result)
end

local original = "AAABBBCCDDDD"
local compressed = compress(original)
local decompressed = decompress(compressed)
print("Original:", original)
print("Compressed:", compressed)
print("Decompressed:", decompressed)

-- 字符串相似度(Levenshtein距离)
function levenshtein(str1, str2)
    local len1, len2 = #str1, #str2
    local matrix = {}
    
    for i = 0, len1 do
        matrix[i] = {[0] = i}
    end
    for j = 0, len2 do
        matrix[0][j] = j
    end
    
    for i = 1, len1 do
        for j = 1, len2 do
            local cost = string.sub(str1, i, i) == string.sub(str2, j, j) and 0 or 1
            matrix[i][j] = math.min(
                matrix[i-1][j] + 1,
                matrix[i][j-1] + 1,
                matrix[i-1][j-1] + cost
            )
        end
    end
    
    return matrix[len1][len2]
end

print(levenshtein("kitten", "sitting"))  -- 3
print(levenshtein("Lua", "Lua"))  -- 0

-- 字符串模板引擎
function template(tmpl, vars)
    return string.gsub(tmpl, "%{(%w+)%}", function(key)
        return vars[key] or ""
    end)
end

local html = template("

Hello, {name}!

Age: {age}

", { name = "张三", age = 25 }) print(html)
重要提示:
练习题:
  1. 编写函数判断字符串是否为回文
  2. 实现函数统计字符串中每个字符出现的次数
  3. 编写函数验证手机号格式(11位数字)
  4. 实现函数将驼峰命名转换为下划线命名