1.2 宏处理函数

1.2. 宏处理函数 Macro Processing Function

1.2.1 简单示例

#例1.2.1-1: strip-tags的函数模块
function strip_tags(subs, sel)
    for _, i in ipairs(sel) do
        local line = subs[i]
    		-- 使用字符串匹配替换值, 这里涉及到匹配模式(lua正则表达式)的内容, 属于很后期的内容
        line.text = line.text:gsub("{[^}]+}", "")
        subs[i] = line
    end
end

这就是一个宏处理函数, 和普通函数不同的是, 这个函数的两个参数是固定的, 比如上文的main()函数, 第一个返回值一定是所有的字幕数据(uesrdate类型的table), 第二个返回值是当前选择的行表(一维数组).


1.2.2 sel参数

sel参数是一个一维数组, 值为当前选中的行数.

例如, 当前选中了#1行和#2行, 我们输出sel[1]sel[2]的值:

image-20221026233713013

行数(#i) sel[i]
#1 10
#2 11
图1.2-1: sel输出

至于为什么sel[i]输出的值不是从1开始的, 在第二章详细讲解subs参数的时候会提及, 现在只需要了解这是因为ass文件的行数统计是将头部信息(ass文件信息, 样式信息等)计算在内的.


1.2.3 subs参数

subs参数返回的是一个具备当前ass文件参数的table, 第二章将会详细讲这个表具体的输出值.

subs表是一个嵌套表, 它的第一层是一个一维数组, 每一个值对应一行, 但是需要注意的是: subs[1]并不是第一行, 关于这一点这个我们在1.2.2已经阐述过了.
在1.2.2中, subs[10]对应我们看到的#1行, subs[11]对应我们看到的#2行.

在本节中, 我们只需要了解我们可以通过 line = subs[i](i为遍历sel的值) 进入到当前选中的行.


1.2.3.1 subs表操作(使用数值型for循环)

#例1.2.3-1: 清除当前选中行说话人(使用数值型for)
function clean_actor(subs, sel)
  for i=1, #sel do
    -- sel[i] 为行数, 也就是上文(图1.2-1)对应的10, 11...
    -- subs[sel[i]]为当前行
    local line = subs[sel[i]]
    line.actor = ""
    subs[sel[i]] = line
  end
end

分析一下这个函数的结构:

  1. 首先通过数值型for遍历sel表获取当前选中的行数sel[i];
    ( for i=1, #sel do <body> end )

  2. 然后选中当前行subs[i], 并将其赋值给中间变量line;
    ( local line = subs[sel[i]] )

  3. 接着通过变量line.actor对字幕数据进行修改(赋予一个空字符串, 等同于清除说话人);
    ( line.actor = "" )

  4. 最后将修改后的行line重新返回到subs表中;
    ( subs[sel[i]] = line )

其中, 第1, 2, 4步是选中行操作, 而第3步是字幕操作. 第1, 2, 4步是必须的, 否则则无法选择字幕行去进行修改.

在初学者阶段, 可以将第1, 2, 4步直接复制下来, 然后在中间通过特效轴阶段了解的行变量知识进行需要效果的实现就可以.

重要的一点是, 不能直接对subs进行操作, 需要通过一个中间值(也就是line)进行转接:

#例1.2.3-2: 错误示例: 无法正确输出的示例
function clean_actor(subs, sel)
  for i=1, #sel do
    subs[sel[i]].start_time = subs[sel[i]].end_time
  end
end

1.2.3.2 subs表操作(使用泛型for循环)

我们也可以使用泛型for循环进入到line中, 而且这样写会更加清晰易懂, 所以这是更加普遍的

#例1.2.3-3: 将当前选中行的行持续时间输出到说话人栏(使用泛型for循环)
function reactor(subs, sel)
  for _, i in ipairs(sel) do
    local line = subs[i]
    line.actor = line.duration
    subs[i] = line
  end
end

这里使用了ipairs迭代器, 关于ipairs迭代器详见1.2.3.3. 这里讲讲我们使用ipairs迭代器定义的这两个迭代变量_ i:

  • _ : 很明显, 这个下划线就是没有意义的, 它在整个循环中没用被用到, 它是用来占位的. 因为我们不需要这个数组键.
  • i: 这里就是sel表里的值, 也就是我们需要的行号.

1.2.3.3 ipairs迭代器

ipairs迭代器是一个用来遍历数组的迭代器, 它有两个迭代变量keyvalue:

for key, value in ipairs(table) do <body> end

#例1.2.3-4: 使用ipairs迭代器的泛型for示例
-- 定义了一个数组tbl
local tbl = {"a","b","c","d","z"}

for i=1, #tbl do
  print(string.format("key=%d; value=%s", i, tbl[i]))
end
print("-------------------")
for k, v in ipairs(tbl) do
  print(string.format("k=%d; v=%s", k, v))
end

输出:

key=1; value=a
key=2; value=b
key=3; value=c
key=4; value=d
key=5; value=z
-------------------
k=1; v=a
k=2; v=b
k=3; v=c
k=4; v=d
k=5; v=z

很明显, ipairs迭代器的第一个返回变量key是这个数组的键, 也就是数组元素顺序. value则是这个键对应的值, 也就是数组元素.


思考

  1. 写一个脚本, 效果为图1.2-1提到的将sel[i]输出到行说话人.
  2. 尝试一下, 将使用例1.2.3-2的函数注册一个脚本, 它到底能不能输出?
    您能够让它正常输出吗?
  3. 数值型for循环和使用ipairs迭代器的泛型for循环相同吗? 他们之间有什么区别?
    您能够将例1.2.3-1的数值型for重写为泛型for吗, 或者将例1.2.3-3的泛型for重写为数值型for.
  4. 是的, 虽然只有两节, 但如果您在学习特效轴阶段对于karaskel变量有一定的了解, 您其实已经能够书写一个完整的Aegisub脚本对字幕数据进行简单的修改了, 尝试一下根据自己的想法书写一个脚本?
posted @ 2022-10-27 00:55  叁仟月  阅读(71)  评论(0编辑  收藏  举报