1
-- Converts tabs to spaces
function detab(text)
    local tab_width = 4
    local function rep(match)
        local spaces = -match:len()
        print("match:"..match)
        while spaces<1 do spaces = spaces + tab_width end
        print("Found "..spaces.." spaces")
        return match .. string.rep(" ", spaces)
    end
    text = text:gsub("([^\n]-)\t", rep)
    return text
end


str='   thisisa string'
--thiis is a      string

print("length: "..str:len())
print(detab(str))
print(str:gsub("\t","    "))

我有来自 markdown.lua 的这段代码,它将制表符转换为空格(顾名思义)。
我设法弄清楚的是它从字符串的开头搜索,直到找到一个选项卡并将匹配的子字符串传递给'rep'函数。它会反复执行此操作,直到没有更多匹配项为止。
我的问题是试图弄清楚 rep 函数在做什么,尤其是在 while 循环中。
为什么循环停在1?
为什么会算上?.
出人意料的是,它统计了字符串中的空格数,究竟如何是个谜。
如果您将其输出与上次gsub替换的输出进行比较,您会发现它们是不同的。
Detab 保持字符的对齐,而gsub替换则不保持。为什么呢?
奖金问题。当我在 Scite 中打开空白时,我可以看到 之前的't'制表符比第三个之前的制表符长's'为什么它们不同?

4

2 回答 2

4

从分析rep函数来看,这似乎是它正在做的事情。首先,它获取传入的匹配字符串的长度并将其设为负数(例如,将其乘以 -1)。在 while 循环中,它会不断增加空间,直到变为正数。

使用数字线可能更容易对此进行可视化:

<--|----|-------|----|----|----|----|----|----|----|----|--->
  -n      -spaces             -2   -1    0    1    2    n

本质上,循环试图在“溢出”之前计算有多少“tab_widths”可以放入空格。这里它使用从 0 到 1 的过渡作为截止点。循环之后,spaces会有多少溢出。

事实上,while 循环正在模仿一种数学运算,您可能知道它是模数。换句话说,内部rep函数可以重写为:

local function rep(match)
  local spaces = tab_width - match:len() % tab_width

  return match .. string.rep(" ", spaces)
end

这与外部str:gsub("\t", " ")加选择地用 4 个空格替换所有制表符不同。OTOH,在detab函数中,替换制表符的空格数取决于匹配捕获的长度。

eg.
matching length is 1, replace tab with 3 spaces
matching length is 2, replace tab with 2 spaces
matching length is 3, replace tab with 1 space
matching length is n, replace tab with tab_width - (n % tab_width) spaces
etc.
于 2013-07-31T22:06:17.310 回答
1

回答额外问题:制表符与制表位对齐。一个制表位是八个字符。第一个制表符从第六列开始,因此需要填充三个空格。第二个选项卡从第 16 列开始,因此它只需要一个空格宽。

当空格变为正数时循环停止,因为循环一直在“缩进”增量中添加空格,直到它有足够的空格比匹配的文本更长。然后,当它将该数量的空格与匹配的文本组合时,它构造了一个字符串,该字符串被填充到正确的制表位。

这也是 gsub 不同的原因。gsub 不将制表符视为制表符字符,而是将其视为四个空格。所以第二个制表符不会填充到制表位,而是扩展到四个空格。

于 2013-07-31T19:36:08.020 回答