【问题标题】:Modifying a character in a string in Lua在 Lua 中修改字符串中的字符
【发布时间】:2011-07-12 02:25:16
【问题描述】:

有什么办法可以替换 Lua 中字符串中位置 N 的字符。

这是我目前想出的:

function replace_char(pos, str, r)
    return str:sub(pos, pos - 1) .. r .. str:sub(pos + 1, str:len())
end

str = replace_char(2, "aaaaaa", "X")
print(str)

我也不能使用 gsub,因为它会替换每个捕获,而不仅仅是位置 N 的捕获。

【问题讨论】:

    标签: string lua gsub lua-patterns


    【解决方案1】:

    Lua 中的字符串是不可变的。这意味着,任何替换字符串中文本的解决方案最终都必须构造一个具有所需内容的新字符串。对于将单个字符替换为其他内容的特定情况,您需要将原始字符串拆分为前缀部分和后缀部分,然后将它们重新连接到新内容周围。

    您的代码的这种变体:

    function replace_char(pos, str, r)
        return str:sub(1, pos-1) .. r .. str:sub(pos+1)
    end
    

    是直接 Lua 的最直接翻译。对于大多数目的来说,它可能已经足够快了。我已经修复了前缀应该是第一个 pos-1 字符的错误,并利用了这样一个事实,即如果 string.sub 的最后一个参数丢失,则假定它是 -1,这相当于字符串。

    但请注意,它会创建一些临时字符串,这些字符串将在字符串存储中徘徊,直到垃圾收集吃掉它们。在任何解决方案中都无法避免前缀和后缀的临时性。但这也必须为第一个 .. 运算符创建一个临时运算符,以供第二个运算符使用。

    两种替代方法中的一种可能会更快。第一个是solution offered by Paŭlo Ebermann,但稍作调整:

    function replace_char2(pos, str, r)
        return ("%s%s%s"):format(str:sub(1,pos-1), r, str:sub(pos+1))
    end
    

    这使用string.format 进行结果的组装,希望它可以猜测最终的缓冲区大小而不需要额外的临时对象。

    但请注意,string.format 可能会在通过其%s 格式传递的任何字符串中的任何\0 字符出现问题。具体来说,由于它是根据标准 C 的 sprintf() 函数实现的,因此期望它在第一次出现 \0 时终止替换字符串是合理的。 (用户 Delusional Logic 在评论中注明。)

    想到的第三种选择是:

    function replace_char3(pos, str, r)
        return table.concat{str:sub(1,pos-1), r, str:sub(pos+1)}
    end
    

    table.concat 有效地将字符串列表连接成最终结果。它有一个可选的第二个参数,它是要在字符串之间插入的文本,默认为"",这符合我们的目的。

    我的猜测是,除非您的字符串很大并且您经常执行此替换,否则您不会看到这些方法之间的任何实际性能差异。但是,我之前曾感到惊讶,因此请分析您的应用程序以验证是否存在瓶颈,并仔细对潜在解决方案进行基准测试。

    【讨论】:

    • 感谢您的深入解释
    • 这是旧的。但是我刚刚解决了我编写的一些代码中的一个小错误。事实证明,replace_char2 方法不插入 null (\0) 字符。
    • @DelusionalLogic 好点。 string.format 完全基于标准 C 的 sprintf() 函数,并且可能存在嵌入 NUL 字节的问题。
    【解决方案2】:

    你应该在你的函数中使用pos而不是文字13,但除此之外它看起来不错。由于 Lua 字符串是不可变的,所以你真的不能做得比这更好。

    也许

     "%s%s%s":format(str:sub(1,pos-1), r, str:sub(pos+1, str:len())
    

    .. 操作符更有效,但我对此表示怀疑——如果它证明是一个瓶颈,请测量它(然后决定在 C 中实现这个替换函数)。

    【讨论】:

    • 是的,.. 运算符是连接字符串最慢的方法,因为每个 .. 都会创建一个新字符串。更快的方法包括string.formattable.concat。除非您使用非常大的字符串或许多连接操作,否则这不会引起任何明显的影响。例如,我有一个脚本使用超过 500MB 的内存来处理小于 1MB 的文件,每行输入使用大约 5 个..,同时对输入进行排序和重构作为输出。将其更改为将字符串存储在表格中,最后table.concat 让它变得如此之快,我什至都懒得测量。
    • @Arrowmaster:您知道在a .. b .. c 中创建了两个(而不是只有一个)新字符串,还是您只是假设?原则上,这可以由编译器/解释器优化,只创建一个新字符串,就像在 Java 中为 + 运算符所做的那样。您的示例是另一种情况,因为您确实必须为每个语句创建新字符串。
    • 这里需要"%s%s%s" 周围的括号。
    • 关于优化:据我所知,标准 Lua 确实尝试将单个表达式中的所有 .. 连接转换为单个 VM 指令(直到某一点)。所以a .. b .. c 不会创建中间字符串。 (但a .. (b .. c) 应该创建一个。)
    • 通常table.concat(以及它需要的表创建)只有在循环中才值得。如果您只有一个表达式,请选择..。 (而且,无论如何,你不应该过早地尝试优化;先以最简洁的方式编写,稍后进行分析和优化)
    【解决方案3】:

    使用 luajit,您可以使用 FFI 库将字符串转换为无符号图表列表:

    local ffi = require 'ffi'
    txt = 'test'
    ptr = ffi.cast('uint8_t*', txt)
    ptr[1] = string.byte('o')
    

    【讨论】:

      猜你喜欢
      • 2014-10-17
      • 2021-11-14
      • 2020-06-05
      • 1970-01-01
      • 2019-03-08
      • 1970-01-01
      • 2016-08-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多