在Lua中修改字符串中的字符

dot*_*nic 6 string lua gsub lua-patterns

有没有办法替换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)
Run Code Online (Sandbox Code Playgroud)

我不能使用gsub,因为它会取代每个捕获,而不仅仅是位置N的捕获.

RBe*_*eig 12

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

代码的这种变化:

function replace_char(pos, str, r)
    return str:sub(1, pos-1) .. r .. str:sub(pos+1)
end
Run Code Online (Sandbox Code Playgroud)

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

但请注意,它会创建一些临时字符串,这些字符串将在字符串存储中挂起,直到垃圾回收占用它们.在任何解决方案中都无法避免前缀和后缀的临时性.但这也必须为第一个..操作员创建一个临时用户.

两种替代方法中的一种可能更快.第一个是PaŭloEbermann提供解决方案,但有一个小调整:

function replace_char2(pos, str, r)
    return ("%s%s%s"):format(str:sub(1,pos-1), r, str:sub(pos+1))
end
Run Code Online (Sandbox Code Playgroud)

这用于string.format组合结果,希望它能猜出最终的缓冲区大小,而不需要额外的临时对象.

但请注意,任何字符串中的string.format任何\0字符都可能会出现问题%s.具体来说,由于它是根据标准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
Run Code Online (Sandbox Code Playgroud)

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

我的猜测是,除非您的字符串很大而且经常进行这种替换,否则您将看不到这些方法之间的任何实际性能差异.但是,我之前一直感到惊讶,因此请对您的应用程序进行分析,以确认存在瓶颈,并仔细评估潜在的解决方案.


Paŭ*_*ann 5

你应该用pos你的函数,而不是字面内13,但除了这一点,看起来不错.因为Lua字符串是不可变的,所以你不能比这更好.

也许

 "%s%s%s":format(str:sub(1,pos-1), r, str:sub(pos+1, str:len())
Run Code Online (Sandbox Code Playgroud)

..运算符更有效,但我对此表示怀疑 - 如果它成为瓶颈,请测量它(然后决定在C中实现这个替换函数).

  • 通常`table.concat`(以及它需要的表创建)仅在循环中是值得的.如果您有一个表达式,请转到`..`.(而且,无论如何,你不应该过早地进行优化;首先以最简洁的方式编写,稍后进行配置和优化) (2认同)