活动公告

系统通知
05-18 21:22
系统通知
通知:本站资源由网友上传分享,如有违规等问题请到版务模块进行投诉,资源失效请在帖子内回复要求补档,会尽快处理!
10-23 09:31

Lua格式化输出详解从基础到高级技巧帮助开发者掌握stringformat函数解决实际编程中的数据格式化挑战优化输出效果

SunJu_FaceMall

3万

主题

2860

科技点

3万

积分

白金月票

碾压王

积分
32872

塔罗立华奏

<font color=白金月票" /> 发表于 2025-9-1 12:50:00 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
引言

在Lua编程中,格式化输出是一项基本但极其重要的技能。无论是日志记录、数据展示还是用户界面生成,良好的格式化输出都能使信息更加清晰易读。Lua提供了强大的string.format函数,它允许开发者按照特定格式输出数据,类似于C语言中的printf函数。本文将深入探讨string.format函数的各个方面,从基础用法到高级技巧,帮助开发者全面掌握Lua中的格式化输出技术,解决实际编程中的数据格式化挑战,优化输出效果。

基础部分:string.format函数的基本语法和用法

string.format函数是Lua标准库中用于格式化字符串的核心函数。它的基本语法如下:
  1. formatted_string = string.format(formatstring, ...)
复制代码

其中,formatstring是一个包含普通文本和格式说明符的字符串,而…是要格式化的参数列表。

最简单的例子:
  1. local name = "Alice"
  2. local age = 25
  3. local message = string.format("Name: %s, Age: %d", name, age)
  4. print(message)  -- 输出: Name: Alice, Age: 25
复制代码

在这个例子中,%s和%d是格式说明符,分别表示字符串和十进制整数。string.format会用后面的参数替换这些说明符,生成最终的格式化字符串。

格式化选项详解

Lua中的string.format支持多种格式说明符,每种都有特定的用途。下面详细介绍各种格式说明符:

基本格式说明符

1.
  1. %s - 字符串local str = "Hello"
  2. print(string.format("String: %s", str))  -- 输出: String: Hello
复制代码
2.
  1. %d - 十进制整数local num = 42
  2. print(string.format("Number: %d", num))  -- 输出: Number: 42
复制代码
3.
  1. %i - 同%d,十进制整数local num = 42
  2. print(string.format("Number: %i", num))  -- 输出: Number: 42
复制代码
4.
  1. %f - 浮点数local pi = 3.14159
  2. print(string.format("Pi: %f", pi))  -- 输出: Pi: 3.141590
复制代码
5.
  1. %c - 字符(将数字转换为对应的ASCII字符)local ascii_code = 65
  2. print(string.format("Character: %c", ascii_code))  -- 输出: Character: A
复制代码
6.
  1. %o - 八进制数local num = 16
  2. print(string.format("Octal: %o", num))  -- 输出: Octal: 20
复制代码
7.
  1. %u - 无符号十进制整数local num = 42
  2. print(string.format("Unsigned: %u", num))  -- 输出: Unsigned: 42
复制代码
8.
  1. %x - 小写十六进制数local num = 255
  2. print(string.format("Hex: %x", num))  -- 输出: Hex: ff
复制代码
9.
  1. %X - 大写十六进制数local num = 255
  2. print(string.format("Hex: %X", num))  -- 输出: Hex: FF
复制代码
10.
  1. %e - 科学计数法(小写e)local num = 12345.67
  2. print(string.format("Scientific: %e", num))  -- 输出: Scientific: 1.234567e+04
复制代码
11.
  1. %E - 科学计数法(大写E)local num = 12345.67
  2. print(string.format("Scientific: %E", num))  -- 输出: Scientific: 1.234567E+04
复制代码
12.
  1. %g - 根据值的大小自动选择%f或%e格式(紧凑形式)local num1 = 123.456
  2. local num2 = 123456789.123
  3. print(string.format("Compact1: %g", num1))  -- 输出: Compact1: 123.456
  4. print(string.format("Compact2: %g", num2))  -- 输出: Compact2: 1.23457e+08
复制代码
13.
  1. %G - 同%g,但使用大写字母local num = 123456789.123
  2. print(string.format("Compact: %G", num))  -- 输出: Compact: 1.23457E+08
复制代码
14.
  1. %% - 百分号本身local percentage = 50
  2. print(string.format("Progress: %d%%", percentage))  -- 输出: Progress: 50%
复制代码

%s - 字符串
  1. local str = "Hello"
  2. print(string.format("String: %s", str))  -- 输出: String: Hello
复制代码

%d - 十进制整数
  1. local num = 42
  2. print(string.format("Number: %d", num))  -- 输出: Number: 42
复制代码

%i - 同%d,十进制整数
  1. local num = 42
  2. print(string.format("Number: %i", num))  -- 输出: Number: 42
复制代码

%f - 浮点数
  1. local pi = 3.14159
  2. print(string.format("Pi: %f", pi))  -- 输出: Pi: 3.141590
复制代码

%c - 字符(将数字转换为对应的ASCII字符)
  1. local ascii_code = 65
  2. print(string.format("Character: %c", ascii_code))  -- 输出: Character: A
复制代码

%o - 八进制数
  1. local num = 16
  2. print(string.format("Octal: %o", num))  -- 输出: Octal: 20
复制代码

%u - 无符号十进制整数
  1. local num = 42
  2. print(string.format("Unsigned: %u", num))  -- 输出: Unsigned: 42
复制代码

%x - 小写十六进制数
  1. local num = 255
  2. print(string.format("Hex: %x", num))  -- 输出: Hex: ff
复制代码

%X - 大写十六进制数
  1. local num = 255
  2. print(string.format("Hex: %X", num))  -- 输出: Hex: FF
复制代码

%e - 科学计数法(小写e)
  1. local num = 12345.67
  2. print(string.format("Scientific: %e", num))  -- 输出: Scientific: 1.234567e+04
复制代码

%E - 科学计数法(大写E)
  1. local num = 12345.67
  2. print(string.format("Scientific: %E", num))  -- 输出: Scientific: 1.234567E+04
复制代码

%g - 根据值的大小自动选择%f或%e格式(紧凑形式)
  1. local num1 = 123.456
  2. local num2 = 123456789.123
  3. print(string.format("Compact1: %g", num1))  -- 输出: Compact1: 123.456
  4. print(string.format("Compact2: %g", num2))  -- 输出: Compact2: 1.23457e+08
复制代码

%G - 同%g,但使用大写字母
  1. local num = 123456789.123
  2. print(string.format("Compact: %G", num))  -- 输出: Compact: 1.23457E+08
复制代码

%% - 百分号本身
  1. local percentage = 50
  2. print(string.format("Progress: %d%%", percentage))  -- 输出: Progress: 50%
复制代码

格式修饰符

除了基本的格式说明符外,Lua还支持多种修饰符来控制输出的格式:

1.
  1. 宽度修饰符local num = 42
  2. print(string.format("Number: %5d", num))  -- 输出: Number:    42 (总宽度为5,右对齐)
  3. print(string.format("Number: %-5d", num)) -- 输出: Number: 42   (总宽度为5,左对齐)
复制代码
2.
  1. 精度修饰符local pi = 3.14159265359
  2. print(string.format("Pi: %.2f", pi))  -- 输出: Pi: 3.14 (保留2位小数)
  3. print(string.format("Pi: %.5f", pi))  -- 输出: Pi: 3.14159 (保留5位小数)
复制代码
3.
  1. 标志修饰符+:显示数字的正负号local num1 = 42
  2. local num2 = -42
  3. print(string.format("Num1: %+d", num1))  -- 输出: Num1: +42
  4. print(string.format("Num2: %+d", num2))  -- 输出: Num2: -420:用0填充而不是空格local num = 42
  5. print(string.format("Num: %05d", num))  -- 输出: Num: 00042#:对于%o,添加前缀0;对于%x/%X,添加前缀0x/0Xlocal num = 255
  6. print(string.format("Octal: %#o", num))    -- 输出: Octal: 0377
  7. print(string.format("Hex: %#x", num))      -- 输出: Hex: 0xff
  8. print(string.format("Hex: %#X", num))      -- 输出: Hex: 0XFF(空格):对于正数,在前面添加一个空格local num1 = 42
  9. local num2 = -42
  10. print(string.format("Num1: % d", num1))  -- 输出: Num1:  42 (注意前面的空格)
  11. print(string.format("Num2: % d", num2))  -- 输出: Num2: -42
复制代码
4.
  1. +:显示数字的正负号local num1 = 42
  2. local num2 = -42
  3. print(string.format("Num1: %+d", num1))  -- 输出: Num1: +42
  4. print(string.format("Num2: %+d", num2))  -- 输出: Num2: -42
复制代码
5.
  1. 0:用0填充而不是空格local num = 42
  2. print(string.format("Num: %05d", num))  -- 输出: Num: 00042
复制代码
6.
  1. #:对于%o,添加前缀0;对于%x/%X,添加前缀0x/0Xlocal num = 255
  2. print(string.format("Octal: %#o", num))    -- 输出: Octal: 0377
  3. print(string.format("Hex: %#x", num))      -- 输出: Hex: 0xff
  4. print(string.format("Hex: %#X", num))      -- 输出: Hex: 0XFF
复制代码
7.
  1. (空格):对于正数,在前面添加一个空格local num1 = 42
  2. local num2 = -42
  3. print(string.format("Num1: % d", num1))  -- 输出: Num1:  42 (注意前面的空格)
  4. print(string.format("Num2: % d", num2))  -- 输出: Num2: -42
复制代码

宽度修饰符
  1. local num = 42
  2. print(string.format("Number: %5d", num))  -- 输出: Number:    42 (总宽度为5,右对齐)
  3. print(string.format("Number: %-5d", num)) -- 输出: Number: 42   (总宽度为5,左对齐)
复制代码

精度修饰符
  1. local pi = 3.14159265359
  2. print(string.format("Pi: %.2f", pi))  -- 输出: Pi: 3.14 (保留2位小数)
  3. print(string.format("Pi: %.5f", pi))  -- 输出: Pi: 3.14159 (保留5位小数)
复制代码

标志修饰符

  1. +:显示数字的正负号local num1 = 42
  2. local num2 = -42
  3. print(string.format("Num1: %+d", num1))  -- 输出: Num1: +42
  4. print(string.format("Num2: %+d", num2))  -- 输出: Num2: -42
复制代码
  1. 0:用0填充而不是空格local num = 42
  2. print(string.format("Num: %05d", num))  -- 输出: Num: 00042
复制代码
  1. #:对于%o,添加前缀0;对于%x/%X,添加前缀0x/0Xlocal num = 255
  2. print(string.format("Octal: %#o", num))    -- 输出: Octal: 0377
  3. print(string.format("Hex: %#x", num))      -- 输出: Hex: 0xff
  4. print(string.format("Hex: %#X", num))      -- 输出: Hex: 0XFF
复制代码
  1. (空格):对于正数,在前面添加一个空格local num1 = 42
  2. local num2 = -42
  3. print(string.format("Num1: % d", num1))  -- 输出: Num1:  42 (注意前面的空格)
  4. print(string.format("Num2: % d", num2))  -- 输出: Num2: -42
复制代码

+:显示数字的正负号
  1. local num1 = 42
  2. local num2 = -42
  3. print(string.format("Num1: %+d", num1))  -- 输出: Num1: +42
  4. print(string.format("Num2: %+d", num2))  -- 输出: Num2: -42
复制代码

0:用0填充而不是空格
  1. local num = 42
  2. print(string.format("Num: %05d", num))  -- 输出: Num: 00042
复制代码

#:对于%o,添加前缀0;对于%x/%X,添加前缀0x/0X
  1. local num = 255
  2. print(string.format("Octal: %#o", num))    -- 输出: Octal: 0377
  3. print(string.format("Hex: %#x", num))      -- 输出: Hex: 0xff
  4. print(string.format("Hex: %#X", num))      -- 输出: Hex: 0XFF
复制代码

(空格):对于正数,在前面添加一个空格
  1. local num1 = 42
  2. local num2 = -42
  3. print(string.format("Num1: % d", num1))  -- 输出: Num1:  42 (注意前面的空格)
  4. print(string.format("Num2: % d", num2))  -- 输出: Num2: -42
复制代码

组合使用修饰符

可以组合使用多个修饰符来达到更复杂的格式化效果:
  1. local pi = 3.14159
  2. local num = 42
  3. -- 宽度、精度和对齐的组合
  4. print(string.format("Pi: %10.2f", pi))  -- 输出: Pi:       3.14 (总宽度10,保留2位小数,右对齐)
  5. print(string.format("Pi: %-10.2f", pi)) -- 输出: Pi: 3.14      (总宽度10,保留2位小数,左对齐)
  6. -- 标志、宽度和精度的组合
  7. print(string.format("Num: %+05d", num))  -- 输出: Num: +0042 (显示符号,用0填充,总宽度5)
  8. print(string.format("Num: %#06X", num))  -- 输出: Num: 0X002A (添加前缀,用0填充,总宽度6)
复制代码

实际应用案例

1. 表格对齐输出

在需要输出对齐的表格数据时,string.format非常有用:
  1. local users = {
  2.     {name = "Alice", age = 25, score = 95.5},
  3.     {name = "Bob", age = 30, score = 87.2},
  4.     {name = "Charlie", age = 35, score = 92.8}
  5. }
  6. -- 打印表头
  7. print(string.format("%-10s | %3s | %5s", "Name", "Age", "Score"))
  8. print(string.format("%-10s | %3s | %5s", "----------", "---", "-----"))
  9. -- 打印数据行
  10. for _, user in ipairs(users) do
  11.     print(string.format("%-10s | %3d | %5.1f", user.name, user.age, user.score))
  12. end
复制代码

输出结果:
  1. Name       | Age | Score
  2. ---------- | --- | -----
  3. Alice      |  25 |  95.5
  4. Bob        |  30 |  87.2
  5. Charlie    |  35 |  92.8
复制代码

2. 货币格式化

格式化货币金额,添加千位分隔符和固定小数位数:
  1. function formatCurrency(amount)
  2.     -- 分离整数部分和小数部分
  3.     local integer_part = math.floor(amount)
  4.     local decimal_part = math.floor((amount - integer_part) * 100 + 0.5)
  5.    
  6.     -- 添加千位分隔符
  7.     local formatted_integer = tostring(integer_part)
  8.     local formatted_with_commas = ""
  9.     local count = 0
  10.    
  11.     -- 从右到左遍历字符串,每3位添加一个逗号
  12.     for i = #formatted_integer, 1, -1 do
  13.         formatted_with_commas = formatted_integer:sub(i, i) .. formatted_with_commas
  14.         count = count + 1
  15.         if count % 3 == 0 and i > 1 then
  16.             formatted_with_commas = "," .. formatted_with_commas
  17.         end
  18.     end
  19.    
  20.     -- 组合整数部分和小数部分
  21.     return string.format("$%s.%02d", formatted_with_commas, decimal_part)
  22. end
  23. local price1 = 1234567.8912
  24. local price2 = 42.5
  25. local price3 = 1000
  26. print(formatCurrency(price1))  -- 输出: $1,234,567.89
  27. print(formatCurrency(price2))  -- 输出: $42.50
  28. print(formatCurrency(price3))  -- 输出: $1,000.00
复制代码

3. 时间和日期格式化

虽然Lua没有内置的日期格式化函数,但可以使用os.date和string.format结合来创建自定义的日期时间格式:
  1. -- 获取当前时间
  2. local current_time = os.time()
  3. -- 使用os.date获取时间组件
  4. local year = os.date("%Y", current_time)
  5. local month = os.date("%m", current_time)
  6. local day = os.date("%d", current_time)
  7. local hour = os.date("%H", current_time)
  8. local min = os.date("%M", current_time)
  9. local sec = os.date("%S", current_time)
  10. -- 自定义格式
  11. local formatted_date = string.format("%s-%s-%s %s:%s:%s", year, month, day, hour, min, sec)
  12. print("Standard format:", formatted_date)  -- 输出类似: Standard format: 2023-05-15 14:30:45
  13. -- 更友好的格式
  14. local friendly_date = string.format("%s年%s月%s日 %s时%s分%s秒", year, month, day, hour, min, sec)
  15. print("Friendly format:", friendly_date)  -- 输出类似: Friendly format: 2023年05月15日 14时30分45秒
复制代码

4. 进制转换

使用string.format进行不同进制之间的转换:
  1. function convertBase(num, fromBase, toBase)
  2.     -- 将原进制数转换为十进制
  3.     local decimal = 0
  4.     local power = 1
  5.     local digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  6.    
  7.     num = num:upper()
  8.     for i = #num, 1, -1 do
  9.         local digit = num:sub(i, i)
  10.         local value = digits:find(digit) - 1
  11.         if value >= fromBase then
  12.             return nil, "Invalid digit for base " .. fromBase
  13.         end
  14.         decimal = decimal + value * power
  15.         power = power * fromBase
  16.     end
  17.    
  18.     -- 将十进制数转换为目标进制
  19.     if toBase == 10 then
  20.         return tostring(decimal)
  21.     end
  22.    
  23.     local result = ""
  24.     while decimal > 0 do
  25.         local remainder = decimal % toBase
  26.         result = digits:sub(remainder + 1, remainder + 1) .. result
  27.         decimal = math.floor(decimal / toBase)
  28.     end
  29.    
  30.     return result == "" and "0" or result
  31. end
  32. -- 示例:二进制转十进制
  33. local bin_num = "1010"
  34. local dec_num = convertBase(bin_num, 2, 10)
  35. print(string.format("Binary %s to Decimal: %s", bin_num, dec_num))  -- 输出: Binary 1010 to Decimal: 10
  36. -- 示例:十进制转十六进制
  37. local dec_num2 = "255"
  38. local hex_num = convertBase(dec_num2, 10, 16)
  39. print(string.format("Decimal %s to Hexadecimal: %s", dec_num2, hex_num))  -- 输出: Decimal 255 to Hexadecimal: FF
  40. -- 使用string.format直接进行十进制到其他进制的转换
  41. local num = 255
  42. print(string.format("Decimal %d to Octal: %o", num, num))      -- 输出: Decimal 255 to Octal: 377
  43. print(string.format("Decimal %d to Hexadecimal: %X", num, num)) -- 输出: Decimal 255 to Hexadecimal: FF
复制代码

5. 数据可视化中的格式化

在简单的控制台数据可视化中,格式化输出非常有用:
  1. -- 简单的水平条形图
  2. function drawBarChart(data, labels, max_label_length)
  3.     max_label_length = max_label_length or 10
  4.     local max_value = 0
  5.    
  6.     -- 找出最大值
  7.     for _, value in ipairs(data) do
  8.         if value > max_value then
  9.             max_value = value
  10.         end
  11.     end
  12.    
  13.     -- 计算比例因子
  14.     local scale = 50 / max_value  -- 50是条形的最大长度
  15.    
  16.     -- 打印图表
  17.     for i, value in ipairs(data) do
  18.         local label = labels[i] or "Item " .. i
  19.         local bar_length = math.floor(value * scale + 0.5)
  20.         local bar = string.rep("=", bar_length)
  21.         
  22.         -- 格式化输出
  23.         print(string.format("%-" .. max_label_length .. "s | %-50s | %6.2f",
  24.               label, bar, value))
  25.     end
  26. end
  27. -- 示例数据
  28. local sales_data = {120, 85, 160, 95, 140}
  29. local months = {"January", "February", "March", "April", "May"}
  30. -- 绘制图表
  31. print("Monthly Sales Chart:")
  32. drawBarChart(sales_data, months, 9)
复制代码

输出结果:
  1. Monthly Sales Chart:
  2. January   | ================================================== | 120.00
  3. February  | ==================================               |  85.00
  4. March     | =============================================================== | 160.00
  5. April     | =======================================         |  95.00
  6. May       | =========================================================       | 140.00
复制代码

高级技巧

1. 动态格式化字符串

有时需要根据运行时条件动态构建格式化字符串:
  1. function dynamicFormat(value, options)
  2.     local format_str = "%"
  3.    
  4.     -- 添加标志
  5.     if options.showSign then
  6.         format_str = format_str .. "+"
  7.     end
  8.    
  9.     -- 添加填充字符
  10.     if options.padZero then
  11.         format_str = format_str .. "0"
  12.     end
  13.    
  14.     -- 添加宽度
  15.     if options.width then
  16.         format_str = format_str .. options.width
  17.     end
  18.    
  19.     -- 添加精度
  20.     if options.precision then
  21.         format_str = format_str .. "." .. options.precision
  22.     end
  23.    
  24.     -- 添加类型
  25.     format_str = format_str .. (options.type or "f")
  26.    
  27.     return string.format(format_str, value)
  28. end
  29. -- 示例使用
  30. local num = 123.456789
  31. local options1 = {
  32.     width = 10,
  33.     precision = 2,
  34.     type = "f"
  35. }
  36. print(dynamicFormat(num, options1))  -- 输出: "  123.46"
  37. local options2 = {
  38.     showSign = true,
  39.     padZero = true,
  40.     width = 10,
  41.     precision = 3,
  42.     type = "f"
  43. }
  44. print(dynamicFormat(num, options2))  -- 输出: "+0123.457"
  45. local options3 = {
  46.     width = 8,
  47.     type = "X"
  48. }
  49. print(dynamicFormat(255, options3))  -- 输出: "      FF"
复制代码

2. 格式化复杂的数据结构

对于表等复杂数据结构,可以编写递归函数来格式化输出:
  1. function formatTable(tbl, indent)
  2.     indent = indent or 0
  3.     local result = ""
  4.     local indent_str = string.rep("  ", indent)
  5.    
  6.     result = result .. "{\n"
  7.    
  8.     for k, v in pairs(tbl) do
  9.         result = result .. indent_str .. "  "
  10.         
  11.         -- 格式化键
  12.         if type(k) == "string" then
  13.             result = result .. string.format("%s = ", k)
  14.         else
  15.             result = result .. string.format("[%s] = ", tostring(k))
  16.         end
  17.         
  18.         -- 格式化值
  19.         if type(v) == "table" then
  20.             result = result .. formatTable(v, indent + 1)
  21.         elseif type(v) == "string" then
  22.             result = result .. string.format("%q", v) .. ",\n"
  23.         elseif type(v) == "number" then
  24.             result = result .. string.format("%f", v) .. ",\n"
  25.         elseif type(v) == "boolean" then
  26.             result = result .. tostring(v) .. ",\n"
  27.         else
  28.             result = result .. tostring(v) .. ",\n"
  29.         end
  30.     end
  31.    
  32.     result = result .. indent_str .. "}"
  33.     if indent > 0 then
  34.         result = result .. ",\n"
  35.     end
  36.    
  37.     return result
  38. end
  39. -- 示例使用
  40. local data = {
  41.     name = "John Doe",
  42.     age = 30,
  43.     scores = {math = 95, english = 88, science = 92},
  44.     active = true,
  45.     address = {
  46.         street = "123 Main St",
  47.         city = "Anytown",
  48.         zip = 12345
  49.     }
  50. }
  51. print(formatTable(data))
复制代码

输出结果:
  1. {
  2.   active = true,
  3.   address = {
  4.     city = "Anytown",
  5.     street = "123 Main St",
  6.     zip = 12345.000000,
  7.   },
  8.   age = 30.000000,
  9.   name = "John Doe",
  10.   scores = {
  11.     english = 88.000000,
  12.     math = 95.000000,
  13.     science = 92.000000,
  14.   },
  15. }
复制代码

3. 本地化格式化

对于需要考虑本地化的应用程序,可以创建本地化格式化函数:
  1. -- 本地化设置
  2. local locales = {
  3.     en_US = {
  4.         decimal_point = ".",
  5.         thousands_sep = ",",
  6.         currency_symbol = "$",
  7.         currency_format = "%s%s",  -- 符号在前
  8.         date_format = "%m/%d/%Y",
  9.         time_format = "%I:%M:%S %p"
  10.     },
  11.     de_DE = {
  12.         decimal_point = ",",
  13.         thousands_sep = ".",
  14.         currency_symbol = "€",
  15.         currency_format = "%s %s",  -- 符号在后
  16.         date_format = "%d.%m.%Y",
  17.         time_format = "%H:%M:%S"
  18.     },
  19.     ja_JP = {
  20.         decimal_point = ".",
  21.         thousands_sep = ",",
  22.         currency_symbol = "¥",
  23.         currency_format = "%s%s",  -- 符号在前
  24.         date_format = "%Y/%m/%d",
  25.         time_format = "%H:%M:%S"
  26.     }
  27. }
  28. -- 当前本地化设置
  29. local current_locale = "en_US"
  30. function setLocale(locale)
  31.     if locales[locale] then
  32.         current_locale = locale
  33.         return true
  34.     end
  35.     return false
  36. end
  37. function formatLocalizedNumber(num)
  38.     local locale = locales[current_locale]
  39.     local formatted = tostring(num)
  40.    
  41.     -- 处理小数部分
  42.     local integer_part, decimal_part = formatted:match("^(%d+)%.?(%d*)$")
  43.    
  44.     -- 添加千位分隔符
  45.     local formatted_integer = ""
  46.     local count = 0
  47.     for i = #integer_part, 1, -1 do
  48.         formatted_integer = integer_part:sub(i, i) .. formatted_integer
  49.         count = count + 1
  50.         if count % 3 == 0 and i > 1 then
  51.             formatted_integer = locale.thousands_sep .. formatted_integer
  52.         end
  53.     end
  54.    
  55.     -- 组合整数和小数部分
  56.     if decimal_part and decimal_part ~= "" then
  57.         formatted = formatted_integer .. locale.decimal_point .. decimal_part
  58.     else
  59.         formatted = formatted_integer
  60.     end
  61.    
  62.     return formatted
  63. end
  64. function formatLocalizedCurrency(amount)
  65.     local locale = locales[current_locale]
  66.     local formatted_num = formatLocalizedNumber(amount)
  67.    
  68.     -- 根据本地化设置格式化货币
  69.     if locale.currency_format == "%s%s" then
  70.         return string.format(locale.currency_format, locale.currency_symbol, formatted_num)
  71.     else
  72.         return string.format(locale.currency_format, formatted_num, locale.currency_symbol)
  73.     end
  74. end
  75. -- 示例使用
  76. local amount = 1234567.89
  77. setLocale("en_US")
  78. print("US format: " .. formatLocalizedCurrency(amount))  -- 输出: US format: $1,234,567.89
  79. setLocale("de_DE")
  80. print("German format: " .. formatLocalizedCurrency(amount))  -- 输出: German format: 1.234.567,89 €
  81. setLocale("ja_JP")
  82. print("Japanese format: " .. formatLocalizedCurrency(amount))  -- 输出: Japanese format: ¥1,234,567.89
复制代码

4. 安全格式化

当处理用户提供的格式化字符串时,需要防止格式化字符串注入攻击:
  1. function safeFormat(template, ...)
  2.     -- 转义所有格式说明符
  3.     local escaped = template:gsub("%%", "%%%%")
  4.    
  5.     -- 使用转义后的模板进行格式化
  6.     return string.format(escaped, ...)
  7. end
  8. -- 示例使用
  9. local user_input = "Hello %s, your balance is %d"
  10. local name = "Alice"
  11. local balance = 1000
  12. -- 不安全的格式化(可能被攻击)
  13. -- print(string.format(user_input, name, balance))  -- 这会出错,因为模板中有未处理的格式说明符
  14. -- 安全的格式化
  15. print(safeFormat(user_input, name, balance))  -- 输出: Hello %s, your balance is %d
复制代码

5. 条件格式化

根据条件应用不同的格式:
  1. function conditionalFormat(value, conditions)
  2.     for _, condition in ipairs(conditions) do
  3.         if condition.test(value) then
  4.             return condition.format(value)
  5.         end
  6.     end
  7.    
  8.     -- 默认格式
  9.     return tostring(value)
  10. end
  11. -- 示例使用
  12. local number = 12345.6789
  13. local conditions = {
  14.     {
  15.         test = function(v) return v >= 10000 end,
  16.         format = function(v) return string.format("Large: %.2e", v) end
  17.     },
  18.     {
  19.         test = function(v) return v >= 1000 end,
  20.         format = function(v) return string.format("Medium: %.2f", v) end
  21.     },
  22.     {
  23.         test = function(v) return v >= 0 end,
  24.         format = function(v) return string.format("Small: %.2f", v) end
  25.     },
  26.     {
  27.         test = function(v) return v < 0 end,
  28.         format = function(v) return string.format("Negative: %.2f", v) end
  29.     }
  30. }
  31. print(conditionalFormat(number, conditions))  -- 输出: Large: 1.23e+04
  32. print(conditionalFormat(500, conditions))    -- 输出: Medium: 500.00
  33. print(conditionalFormat(50, conditions))     -- 输出: Small: 50.00
  34. print(conditionalFormat(-50, conditions))    -- 输出: Negative: -50.00
复制代码

性能考虑和最佳实践

1. 避免频繁的字符串连接

在需要构建复杂字符串时,频繁使用字符串连接(..)可能会导致性能问题。可以使用table.concat和string.format结合来提高性能:
  1. -- 低效方式
  2. local result = ""
  3. for i = 1, 1000 do
  4.     result = result .. string.format("Item %d: %s\n", i, tostring(i))
  5. end
  6. -- 高效方式
  7. local parts = {}
  8. for i = 1, 1000 do
  9.     parts[#parts + 1] = string.format("Item %d: %s", i, tostring(i))
  10. end
  11. local result = table.concat(parts, "\n")
复制代码

2. 预编译格式化字符串

在循环中重复使用相同的格式化字符串时,可以预编译格式化字符串以提高性能:
  1. -- 低效方式
  2. for i = 1, 1000 do
  3.     local message = string.format("Processing item %d of %d (%.1f%%)", i, 1000, i/10)
  4.     -- 使用message...
  5. end
  6. -- 高效方式
  7. local format_str = "Processing item %d of %d (%.1f%%)"
  8. for i = 1, 1000 do
  9.     local message = string.format(format_str, i, 1000, i/10)
  10.     -- 使用message...
  11. end
复制代码

3. 缓存格式化结果

如果相同的值需要多次格式化,可以考虑缓存格式化结果:
  1. local format_cache = {}
  2. function cachedFormat(fmt, ...)
  3.     local key = fmt .. table.concat({...}, "|")
  4.    
  5.     if not format_cache[key] then
  6.         format_cache[key] = string.format(fmt, ...)
  7.     end
  8.    
  9.     return format_cache[key]
  10. end
  11. -- 示例使用
  12. local name = "Alice"
  13. local age = 25
  14. -- 第一次调用会计算并缓存
  15. local message1 = cachedFormat("Name: %s, Age: %d", name, age)
  16. -- 第二次调用直接从缓存获取
  17. local message2 = cachedFormat("Name: %s, Age: %d", name, age)
  18. print(message1)  -- 输出: Name: Alice, Age: 25
  19. print(message2)  -- 输出: Name: Alice, Age: 25
复制代码

4. 限制缓存大小

为了避免缓存过大,可以实现一个带有大小限制的缓存:
  1. local LimitedCache = {}
  2. LimitedCache.__index = LimitedCache
  3. function LimitedCache.new(max_size)
  4.     return setmetatable({
  5.         items = {},
  6.         max_size = max_size or 100,
  7.         order = {}
  8.     }, LimitedCache)
  9. end
  10. function LimitedCache:get(key)
  11.     return self.items[key]
  12. end
  13. function LimitedCache:set(key, value)
  14.     -- 如果键已存在,更新值
  15.     if self.items[key] then
  16.         self.items[key] = value
  17.         return
  18.     end
  19.    
  20.     -- 如果缓存已满,删除最旧的项
  21.     if #self.order >= self.max_size then
  22.         local oldest_key = table.remove(self.order, 1)
  23.         self.items[oldest_key] = nil
  24.     end
  25.    
  26.     -- 添加新项
  27.     self.items[key] = value
  28.     table.insert(self.order, key)
  29. end
  30. local format_cache = LimitedCache.new(100)
  31. function cachedFormatWithLimit(fmt, ...)
  32.     local key = fmt .. table.concat({...}, "|")
  33.    
  34.     local cached = format_cache:get(key)
  35.     if not cached then
  36.         cached = string.format(fmt, ...)
  37.         format_cache:set(key, cached)
  38.     end
  39.    
  40.     return cached
  41. end
复制代码

常见问题和解决方案

1. 格式化字符串中的百分号问题

当需要在格式化字符串中包含百分号时,必须使用双百分号(%%)来转义:
  1. local percentage = 50
  2. -- 错误方式
  3. -- print(string.format("Progress: %d%", percentage))  -- 这会出错
  4. -- 正确方式
  5. print(string.format("Progress: %d%%", percentage))  -- 输出: Progress: 50%
复制代码

2. 格式化nil值

尝试格式化nil值会导致错误:
  1. -- 错误方式
  2. -- print(string.format("Value: %s", nil))  -- 这会出错
  3. -- 解决方案1:使用默认值
  4. local value = nil
  5. print(string.format("Value: %s", value or "N/A"))  -- 输出: Value: N/A
  6. -- 解决方案2:创建安全的格式化函数
  7. function safeFormat(fmt, ...)
  8.     local args = {...}
  9.     for i, arg in ipairs(args) do
  10.         if arg == nil then
  11.             args[i] = "nil"
  12.         end
  13.     end
  14.     return string.format(fmt, unpack(args))
  15. end
  16. print(safeFormat("Value: %s", nil))  -- 输出: Value: nil
复制代码

3. 格式化表类型

string.format不能直接格式化表类型:
  1. -- 错误方式
  2. -- local data = {name = "Alice", age = 25}
  3. -- print(string.format("Data: %s", data))  -- 这会出错
  4. -- 解决方案:自定义表格式化函数
  5. function formatTableSimple(tbl)
  6.     if type(tbl) ~= "table" then
  7.         return tostring(tbl)
  8.     end
  9.    
  10.     local result = "{"
  11.     local first = true
  12.    
  13.     for k, v in pairs(tbl) do
  14.         if not first then
  15.             result = result .. ", "
  16.         end
  17.         first = false
  18.         
  19.         if type(k) == "string" then
  20.             result = result .. k .. "="
  21.         else
  22.             result = result .. "[" .. tostring(k) .. "]="
  23.         end
  24.         
  25.         if type(v) == "string" then
  26.             result = result .. '"' .. v .. '"'
  27.         elseif type(v) == "table" then
  28.             result = result .. formatTableSimple(v)
  29.         else
  30.             result = result .. tostring(v)
  31.         end
  32.     end
  33.    
  34.     return result .. "}"
  35. end
  36. local data = {name = "Alice", age = 25, scores = {math = 95, english = 88}}
  37. print(string.format("Data: %s", formatTableSimple(data)))
  38. -- 输出: Data: {name="Alice", age=25, scores={math=95, english=88}}
复制代码

4. 处理宽字符和Unicode

Lua的string.format对宽字符和Unicode的支持有限,特别是在长度计算方面:
  1. -- 示例:中文字符
  2. local chinese = "中文"
  3. print(string.format("Length of '%s': %d", chinese, #chinese))  -- 输出: Length of '中文': 4 (而不是2)
  4. -- 解决方案:使用UTF-8长度计算函数
  5. function utf8len(str)
  6.     local len = 0
  7.     local i = 1
  8.     while i <= #str do
  9.         local byte = str:byte(i)
  10.         if byte >= 240 then
  11.             len = len + 1
  12.             i = i + 4
  13.         elseif byte >= 224 then
  14.             len = len + 1
  15.             i = i + 3
  16.         elseif byte >= 192 then
  17.             len = len + 1
  18.             i = i + 2
  19.         else
  20.             len = len + 1
  21.             i = i + 1
  22.         end
  23.     end
  24.     return len
  25. end
  26. print(string.format("UTF-8 length of '%s': %d", chinese, utf8len(chinese)))  -- 输出: UTF-8 length of '中文': 2
复制代码

5. 格式化精度问题

浮点数格式化时可能会遇到精度问题:
  1. local num = 0.1 + 0.2
  2. print(string.format("0.1 + 0.2 = %.17f", num))  -- 输出: 0.1 + 0.2 = 0.30000000000000004
  3. -- 解决方案:使用四舍五入函数
  4. function round(num, decimal_places)
  5.     local mult = 10 ^ (decimal_places or 0)
  6.     return math.floor(num * mult + 0.5) / mult
  7. end
  8. print(string.format("Rounded: %.2f", round(num, 2)))  -- 输出: Rounded: 0.30
复制代码

总结

Lua的string.format函数是一个强大而灵活的工具,可以帮助开发者以各种方式格式化输出数据。从基本的数字和字符串格式化,到复杂的表格对齐、数据可视化和本地化输出,string.format都能胜任。

通过掌握各种格式说明符和修饰符,开发者可以精确控制输出的格式,满足各种应用场景的需求。同时,了解高级技巧如动态格式化、条件格式化和安全格式化,可以帮助开发者编写更健壮、更灵活的代码。

在实际应用中,还需要考虑性能因素,避免不必要的字符串操作,合理使用缓存和预编译技术。同时,处理常见问题如nil值格式化、Unicode字符和浮点数精度等,也是编写高质量代码的重要方面。

通过本文的介绍和示例,希望开发者能够全面掌握Lua中的格式化输出技术,在实际编程中更加得心应手,创造出更加优雅、高效的代码。
「七転び八起き(ななころびやおき)」
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则