【问题标题】:How would I implement a rfind in Lua with the arguments?我将如何使用参数在 Lua 中实现 rfind?
【发布时间】:2021-01-12 05:41:48
【问题描述】:

例如,我想在lua中做这样的事情:

s = "Hey\n There And Yea\n"

print(s.rfind("\n", 0, 5))

我已经尝试在 lua 中使用 string.find 函数:

local s = "Hey\n There And Yea\n"

local _, p = s:find(".*\n", -5)

print(p)

但是这些并没有产生相同的结果。我做错了什么,我该如何解决这个问题以使其与 rfind 相同?

【问题讨论】:

  • 为以后的读者澄清一下:Python 代码的输出是“3”(左起第 4 个字母)。 rfind 找到 HIGHEST 或 RIGHTMOST 索引,但 OP 提供了 0-5 范围,因此它返回该范围内最右边的。 Lua 代码的输出是 p = 19,p 是子字符串的结束索引(_ 是开始)。我还不知道这个问题的答案。我会尝试想出一些东西,或者至少指出调用 find 方法的错误。

标签: python string lua


【解决方案1】:

Lua 有一个鲜为人知的函数string.reverse,它可以反转字符串的所有字符。虽然这很少需要,但该函数通常可用于在字符串中进行反向搜索。 所以要实现rfind,你要在reverse原字符串中搜索reverse模式,最后进行一些算术运算得到原字符串的偏移量。 这是模仿 Python rfind 的代码:

function rfind(subject, tofind, startIdx, endIdx)
    startIdx = startIdx or 0
    endIdx = endIdx or #subject
    subject = subject:sub(startIdx+1, endIdx):reverse()
    tofind = tofind:reverse()
    local idx = subject:find(tofind)
    return idx and #subject - #tofind - idx + startIdx + 1 or -1
end

print(rfind("Hello World", "H")) --> 0
print(rfind("Hello World", "l")) --> 9
print(rfind("foo foo foo", "foo")) --> 8
print(rfind("Hello World", "Toto")) --> -1
print(rfind("Hello World", "l", 1, 4)) --> 3

请注意,此版本的 rfind 使用 Python 索引约定,从 0 开始,如果未找到字符串,则返回 -1。在 Lua 中,拥有基于 1 的索引并在没有匹配项时返回 nil 会更加一致。修改将是微不足道的。

【讨论】:

  • 我想知道更多关于如何使用其他参数来实现它,而不仅仅是询问字符串。
  • @JackMurrow 没问题,只需在反转之前使用string.sub 选择感兴趣的子字符串。并在最终索引计算中考虑起始位置。请参阅我的更新答案。
【解决方案2】:

我编写的模式仅适用于单字符子字符串,例如作为测试用例的提问者。跳到下一个粗体标题以查看该答案,或继续阅读以了解他们在尝试中做错的一些事情的解释。跳到最后的粗体标题,了解多字符子字符串的通用、低效解决方案

我尝试使用 lua mystring:find 重新创建 python mystring.rfind 的输出,它仅适用于单字符子字符串。稍后我将向您展示一个适用于所有情况的函数,但它是一个非常糟糕的循环。

作为回顾(以解决您做错的事情),让我们谈谈mystringvar:find("pattern", index)string.find(mystringvar, "pattern", index) 的糖。这将返回 start, stop 索引。

可选的索引设置开始,而不是结束,但负索引将从“右减索引”倒数到字符串结尾(-1 的索引将只计算最后一个字符,-2 将计算最后一个 2 )。这不是我们想要的行为。

不要尝试使用索引来创建子字符串,而应该像这样创建子字符串:
mystringvar:sub(start, end) 将提取并返回从头到尾的子字符串(1 个索引,包括结尾)。所以要重新创建 Python 的 0-5(0 索引,排他端),请使用 1-5。

现在请注意,这些方法可以链接到string:sub(x, y):find(""),但为了便于阅读,我将其分解。事不宜迟,我向您介绍:
答案

local s = "Hey\n There And Yea\n"
local substr = s:sub(1,5)
local start, fin = substr:find("\n[^\n]-$")
print(start, ",", fin)

我有几个半度量解决方案,但为了确保我所写的内容适用于多个子字符串实例(1-5 子字符串仅包含 1),我使用子字符串和整个字符串进行了测试。观察:
使用 sub(1, 5) 输出:4 , 5
输出 sub(1, 19) (全长):19 , 19

这些都正确报告了最右边子字符串的开头,但请注意“fin”索引位于句子的末尾,我稍后会解释。我希望这没问题,因为 rfind 无论如何只返回起始索引,所以这应该是一个合适的替换。

让我们重新阅读代码,看看它是如何工作的:
子我已经解释过了
string.find 中不再需要索引
好的,这是什么模式"\n[^\n]-$"
$ - 锚定到句尾
[^x] - 匹配“非 x”
- - 尽可能少的匹配(甚至 0)前一个字符或集合(在本例中为[^\n])。这意味着如果一个字符串以您的子字符串结尾,它仍然可以工作)
它以 \n 开头,所以总而言之,它的意思是:“给我找一个换行符,但后面没有其他换行符,直到句子的结尾。”这意味着即使您的子字符串仅包含 1 个 \n 实例,如果您要在具有多个子字符串的字符串上使用此函数,您仍将获得最高索引,就像 rfind 所做的那样。

请注意,string.find 不符合模式组 (()),因此将 \n 包装在一个组中是徒劳的。因此,我无法阻止结束锚定 $fin 变量扩展到句子的末尾。

我希望这对你有用。

对任意长度的子字符串执行此操作的函数
这个我就不解释了。

function string.rfind(str, substr, plain) --plain is included for you to pass to find if you wish to ignore patterns
  assert(substr ~= "") --An empty substring would cause an endless loop. Bad!
  local plain = plain or false --default plain to false if not included
  local index = 0
  --[[
    Watch closely... we continually shift the starting point after each found index until nothing is left. 
        At that point, we find the difference between the original string's length and the new string's length, to see how many characters we cut out. 
  ]]--
  while true do
    local new_start, _ = string.find(str, substr, index, plain) --index will continually push up the string to after whenever the last index was.
    if new_start == nil then --no match is found
            if index == 0 then return nil end   --if no match is found and the index was never changed, return nil (there was no match)
      return #str - #str:sub(index)  --if no match is found and we have some index, do math.
    end
    --print("new start", new_start)
    index = new_start + 1 --ok, there was some kind of match. set our index to whatever that was, and add 1 so that we don't get stuck in a loop of rematching the start of our substring.
  end
end

如果您想查看我为this 提供的整个“测试套件”...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-27
    • 1970-01-01
    • 2017-03-15
    • 1970-01-01
    • 1970-01-01
    • 2020-08-30
    • 2015-06-15
    相关资源
    最近更新 更多