寻找一种基于 Lua 的解决方案来将字符串拆分成两个或更多组件

这是我在这个网站上的第一篇帖子,请多多包涵。

考虑以下相对典型的字符串:

fld u.a. ldfjal \verb*u.a.* dlf \lstinline$u.a.$ u.a. dfla \url{u.a.}rrr

一些背景:\verb*....*\lstline$...$是 LaTeX 宏,其参数不是由匹配的花括号分隔的,而是由常见字符分隔的:在\verb的情况下为*,在\lstinline的情况下为$。一个重要的点是分隔符字符可以是任何可打印的 ASCII 字符,除了{}; 不应该假设在所有(或即使是任何)情况下将使用*$作为分隔符。另外,\url{...}是一个 LaTeX 宏,其参数由花括号分隔。应该假定完整字符串包含 utf8 编码字符; 为简单起见,让我们假设它们是纯 ASCII 字符。

我想创造一个(希望是相当高效的...)基于 Lua 的方法来将完整字符串拆分成两组子字符串:(a)由 LaTeX 宏及其关联参数组成的部分和(b)其他部分。最终目标是将“其他部分”提供给一个 string.gsub 函数调用。

转向前面的示例,如何将字符串分离

fld u.a. ldfjal \verb*u.a.* dlf \lstinline$u.a.$ u.a. dfla \url{u.a.}rrr

成为“Y”(在 verbatim 类似的宏内)和“N”(不在 verbatim 类似的宏内)组件,即,

NNNNNNNNNNNNNNNNYYYYYYYYYYYNNNNNYYYYYYYYYYYYYYYYNNNNNNNNNNNYYYYYYYYYYNNN

哦,每个字符串保证有“N”组件,但可能没有“Y”组件。该字符串可以原则上从“N”或“Y”组件开始和结束。

我一直在尝试提出一种使用 Lua 的字符串库函数的解决方案,但根本没有进展。 :-(

点赞
用户2858170
用户2858170

让自己熟悉 Lua 的字符串模式。

例如,"[^}{]" 将匹配除了 "}""{" 以外的所有字符。

捕获:

"{([^{}]*)}" 这将捕获花括号所包裹的任意数量的非 "{""}" 字符。

你所要做的就是把所有这些拼在一起。

https://www.lua.org/pil/20.2.html

2017-08-14 19:33:18
用户107090
用户107090

尝试一下:

s=[[
fld u.a. ldfjal \verb*u.a.* dlf \lstinline$u.a.$ u.a. dfla \url{u.a.}rrr
]]


-- 使用 gmatch 进行匹配,获取每个 primitive 类型、分隔符和包含内容
for a,b,c in s:gmatch("(\\verb(.)(.-)%2)") do
    print(a,b,c)
end

你需要为每个你感兴趣的原始码类型循环一次,但至少分隔符部分可以自动处理。

2017-08-14 21:25:35
用户6834680
用户6834680

假设:

  • 宏名仅由字母和@组成
  • 分隔符只能是数字或标点符号字符,除了@ \

代码:

- 指定每个宏的参数数量,
- 对于支持匹配大括号{}的宏,请使用负数
local all_macros = {
   verb = 1,
   url = -1,
   lstinline = -1,
   ["@Some@Macros"] = -2,
   makeatletter = 0
}

- 列出所有分隔符(仅标点符号和数字)
local all_delimiters = [[!"#$%&'*+,-./:;<=>?^_`|~()[]{}0123456789]]

- 指定用于处理字符串的N部分的函数
local function convert(N_substring)
   return N_substring:upper()
end

- 现在进行处理
local s = [[
fld u.a. ldfjal \verb{u.a.{ dlf \lstinline{u.a.} u.a. dfla
\url{u.a.}rrr \@Some@Macros~u.a.~{u.a.}{u.a.}qq\verb|\lstinline+nested use+qqq|q
]]
s = s:gsub("\\([%a@]+)",
   function(macro_name)
      if all_macros[macro_name] then
         return
            "\1\\"..macro_name
            ..(all_macros[macro_name] < 0 and "\2" or "\3")
            :rep(math.abs(all_macros[macro_name]) + 1)
      end
   end
)
repeat
   local old_length = #s
   repeat
      local old_length = #s
      s = s:gsub("\2(\2+)(%b{})", "%2%1")
   until old_length == #s
   s = s:gsub("[\2\3]([\2\3]+)((["..all_delimiters:gsub("%p", "%%%0").."])(.-)%3)", "%2%1")
until old_length == #s
s = ("\2"..s.."\1"):gsub("[\2\3]+([^\2\3]-)\1", convert):gsub("[\1\2\3]", "")

- 打印结果
print(s)

输出:

FLD U.A. LDFJAL \verb{u.a.{ DLF \lstinline{u.a.} U.A. DFLA
\url{u.a.}RRR \@Some@Macros~u.a.~{u.a.}{U.A.}QQ\verb|\lstinline+nested use+qqq|Q
2017-08-15 08:00:11