这个Lua中的去制表符(detab)函数是如何工作的?

-- 将制表符转换为空格 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' 前的制表符长。 它们为什么不同?

点赞
用户258523
用户258523

回答奖励问题:

制表符会对齐到制表位。制表位是八个字符。第一个制表符从第六列开始,所以需要填充三个空格。第二个制表符从第16列开始,所以只需要一个空格宽度。

循环会在空格变成正数时停止,因为循环已经以“缩进”增量添加空格,直到拥有足够的空格长于匹配文本。当将该数量的空格与匹配文本结合起来时,构造了一个填充到正确制表位的字符串。

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

```markdown

2013-07-31 19:36:08
用户234175
用户234175

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

可以使用数字线更容易地可视化这一点:

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

实质上,该循环试图确定有多少个 "tab_widths" 可以适合在 space 中 "溢出"。在这里,它使用了从 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 个空格替换所有制表符字符。另一方面,在 detab 函数中,替换制表符字符的空格数量取决于匹配捕获的长度。

例如:
匹配长度为 1,则使用 3 个空格替换制表符
匹配长度为 2,则使用 2 个空格替换制表符
匹配长度为 3,则使用 1 个空格替换制表符
匹配长度为 n,则使用 tab_width - (n % tab_width) 个空格替换制表符
依此类推。
2013-07-31 22:06:17