|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
引言
在Lua编程中,格式化输出是一项基本但极其重要的技能。无论是日志记录、数据展示还是用户界面生成,良好的格式化输出都能使信息更加清晰易读。Lua提供了强大的string.format函数,它允许开发者按照特定格式输出数据,类似于C语言中的printf函数。本文将深入探讨string.format函数的各个方面,从基础用法到高级技巧,帮助开发者全面掌握Lua中的格式化输出技术,解决实际编程中的数据格式化挑战,优化输出效果。
基础部分:string.format函数的基本语法和用法
string.format函数是Lua标准库中用于格式化字符串的核心函数。它的基本语法如下:
- formatted_string = string.format(formatstring, ...)
复制代码
其中,formatstring是一个包含普通文本和格式说明符的字符串,而…是要格式化的参数列表。
最简单的例子:
- local name = "Alice"
- local age = 25
- local message = string.format("Name: %s, Age: %d", name, age)
- print(message) -- 输出: Name: Alice, Age: 25
复制代码
在这个例子中,%s和%d是格式说明符,分别表示字符串和十进制整数。string.format会用后面的参数替换这些说明符,生成最终的格式化字符串。
格式化选项详解
Lua中的string.format支持多种格式说明符,每种都有特定的用途。下面详细介绍各种格式说明符:
基本格式说明符
1. - %s - 字符串local str = "Hello"
- print(string.format("String: %s", str)) -- 输出: String: Hello
复制代码 2. - %d - 十进制整数local num = 42
- print(string.format("Number: %d", num)) -- 输出: Number: 42
复制代码 3. - %i - 同%d,十进制整数local num = 42
- print(string.format("Number: %i", num)) -- 输出: Number: 42
复制代码 4. - %f - 浮点数local pi = 3.14159
- print(string.format("Pi: %f", pi)) -- 输出: Pi: 3.141590
复制代码 5. - %c - 字符(将数字转换为对应的ASCII字符)local ascii_code = 65
- print(string.format("Character: %c", ascii_code)) -- 输出: Character: A
复制代码 6. - %o - 八进制数local num = 16
- print(string.format("Octal: %o", num)) -- 输出: Octal: 20
复制代码 7. - %u - 无符号十进制整数local num = 42
- print(string.format("Unsigned: %u", num)) -- 输出: Unsigned: 42
复制代码 8. - %x - 小写十六进制数local num = 255
- print(string.format("Hex: %x", num)) -- 输出: Hex: ff
复制代码 9. - %X - 大写十六进制数local num = 255
- print(string.format("Hex: %X", num)) -- 输出: Hex: FF
复制代码 10. - %e - 科学计数法(小写e)local num = 12345.67
- print(string.format("Scientific: %e", num)) -- 输出: Scientific: 1.234567e+04
复制代码 11. - %E - 科学计数法(大写E)local num = 12345.67
- print(string.format("Scientific: %E", num)) -- 输出: Scientific: 1.234567E+04
复制代码 12. - %g - 根据值的大小自动选择%f或%e格式(紧凑形式)local num1 = 123.456
- local num2 = 123456789.123
- print(string.format("Compact1: %g", num1)) -- 输出: Compact1: 123.456
- print(string.format("Compact2: %g", num2)) -- 输出: Compact2: 1.23457e+08
复制代码 13. - %G - 同%g,但使用大写字母local num = 123456789.123
- print(string.format("Compact: %G", num)) -- 输出: Compact: 1.23457E+08
复制代码 14. - %% - 百分号本身local percentage = 50
- print(string.format("Progress: %d%%", percentage)) -- 输出: Progress: 50%
复制代码
%s - 字符串
- local str = "Hello"
- print(string.format("String: %s", str)) -- 输出: String: Hello
复制代码
%d - 十进制整数
- local num = 42
- print(string.format("Number: %d", num)) -- 输出: Number: 42
复制代码
%i - 同%d,十进制整数
- local num = 42
- print(string.format("Number: %i", num)) -- 输出: Number: 42
复制代码
%f - 浮点数
- local pi = 3.14159
- print(string.format("Pi: %f", pi)) -- 输出: Pi: 3.141590
复制代码
%c - 字符(将数字转换为对应的ASCII字符)
- local ascii_code = 65
- print(string.format("Character: %c", ascii_code)) -- 输出: Character: A
复制代码
%o - 八进制数
- local num = 16
- print(string.format("Octal: %o", num)) -- 输出: Octal: 20
复制代码
%u - 无符号十进制整数
- local num = 42
- print(string.format("Unsigned: %u", num)) -- 输出: Unsigned: 42
复制代码
%x - 小写十六进制数
- local num = 255
- print(string.format("Hex: %x", num)) -- 输出: Hex: ff
复制代码
%X - 大写十六进制数
- local num = 255
- print(string.format("Hex: %X", num)) -- 输出: Hex: FF
复制代码
%e - 科学计数法(小写e)
- local num = 12345.67
- print(string.format("Scientific: %e", num)) -- 输出: Scientific: 1.234567e+04
复制代码
%E - 科学计数法(大写E)
- local num = 12345.67
- print(string.format("Scientific: %E", num)) -- 输出: Scientific: 1.234567E+04
复制代码
%g - 根据值的大小自动选择%f或%e格式(紧凑形式)
- local num1 = 123.456
- local num2 = 123456789.123
- print(string.format("Compact1: %g", num1)) -- 输出: Compact1: 123.456
- print(string.format("Compact2: %g", num2)) -- 输出: Compact2: 1.23457e+08
复制代码
%G - 同%g,但使用大写字母
- local num = 123456789.123
- print(string.format("Compact: %G", num)) -- 输出: Compact: 1.23457E+08
复制代码
%% - 百分号本身
- local percentage = 50
- print(string.format("Progress: %d%%", percentage)) -- 输出: Progress: 50%
复制代码
格式修饰符
除了基本的格式说明符外,Lua还支持多种修饰符来控制输出的格式:
1. - 宽度修饰符local num = 42
- print(string.format("Number: %5d", num)) -- 输出: Number: 42 (总宽度为5,右对齐)
- print(string.format("Number: %-5d", num)) -- 输出: Number: 42 (总宽度为5,左对齐)
复制代码 2. - 精度修饰符local pi = 3.14159265359
- print(string.format("Pi: %.2f", pi)) -- 输出: Pi: 3.14 (保留2位小数)
- print(string.format("Pi: %.5f", pi)) -- 输出: Pi: 3.14159 (保留5位小数)
复制代码 3. - 标志修饰符+:显示数字的正负号local num1 = 42
- local num2 = -42
- print(string.format("Num1: %+d", num1)) -- 输出: Num1: +42
- print(string.format("Num2: %+d", num2)) -- 输出: Num2: -420:用0填充而不是空格local num = 42
- print(string.format("Num: %05d", num)) -- 输出: Num: 00042#:对于%o,添加前缀0;对于%x/%X,添加前缀0x/0Xlocal num = 255
- print(string.format("Octal: %#o", num)) -- 输出: Octal: 0377
- print(string.format("Hex: %#x", num)) -- 输出: Hex: 0xff
- print(string.format("Hex: %#X", num)) -- 输出: Hex: 0XFF(空格):对于正数,在前面添加一个空格local num1 = 42
- local num2 = -42
- print(string.format("Num1: % d", num1)) -- 输出: Num1: 42 (注意前面的空格)
- print(string.format("Num2: % d", num2)) -- 输出: Num2: -42
复制代码 4. - +:显示数字的正负号local num1 = 42
- local num2 = -42
- print(string.format("Num1: %+d", num1)) -- 输出: Num1: +42
- print(string.format("Num2: %+d", num2)) -- 输出: Num2: -42
复制代码 5. - 0:用0填充而不是空格local num = 42
- print(string.format("Num: %05d", num)) -- 输出: Num: 00042
复制代码 6. - #:对于%o,添加前缀0;对于%x/%X,添加前缀0x/0Xlocal num = 255
- print(string.format("Octal: %#o", num)) -- 输出: Octal: 0377
- print(string.format("Hex: %#x", num)) -- 输出: Hex: 0xff
- print(string.format("Hex: %#X", num)) -- 输出: Hex: 0XFF
复制代码 7. - (空格):对于正数,在前面添加一个空格local num1 = 42
- local num2 = -42
- print(string.format("Num1: % d", num1)) -- 输出: Num1: 42 (注意前面的空格)
- print(string.format("Num2: % d", num2)) -- 输出: Num2: -42
复制代码
宽度修饰符
- local num = 42
- print(string.format("Number: %5d", num)) -- 输出: Number: 42 (总宽度为5,右对齐)
- print(string.format("Number: %-5d", num)) -- 输出: Number: 42 (总宽度为5,左对齐)
复制代码
精度修饰符
- local pi = 3.14159265359
- print(string.format("Pi: %.2f", pi)) -- 输出: Pi: 3.14 (保留2位小数)
- print(string.format("Pi: %.5f", pi)) -- 输出: Pi: 3.14159 (保留5位小数)
复制代码
标志修饰符
• - +:显示数字的正负号local num1 = 42
- local num2 = -42
- print(string.format("Num1: %+d", num1)) -- 输出: Num1: +42
- print(string.format("Num2: %+d", num2)) -- 输出: Num2: -42
复制代码 • - 0:用0填充而不是空格local num = 42
- print(string.format("Num: %05d", num)) -- 输出: Num: 00042
复制代码 • - #:对于%o,添加前缀0;对于%x/%X,添加前缀0x/0Xlocal num = 255
- print(string.format("Octal: %#o", num)) -- 输出: Octal: 0377
- print(string.format("Hex: %#x", num)) -- 输出: Hex: 0xff
- print(string.format("Hex: %#X", num)) -- 输出: Hex: 0XFF
复制代码 • - (空格):对于正数,在前面添加一个空格local num1 = 42
- local num2 = -42
- print(string.format("Num1: % d", num1)) -- 输出: Num1: 42 (注意前面的空格)
- print(string.format("Num2: % d", num2)) -- 输出: Num2: -42
复制代码
+:显示数字的正负号
- local num1 = 42
- local num2 = -42
- print(string.format("Num1: %+d", num1)) -- 输出: Num1: +42
- print(string.format("Num2: %+d", num2)) -- 输出: Num2: -42
复制代码
0:用0填充而不是空格
- local num = 42
- print(string.format("Num: %05d", num)) -- 输出: Num: 00042
复制代码
#:对于%o,添加前缀0;对于%x/%X,添加前缀0x/0X
- local num = 255
- print(string.format("Octal: %#o", num)) -- 输出: Octal: 0377
- print(string.format("Hex: %#x", num)) -- 输出: Hex: 0xff
- print(string.format("Hex: %#X", num)) -- 输出: Hex: 0XFF
复制代码
(空格):对于正数,在前面添加一个空格
- local num1 = 42
- local num2 = -42
- print(string.format("Num1: % d", num1)) -- 输出: Num1: 42 (注意前面的空格)
- print(string.format("Num2: % d", num2)) -- 输出: Num2: -42
复制代码
组合使用修饰符
可以组合使用多个修饰符来达到更复杂的格式化效果:
- local pi = 3.14159
- local num = 42
- -- 宽度、精度和对齐的组合
- print(string.format("Pi: %10.2f", pi)) -- 输出: Pi: 3.14 (总宽度10,保留2位小数,右对齐)
- print(string.format("Pi: %-10.2f", pi)) -- 输出: Pi: 3.14 (总宽度10,保留2位小数,左对齐)
- -- 标志、宽度和精度的组合
- print(string.format("Num: %+05d", num)) -- 输出: Num: +0042 (显示符号,用0填充,总宽度5)
- print(string.format("Num: %#06X", num)) -- 输出: Num: 0X002A (添加前缀,用0填充,总宽度6)
复制代码
实际应用案例
1. 表格对齐输出
在需要输出对齐的表格数据时,string.format非常有用:
- local users = {
- {name = "Alice", age = 25, score = 95.5},
- {name = "Bob", age = 30, score = 87.2},
- {name = "Charlie", age = 35, score = 92.8}
- }
- -- 打印表头
- print(string.format("%-10s | %3s | %5s", "Name", "Age", "Score"))
- print(string.format("%-10s | %3s | %5s", "----------", "---", "-----"))
- -- 打印数据行
- for _, user in ipairs(users) do
- print(string.format("%-10s | %3d | %5.1f", user.name, user.age, user.score))
- end
复制代码
输出结果:
- Name | Age | Score
- ---------- | --- | -----
- Alice | 25 | 95.5
- Bob | 30 | 87.2
- Charlie | 35 | 92.8
复制代码
2. 货币格式化
格式化货币金额,添加千位分隔符和固定小数位数:
- function formatCurrency(amount)
- -- 分离整数部分和小数部分
- local integer_part = math.floor(amount)
- local decimal_part = math.floor((amount - integer_part) * 100 + 0.5)
-
- -- 添加千位分隔符
- local formatted_integer = tostring(integer_part)
- local formatted_with_commas = ""
- local count = 0
-
- -- 从右到左遍历字符串,每3位添加一个逗号
- for i = #formatted_integer, 1, -1 do
- formatted_with_commas = formatted_integer:sub(i, i) .. formatted_with_commas
- count = count + 1
- if count % 3 == 0 and i > 1 then
- formatted_with_commas = "," .. formatted_with_commas
- end
- end
-
- -- 组合整数部分和小数部分
- return string.format("$%s.%02d", formatted_with_commas, decimal_part)
- end
- local price1 = 1234567.8912
- local price2 = 42.5
- local price3 = 1000
- print(formatCurrency(price1)) -- 输出: $1,234,567.89
- print(formatCurrency(price2)) -- 输出: $42.50
- print(formatCurrency(price3)) -- 输出: $1,000.00
复制代码
3. 时间和日期格式化
虽然Lua没有内置的日期格式化函数,但可以使用os.date和string.format结合来创建自定义的日期时间格式:
- -- 获取当前时间
- local current_time = os.time()
- -- 使用os.date获取时间组件
- local year = os.date("%Y", current_time)
- local month = os.date("%m", current_time)
- local day = os.date("%d", current_time)
- local hour = os.date("%H", current_time)
- local min = os.date("%M", current_time)
- local sec = os.date("%S", current_time)
- -- 自定义格式
- local formatted_date = string.format("%s-%s-%s %s:%s:%s", year, month, day, hour, min, sec)
- print("Standard format:", formatted_date) -- 输出类似: Standard format: 2023-05-15 14:30:45
- -- 更友好的格式
- local friendly_date = string.format("%s年%s月%s日 %s时%s分%s秒", year, month, day, hour, min, sec)
- print("Friendly format:", friendly_date) -- 输出类似: Friendly format: 2023年05月15日 14时30分45秒
复制代码
4. 进制转换
使用string.format进行不同进制之间的转换:
- function convertBase(num, fromBase, toBase)
- -- 将原进制数转换为十进制
- local decimal = 0
- local power = 1
- local digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-
- num = num:upper()
- for i = #num, 1, -1 do
- local digit = num:sub(i, i)
- local value = digits:find(digit) - 1
- if value >= fromBase then
- return nil, "Invalid digit for base " .. fromBase
- end
- decimal = decimal + value * power
- power = power * fromBase
- end
-
- -- 将十进制数转换为目标进制
- if toBase == 10 then
- return tostring(decimal)
- end
-
- local result = ""
- while decimal > 0 do
- local remainder = decimal % toBase
- result = digits:sub(remainder + 1, remainder + 1) .. result
- decimal = math.floor(decimal / toBase)
- end
-
- return result == "" and "0" or result
- end
- -- 示例:二进制转十进制
- local bin_num = "1010"
- local dec_num = convertBase(bin_num, 2, 10)
- print(string.format("Binary %s to Decimal: %s", bin_num, dec_num)) -- 输出: Binary 1010 to Decimal: 10
- -- 示例:十进制转十六进制
- local dec_num2 = "255"
- local hex_num = convertBase(dec_num2, 10, 16)
- print(string.format("Decimal %s to Hexadecimal: %s", dec_num2, hex_num)) -- 输出: Decimal 255 to Hexadecimal: FF
- -- 使用string.format直接进行十进制到其他进制的转换
- local num = 255
- print(string.format("Decimal %d to Octal: %o", num, num)) -- 输出: Decimal 255 to Octal: 377
- print(string.format("Decimal %d to Hexadecimal: %X", num, num)) -- 输出: Decimal 255 to Hexadecimal: FF
复制代码
5. 数据可视化中的格式化
在简单的控制台数据可视化中,格式化输出非常有用:
- -- 简单的水平条形图
- function drawBarChart(data, labels, max_label_length)
- max_label_length = max_label_length or 10
- local max_value = 0
-
- -- 找出最大值
- for _, value in ipairs(data) do
- if value > max_value then
- max_value = value
- end
- end
-
- -- 计算比例因子
- local scale = 50 / max_value -- 50是条形的最大长度
-
- -- 打印图表
- for i, value in ipairs(data) do
- local label = labels[i] or "Item " .. i
- local bar_length = math.floor(value * scale + 0.5)
- local bar = string.rep("=", bar_length)
-
- -- 格式化输出
- print(string.format("%-" .. max_label_length .. "s | %-50s | %6.2f",
- label, bar, value))
- end
- end
- -- 示例数据
- local sales_data = {120, 85, 160, 95, 140}
- local months = {"January", "February", "March", "April", "May"}
- -- 绘制图表
- print("Monthly Sales Chart:")
- drawBarChart(sales_data, months, 9)
复制代码
输出结果:
- Monthly Sales Chart:
- January | ================================================== | 120.00
- February | ================================== | 85.00
- March | =============================================================== | 160.00
- April | ======================================= | 95.00
- May | ========================================================= | 140.00
复制代码
高级技巧
1. 动态格式化字符串
有时需要根据运行时条件动态构建格式化字符串:
- function dynamicFormat(value, options)
- local format_str = "%"
-
- -- 添加标志
- if options.showSign then
- format_str = format_str .. "+"
- end
-
- -- 添加填充字符
- if options.padZero then
- format_str = format_str .. "0"
- end
-
- -- 添加宽度
- if options.width then
- format_str = format_str .. options.width
- end
-
- -- 添加精度
- if options.precision then
- format_str = format_str .. "." .. options.precision
- end
-
- -- 添加类型
- format_str = format_str .. (options.type or "f")
-
- return string.format(format_str, value)
- end
- -- 示例使用
- local num = 123.456789
- local options1 = {
- width = 10,
- precision = 2,
- type = "f"
- }
- print(dynamicFormat(num, options1)) -- 输出: " 123.46"
- local options2 = {
- showSign = true,
- padZero = true,
- width = 10,
- precision = 3,
- type = "f"
- }
- print(dynamicFormat(num, options2)) -- 输出: "+0123.457"
- local options3 = {
- width = 8,
- type = "X"
- }
- print(dynamicFormat(255, options3)) -- 输出: " FF"
复制代码
2. 格式化复杂的数据结构
对于表等复杂数据结构,可以编写递归函数来格式化输出:
- function formatTable(tbl, indent)
- indent = indent or 0
- local result = ""
- local indent_str = string.rep(" ", indent)
-
- result = result .. "{\n"
-
- for k, v in pairs(tbl) do
- result = result .. indent_str .. " "
-
- -- 格式化键
- if type(k) == "string" then
- result = result .. string.format("%s = ", k)
- else
- result = result .. string.format("[%s] = ", tostring(k))
- end
-
- -- 格式化值
- if type(v) == "table" then
- result = result .. formatTable(v, indent + 1)
- elseif type(v) == "string" then
- result = result .. string.format("%q", v) .. ",\n"
- elseif type(v) == "number" then
- result = result .. string.format("%f", v) .. ",\n"
- elseif type(v) == "boolean" then
- result = result .. tostring(v) .. ",\n"
- else
- result = result .. tostring(v) .. ",\n"
- end
- end
-
- result = result .. indent_str .. "}"
- if indent > 0 then
- result = result .. ",\n"
- end
-
- return result
- end
- -- 示例使用
- local data = {
- name = "John Doe",
- age = 30,
- scores = {math = 95, english = 88, science = 92},
- active = true,
- address = {
- street = "123 Main St",
- city = "Anytown",
- zip = 12345
- }
- }
- print(formatTable(data))
复制代码
输出结果:
- {
- active = true,
- address = {
- city = "Anytown",
- street = "123 Main St",
- zip = 12345.000000,
- },
- age = 30.000000,
- name = "John Doe",
- scores = {
- english = 88.000000,
- math = 95.000000,
- science = 92.000000,
- },
- }
复制代码
3. 本地化格式化
对于需要考虑本地化的应用程序,可以创建本地化格式化函数:
- -- 本地化设置
- local locales = {
- en_US = {
- decimal_point = ".",
- thousands_sep = ",",
- currency_symbol = "$",
- currency_format = "%s%s", -- 符号在前
- date_format = "%m/%d/%Y",
- time_format = "%I:%M:%S %p"
- },
- de_DE = {
- decimal_point = ",",
- thousands_sep = ".",
- currency_symbol = "€",
- currency_format = "%s %s", -- 符号在后
- date_format = "%d.%m.%Y",
- time_format = "%H:%M:%S"
- },
- ja_JP = {
- decimal_point = ".",
- thousands_sep = ",",
- currency_symbol = "¥",
- currency_format = "%s%s", -- 符号在前
- date_format = "%Y/%m/%d",
- time_format = "%H:%M:%S"
- }
- }
- -- 当前本地化设置
- local current_locale = "en_US"
- function setLocale(locale)
- if locales[locale] then
- current_locale = locale
- return true
- end
- return false
- end
- function formatLocalizedNumber(num)
- local locale = locales[current_locale]
- local formatted = tostring(num)
-
- -- 处理小数部分
- local integer_part, decimal_part = formatted:match("^(%d+)%.?(%d*)$")
-
- -- 添加千位分隔符
- local formatted_integer = ""
- local count = 0
- for i = #integer_part, 1, -1 do
- formatted_integer = integer_part:sub(i, i) .. formatted_integer
- count = count + 1
- if count % 3 == 0 and i > 1 then
- formatted_integer = locale.thousands_sep .. formatted_integer
- end
- end
-
- -- 组合整数和小数部分
- if decimal_part and decimal_part ~= "" then
- formatted = formatted_integer .. locale.decimal_point .. decimal_part
- else
- formatted = formatted_integer
- end
-
- return formatted
- end
- function formatLocalizedCurrency(amount)
- local locale = locales[current_locale]
- local formatted_num = formatLocalizedNumber(amount)
-
- -- 根据本地化设置格式化货币
- if locale.currency_format == "%s%s" then
- return string.format(locale.currency_format, locale.currency_symbol, formatted_num)
- else
- return string.format(locale.currency_format, formatted_num, locale.currency_symbol)
- end
- end
- -- 示例使用
- local amount = 1234567.89
- setLocale("en_US")
- print("US format: " .. formatLocalizedCurrency(amount)) -- 输出: US format: $1,234,567.89
- setLocale("de_DE")
- print("German format: " .. formatLocalizedCurrency(amount)) -- 输出: German format: 1.234.567,89 €
- setLocale("ja_JP")
- print("Japanese format: " .. formatLocalizedCurrency(amount)) -- 输出: Japanese format: ¥1,234,567.89
复制代码
4. 安全格式化
当处理用户提供的格式化字符串时,需要防止格式化字符串注入攻击:
- function safeFormat(template, ...)
- -- 转义所有格式说明符
- local escaped = template:gsub("%%", "%%%%")
-
- -- 使用转义后的模板进行格式化
- return string.format(escaped, ...)
- end
- -- 示例使用
- local user_input = "Hello %s, your balance is %d"
- local name = "Alice"
- local balance = 1000
- -- 不安全的格式化(可能被攻击)
- -- print(string.format(user_input, name, balance)) -- 这会出错,因为模板中有未处理的格式说明符
- -- 安全的格式化
- print(safeFormat(user_input, name, balance)) -- 输出: Hello %s, your balance is %d
复制代码
5. 条件格式化
根据条件应用不同的格式:
- function conditionalFormat(value, conditions)
- for _, condition in ipairs(conditions) do
- if condition.test(value) then
- return condition.format(value)
- end
- end
-
- -- 默认格式
- return tostring(value)
- end
- -- 示例使用
- local number = 12345.6789
- local conditions = {
- {
- test = function(v) return v >= 10000 end,
- format = function(v) return string.format("Large: %.2e", v) end
- },
- {
- test = function(v) return v >= 1000 end,
- format = function(v) return string.format("Medium: %.2f", v) end
- },
- {
- test = function(v) return v >= 0 end,
- format = function(v) return string.format("Small: %.2f", v) end
- },
- {
- test = function(v) return v < 0 end,
- format = function(v) return string.format("Negative: %.2f", v) end
- }
- }
- print(conditionalFormat(number, conditions)) -- 输出: Large: 1.23e+04
- print(conditionalFormat(500, conditions)) -- 输出: Medium: 500.00
- print(conditionalFormat(50, conditions)) -- 输出: Small: 50.00
- print(conditionalFormat(-50, conditions)) -- 输出: Negative: -50.00
复制代码
性能考虑和最佳实践
1. 避免频繁的字符串连接
在需要构建复杂字符串时,频繁使用字符串连接(..)可能会导致性能问题。可以使用table.concat和string.format结合来提高性能:
- -- 低效方式
- local result = ""
- for i = 1, 1000 do
- result = result .. string.format("Item %d: %s\n", i, tostring(i))
- end
- -- 高效方式
- local parts = {}
- for i = 1, 1000 do
- parts[#parts + 1] = string.format("Item %d: %s", i, tostring(i))
- end
- local result = table.concat(parts, "\n")
复制代码
2. 预编译格式化字符串
在循环中重复使用相同的格式化字符串时,可以预编译格式化字符串以提高性能:
- -- 低效方式
- for i = 1, 1000 do
- local message = string.format("Processing item %d of %d (%.1f%%)", i, 1000, i/10)
- -- 使用message...
- end
- -- 高效方式
- local format_str = "Processing item %d of %d (%.1f%%)"
- for i = 1, 1000 do
- local message = string.format(format_str, i, 1000, i/10)
- -- 使用message...
- end
复制代码
3. 缓存格式化结果
如果相同的值需要多次格式化,可以考虑缓存格式化结果:
- local format_cache = {}
- function cachedFormat(fmt, ...)
- local key = fmt .. table.concat({...}, "|")
-
- if not format_cache[key] then
- format_cache[key] = string.format(fmt, ...)
- end
-
- return format_cache[key]
- end
- -- 示例使用
- local name = "Alice"
- local age = 25
- -- 第一次调用会计算并缓存
- local message1 = cachedFormat("Name: %s, Age: %d", name, age)
- -- 第二次调用直接从缓存获取
- local message2 = cachedFormat("Name: %s, Age: %d", name, age)
- print(message1) -- 输出: Name: Alice, Age: 25
- print(message2) -- 输出: Name: Alice, Age: 25
复制代码
4. 限制缓存大小
为了避免缓存过大,可以实现一个带有大小限制的缓存:
- local LimitedCache = {}
- LimitedCache.__index = LimitedCache
- function LimitedCache.new(max_size)
- return setmetatable({
- items = {},
- max_size = max_size or 100,
- order = {}
- }, LimitedCache)
- end
- function LimitedCache:get(key)
- return self.items[key]
- end
- function LimitedCache:set(key, value)
- -- 如果键已存在,更新值
- if self.items[key] then
- self.items[key] = value
- return
- end
-
- -- 如果缓存已满,删除最旧的项
- if #self.order >= self.max_size then
- local oldest_key = table.remove(self.order, 1)
- self.items[oldest_key] = nil
- end
-
- -- 添加新项
- self.items[key] = value
- table.insert(self.order, key)
- end
- local format_cache = LimitedCache.new(100)
- function cachedFormatWithLimit(fmt, ...)
- local key = fmt .. table.concat({...}, "|")
-
- local cached = format_cache:get(key)
- if not cached then
- cached = string.format(fmt, ...)
- format_cache:set(key, cached)
- end
-
- return cached
- end
复制代码
常见问题和解决方案
1. 格式化字符串中的百分号问题
当需要在格式化字符串中包含百分号时,必须使用双百分号(%%)来转义:
- local percentage = 50
- -- 错误方式
- -- print(string.format("Progress: %d%", percentage)) -- 这会出错
- -- 正确方式
- print(string.format("Progress: %d%%", percentage)) -- 输出: Progress: 50%
复制代码
2. 格式化nil值
尝试格式化nil值会导致错误:
- -- 错误方式
- -- print(string.format("Value: %s", nil)) -- 这会出错
- -- 解决方案1:使用默认值
- local value = nil
- print(string.format("Value: %s", value or "N/A")) -- 输出: Value: N/A
- -- 解决方案2:创建安全的格式化函数
- function safeFormat(fmt, ...)
- local args = {...}
- for i, arg in ipairs(args) do
- if arg == nil then
- args[i] = "nil"
- end
- end
- return string.format(fmt, unpack(args))
- end
- print(safeFormat("Value: %s", nil)) -- 输出: Value: nil
复制代码
3. 格式化表类型
string.format不能直接格式化表类型:
- -- 错误方式
- -- local data = {name = "Alice", age = 25}
- -- print(string.format("Data: %s", data)) -- 这会出错
- -- 解决方案:自定义表格式化函数
- function formatTableSimple(tbl)
- if type(tbl) ~= "table" then
- return tostring(tbl)
- end
-
- local result = "{"
- local first = true
-
- for k, v in pairs(tbl) do
- if not first then
- result = result .. ", "
- end
- first = false
-
- if type(k) == "string" then
- result = result .. k .. "="
- else
- result = result .. "[" .. tostring(k) .. "]="
- end
-
- if type(v) == "string" then
- result = result .. '"' .. v .. '"'
- elseif type(v) == "table" then
- result = result .. formatTableSimple(v)
- else
- result = result .. tostring(v)
- end
- end
-
- return result .. "}"
- end
- local data = {name = "Alice", age = 25, scores = {math = 95, english = 88}}
- print(string.format("Data: %s", formatTableSimple(data)))
- -- 输出: Data: {name="Alice", age=25, scores={math=95, english=88}}
复制代码
4. 处理宽字符和Unicode
Lua的string.format对宽字符和Unicode的支持有限,特别是在长度计算方面:
- -- 示例:中文字符
- local chinese = "中文"
- print(string.format("Length of '%s': %d", chinese, #chinese)) -- 输出: Length of '中文': 4 (而不是2)
- -- 解决方案:使用UTF-8长度计算函数
- function utf8len(str)
- local len = 0
- local i = 1
- while i <= #str do
- local byte = str:byte(i)
- if byte >= 240 then
- len = len + 1
- i = i + 4
- elseif byte >= 224 then
- len = len + 1
- i = i + 3
- elseif byte >= 192 then
- len = len + 1
- i = i + 2
- else
- len = len + 1
- i = i + 1
- end
- end
- return len
- end
- print(string.format("UTF-8 length of '%s': %d", chinese, utf8len(chinese))) -- 输出: UTF-8 length of '中文': 2
复制代码
5. 格式化精度问题
浮点数格式化时可能会遇到精度问题:
- local num = 0.1 + 0.2
- print(string.format("0.1 + 0.2 = %.17f", num)) -- 输出: 0.1 + 0.2 = 0.30000000000000004
- -- 解决方案:使用四舍五入函数
- function round(num, decimal_places)
- local mult = 10 ^ (decimal_places or 0)
- return math.floor(num * mult + 0.5) / mult
- end
- print(string.format("Rounded: %.2f", round(num, 2))) -- 输出: Rounded: 0.30
复制代码
总结
Lua的string.format函数是一个强大而灵活的工具,可以帮助开发者以各种方式格式化输出数据。从基本的数字和字符串格式化,到复杂的表格对齐、数据可视化和本地化输出,string.format都能胜任。
通过掌握各种格式说明符和修饰符,开发者可以精确控制输出的格式,满足各种应用场景的需求。同时,了解高级技巧如动态格式化、条件格式化和安全格式化,可以帮助开发者编写更健壮、更灵活的代码。
在实际应用中,还需要考虑性能因素,避免不必要的字符串操作,合理使用缓存和预编译技术。同时,处理常见问题如nil值格式化、Unicode字符和浮点数精度等,也是编写高质量代码的重要方面。
通过本文的介绍和示例,希望开发者能够全面掌握Lua中的格式化输出技术,在实际编程中更加得心应手,创造出更加优雅、高效的代码。 |
|