找回密码
 registerss
搜索
查看: 83|回复: 1

[*LUA5*] Lua 中 `#` 运算符详解

[复制链接]
  • 打卡等级:本地老炮
  • 打卡总天数:597
  • 打卡月天数:12
  • 打卡总奖励:594
  • 最近打卡:2026-09-15 17:12:42
Waylee 发表于 2026-3-9 19:29 | 显示全部楼层 |阅读模式 | Google Chrome | Windows 10

马上注册,查看网站隐藏内容!!

您需要 登录 才可以下载或查看,没有账号?registerss

×

# 是 Lua 中的长度运算符,但它的行为有些特殊,需要仔细理解。


一、基础用法

1. 获取字符串长度

local str = "Hello"
print(#str)        -- 5

local chinese = "你好"
print(#chinese)    -- 6(UTF-8 编码,每个中文字符占 3 字节)

-- 空字符串
print(#"")         -- 0

2. 获取数组(序列)长度

local arr = {10, 20, 30, 40, 50}
print(#arr)        -- 5

-- 空表
print(#{})         -- 0

二、数组长度的关键概念

1. 什么是"序列"(Sequence)

Lua 中,# 只对序列有效。序列的定义是:

  • 索引从 1 开始
  • 连续的正整数索引(1, 2, 3, ... n)
  • 中间没有空洞(nil)
-- 有效的序列
local seq1 = {10, 20, 30}           -- #seq1 = 3
local seq2 = {"a", "b", nil, "d"}   -- 不是序列,因为中间有 nil

-- 无效的情况(有"空洞")
local bad = {}
bad[1] = "a"
bad[2] = "b"
bad[4] = "d"   -- 跳过了 3
print(#bad)    -- 结果不确定!可能是 2 或 4

2. 长度运算符的工作原理

local t = {10, 20, 30, nil, 50}

-- 找最后一个非 nil 的索引
-- 但遇到 nil 会停止,所以结果不确定
print(#t)      -- 可能是 3(在 30 处停止)

-- 更明确的例子
local t2 = {10, 20, nil}
print(#t2)     -- 2(在 nil 前停止)

三、实际应用示例

1. 遍历数组

local fruits = {"apple", "banana", "cherry"}

-- 方法1:使用 # 运算符
for i = 1, #fruits do
    print(i, fruits[i])
end

-- 方法2:使用 ipairs(推荐,更安全)
for i, fruit in ipairs(fruits) do
    print(i, fruit)
end

2. 向数组末尾添加元素

local stack = {10, 20, 30}

-- 在末尾添加
table.insert(stack, 40)           -- 使用内置函数
-- 或者
stack[#stack + 1] = 40            -- 手动添加

print(#stack)                     -- 5

3. 获取最后一个元素

local queue = {"first", "second", "third"}
local last = queue[#queue]        -- "third"

-- 删除最后一个元素
queue[#queue] = nil
print(#queue)                     -- 2

四、常见陷阱和解决方案

陷阱 1:包含 nil 的数组

local data = {10, 20, nil, 40, 50}
print(#data)      -- 结果不确定!可能是 2 或 5

-- 解决方案:不要用 nil 表示空值,用特殊值
local data2 = {10, 20, false, 40, 50}  -- 用 false 代替 nil
print(#data2)     -- 5(确定)

陷阱 2:非连续索引

local sparse = {}
sparse[1] = "a"
sparse[100] = "b"
print(#sparse)    -- 1(不是 100!)

-- 解决方案:如果需要稀疏数组,自己记录长度
local sparse2 = {
    data = {[1] = "a", [100] = "b"},
    length = 2    -- 手动维护长度
}

陷阱 3:字符串中的特殊字符

local str = "Hello\nWorld"   -- 包含换行符
print(#str)                  -- 11(\n 算 1 个字符)

local str2 = "Hello\0World"  -- 包含空字符
print(#str2)                 -- 11(\0 也算 1 个字符)

五、高级用法

1. 实现栈(Stack)

local Stack = {}
Stack.__index = Stack

function Stack:new()
    return setmetatable({items = {}}, self)
end

function Stack:push(item)
    self.items[#self.items + 1] = item
end

function Stack:pop()
    local len = #self.items
    if len == 0 then return nil end
    local item = self.items[len]
    self.items[len] = nil
    return item
end

function Stack:peek()
    return self.items[#self.items]
end

function Stack:isEmpty()
    return #self.items == 0
end

function Stack:size()
    return #self.items
end

-- 使用
local s = Stack:new()
s:push(10)
s:push(20)
s:push(30)
print(s:pop())     -- 30
print(s:size())    -- 2

2. 实现队列(Queue)

local Queue = {}
Queue.__index = Queue

function Queue:new()
    return setmetatable({
        items = {},
        head = 1,
        tail = 0
    }, self)
end

function Queue:enqueue(item)
    self.tail = self.tail + 1
    self.items[self.tail] = item
end

function Queue:dequeue()
    if self.head > self.tail then return nil end
    local item = self.items[self.head]
    self.items[self.head] = nil  -- 允许垃圾回收
    self.head = self.head + 1
    return item
end

function Queue:size()
    return self.tail - self.head + 1
end

-- 使用
local q = Queue:new()
q:enqueue("A")
q:enqueue("B")
q:enqueue("C")
print(q:dequeue())  -- A
print(q:size())     -- 2

3. 安全的获取表长度函数

-- 自定义安全的长度函数
function safeLength(t)
    if type(t) ~= "table" then
        return 0
    end

    local len = 0
    for i = 1, math.huge do
        if t[i] == nil then
            break
        end
        len = i
    end
    return len
end

-- 测试
local t1 = {10, 20, 30}
local t2 = {10, nil, 30}  -- 有 nil

print(safeLength(t1))  -- 3
print(safeLength(t2))  -- 1(在第一个 nil 处停止)

六、与其他函数对比

方式 用途 特点
#t 获取序列长度 快速,但对非序列结果不确定
ipairs(t) 遍历序列 遇到 nil 停止,安全
pairs(t) 遍历所有键值 遍历所有键,不保证顺序
table.insert(t, v) 插入元素 自动处理长度
table.remove(t) 删除元素 自动处理长度

七、总结要点

  1. # 对字符串:返回字节数(不是字符数)
  2. # 对表:只对序列有效(连续的正整数索引,无 nil)
  3. 避免陷阱:不要在数组中间放 nil
  4. 替代方案:需要稀疏数组时,自己维护长度或使用其他数据结构

  • 打卡等级:即来则安
  • 打卡总天数:24
  • 打卡月天数:0
  • 打卡总奖励:23
  • 最近打卡:2026-08-02 01:35:50
qingge 发表于 2026-6-10 20:34 | 显示全部楼层 | Edg | Windows 10
:victory::victory::victory::victory::victory::victory::victory::victory::victory::victory::victory::victory::victory::victory::victory::victory::victory::victory::victory::victory::victory:
您需要登录后才可以回帖 登录 | registerss

本版积分规则

QQ|手机版|雪舞知识库 ( 浙ICP备15015590号-1 | 萌ICP备20232229号|浙公网安备33048102000118号 )|天天打卡

GMT+8, 2026-9-16 01:42 , Processed in 0.069182 second(s), 26 queries .

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表