Lua中是否有一个字符串替换函数replace(),它比gsub()更快?

4
我看到了一份Lua字符串函数列表,其中包括用于全局搜索和替换的.gsub()函数:http://www.gammon.com.au/scripts/doc.php?general=lua_string 所有的Lua字符串函数:
static const luaL_Reg strlib[] = {
  {"byte", str_byte},
  {"char", str_char},
  {"dump", str_dump},
  {"find", str_find},
  {"format", str_format},
  {"gfind", gfind_nodef},
  {"gmatch", gmatch},
  {"gsub", str_gsub},
  {"len", str_len},
  {"lower", str_lower},
  {"match", str_match},
  {"rep", str_rep},
  {"reverse", str_reverse},
  {"sub", str_sub},
  {"upper", str_upper},
  {NULL, NULL}
};

为什么没有一个简单、快速、非正则表达式的字符串替换函数呢?.gsub() 如此高效,以至于没有任何好处吗? 我发现这篇文章是2006年写的,但似乎并没有包括在内:http://lua-users.org/wiki/StringReplace

在gsub中使用表格可能比使用函数更快。 - lhf
3
为什么没有一个简单、快速的文字替换函数(非正则表达式)?它替换什么为什么? - Nicol Bolas
1
请举出您所考虑的替换示例。 - lhf
1
你是否在使用 gsub 时遇到了性能问题?当 gsub 已经可以相对高效地完成纯文本搜索和替换时,还有什么其他用途呢?即使是 Penlight 对 replace 的解释也只是几个对 gsub 的调用。不过,你可以很容易地将找到的代码片段打包成一个模块。 - Oka
2
@MindaugasBernatavičius 可能是因为 gsub 能够完全实现这个 replace 函数的功能,而 Lua 的设计目标之一就是拥有一个小巧、通常不复杂的标准库。没有必要像这样重复。再次强调,Lua 是一种可扩展的语言——如果 需要它,非常容易添加这个功能。但大多数人并不需要它。 - Oka
显示剩余4条评论
1个回答

3
这可能是因为gsub能够完全实现replace函数的功能,而Lua的设计目标之一是构建一个小型、普遍简单的标准库。没有必要将这样的冗余内置到语言中。

以Ruby编程语言为例,其标准库提供了String#gsubString#replace两种方法。出于这种决策,Ruby的体积要大得多。

然而,Lua以其易于扩展的特点自豪。你提供的链接展示了如何在编译整个Lua时将该函数集成到标准库中。你也可以将其组合成模块。

快速拼凑所需部分的结果如下(请注意,我们需要来自lstrlib.clmemfind函数):

#include <lua.h>
#include <lauxlib.h>
#include <string.h>

static const char *lmemfind
(const char *s1, size_t l1, const char *s2, size_t l2) {
    if (l2 == 0)
        return s1;  /* empty strings are everywhere */
    else if (l2 > l1)
        return NULL;  /* avoids a negative 'l1' */

    const char *init;  /* to search for a '*s2' inside 's1' */
    l2--;  /* 1st char will be checked by 'memchr' */
    l1 = l1-l2;  /* 's2' cannot be found after that */

    while (l1 > 0 && (init = (const char *) memchr(s1, *s2, l1)) != NULL) {
        init++;   /* 1st char is already checked */

        if (memcmp(init, s2+1, l2) == 0)
            return init-1;
        else {  /* correct 'l1' and 's1' to try again */
            l1 -= init-s1;
            s1 = init;
        }
    }

    return NULL;  /* not found */
}

static int str_replace(lua_State *L) {
    size_t l1, l2, l3;
    const char *src = luaL_checklstring(L, 1, &l1);
    const char *p = luaL_checklstring(L, 2, &l2);
    const char *p2 = luaL_checklstring(L, 3, &l3);
    const char *s2;
    int n = 0;
    int init = 0;

    luaL_Buffer b;
    luaL_buffinit(L, &b);

    while (1) {
        s2 = lmemfind(src+init, l1-init, p, l2);
        if (s2) {
            luaL_addlstring(&b, src+init, s2-(src+init));
            luaL_addlstring(&b, p2, l3);
            init = init + (s2-(src+init)) + l2;
            n++;
        } else {
            luaL_addlstring(&b, src+init, l1-init);
            break;
        }
    }

    luaL_pushresult(&b);
    lua_pushnumber(L, (lua_Number) n);  /* number of substitutions */
    return 2;
}

int luaopen_strrep (lua_State *L) {
    lua_pushcfunction(L, str_replace);
    return 1;
}

我们可以使用适当的链接(cc -sharedcc -bundle等)将其编译成共享对象,并像其他模块一样使用 require 将其加载到 Lua 中。
local replace = require 'strrep'

print(replace('hello world', 'hello', 'yellow')) -- yellow world, 1.0

这个答案是以上评论的正式重构。

这里涉及到IT技术相关内容,请您慎重考虑。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接