MDX文本修订批量替换

新尝试单独开一贴。
MDX的修订,现在大多还是手工+正则的形式一条条替换,此贴尝试用 patch文本的形式解决批量替换的问题。

以下面这个 MDX 原始文本为例:

down
<span>Get down off the taable.</span>
</>
float
<span>I wasn’t sure if the raft would float.</span>
</>

上面的 taable 需要替换成 table,另外我们加个需求,所有 span 外加上div 标签,这里使用正则。

Patch文本的组织形式和 mdx 文本保持一致,用</>分开需要修的词头,第一行需要匹配的词头, 第二行是需要修的文本,第三行是替换文本, patch文本的格式:

down
the taable
the table
</>
*
(<span>(([\s\S])*?)<\/span>)
<div>$1</div>
</>

上面比较特殊的是,有个星号(*)在第一行,这表示所有词头里替换,第二,三行用正则替换,完成span 外套 div。整个 patch 文本,按顺序执行。这些都可以手工修,问题是很繁琐,文本 patch的形式,会更清楚些,也方便分享,替换结果:

down
<div><span>Get down off the table.</span></div>
</>
float
<div><span>I wasn’t sure if the raft would float.</span></div>
</>

衍生处理,还可以替换词头,拆分词头之类的,实现的Python2脚本:

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
import re

def read_text(path):
    with open(path, "r") as f: 
        text = f.read()
        return text

# run: python patch.py mdx.txt patch.txt
def main(argv):
    if len(sys.argv) != 3:
        sys.ext(2)
    
    mdx = read_text(sys.argv[1])
    items = []
    for item in [x.strip() for x in mdx.split("</>") if x.strip()]:
        i = item.find("\n")
        if i != -1:
            word = item[0: i].strip()
            content = item[i:].strip()
            items.append((word, content))

    rules = []
    for patch in read_text(sys.argv[2]).split("</>"):
        arr = patch.splitlines();
        arr[:] = [x.strip() for x in arr if x.strip()]
        if len(arr) == 3:
            rules.append((arr[0], arr[1], arr[2]))

    for rule in rules:
        match, r1, r2 = rule[0], rule[1], rule[2]
        r2 = r2.replace('$', '\\')

        for i, item in enumerate(items):
            word, content = item;
            if match == '*' or match == word:
                content = re.sub(r1, r2, content)
                items[i] = (word, content)

    with open("mdx_new.txt", "a") as f:
        for item in items:
            word, content = item
            f.write(word.strip())
            f.write("\n")
            f.write(content.strip())
            f.write("\n")
            f.write("</>\n")

if __name__ == "__main__":
   main(sys.argv[1:])

可以下载尝试,patch.zip (2.5 KB),包含 patch.py, mdx.txt, patch.txt 。

python patch.py mdx.txt patch.txt

会生成修订好的 mdx_new.txt

4 Likes

这个想法很不错,不过有三个问题:

  1. 正则不能解决所有的修订;
  2. 对于制作者来说,这样的text patch很方便,但是绝大部分只是使用者,text patch给使用者分享还需要重新打包;
  3. 上面的“语法”很类似于mdx源文件,因此可读性不是很好,另外有一点冗余之处:若down这个词条里面不止taable一个错误,那么就需要很多条patch文本。

对于第二点,我个人比较想实现binary patch,就是对打包之后的mdx或者mdd实现替换,这样就省去了每次更新都要下载好几个GB的麻烦。(不知道能否实现?)

对于1、3两点,不妨考虑类似于yaml的各种,并且可以附带Python脚本。而对于HTML常见的操作,可以定义一些方便的命令,让不懂正则的人可以轻松上手。例如down这个词条有诸多需要替换的,那么可以这样写:

key: down
patches:
  - replace "the taable" "the table"
  - regex "(<span>(([\s\S])*?)<\/span>)"  "<div>$1</div>"
  - script some_very_complex_patch.py arg1 arg2 ...
  - html replace span.def div.def
  - html remove classes .useless
  - html remove elements div.hiden div.ads
  - html insert parent div span

上面的例子分别是:

  1. 替换文本
  2. 利用正则在span外加div
  3. 调用外部脚本进行复杂的替换
  4. 替换class有def的span为div
  5. 删掉没用的class
  6. 删掉没用的元素
  7. 同2,但不用正则

另:实现patch是进行词典版本管理的重要一步。

1 Like

很好的想法。
这个应该是面向脱离小白阶段的朋友了。
请教个问题,如何在mdx编译前的txt里头发现没有正确闭合的标签?很多mdx是存在这样那样的毛病的。
例如某一定义行少了个</span>,替换的结果甚至代码就可能会出现问题。所以,如果事先能检查<span>标签的闭合是最好了。

btw 论坛讨论氛围起来了,好事。

检查标签闭合问题属于应用patch之前就应该做的,目前已经有可以用的实现了:

检查HTML错误:

  1. Online W3C Validator
  2. py w3c

更正错误:
(应该也有很多库,不过可以取巧:使用bs4+lxml解析HTML,然后prettify)

w3c 的Validator貌似只能检查标准的HTML标签,而很多mdx使用了大量的自定义tag名称,然后在css定义是block还是inline。例如本论坛的cod9双解,只有极少数地方用div和span(其实其数据内容非常好,显示效果也不错,高度自定义)。
有一些夸张的mdx,每个全角半角的括号逗号顿号句号都包上特定标签,看了代码都难受(但显示效果毫无问题)。

1 Like

感谢指正!

貌似很多出版社都是这样做的,应该是他们用的软件自动生成的。

可以使用Tidy,不限于HTML标签,自定义标签都是可以的。

2 Likes

再有一点就是更新词典文件不一定要通过补丁文件,而是像安卓应用市场那样建立一个分发平台,词典更改后上传分发平台,用户通过终端程序对比本地词典文件和平台上的版本,通过计算对比后下载新增的那部分

1 Like

之前用过tidy,不过它的可编程性似乎不是特别好,这一点之后想想办法,看能不能集成到Python里面去。另外,tidy是让处理后的HTML满足w3c标准,所以会在mdx源码前面添加<html> <head> <body> 之类的,而词条分隔符 </> 会被视为HTML文本而被转换成 &lt; / &gt; 各位若要使用,建议对每一个词条单独处理。

1 Like

可以写个后端和本地下载的程序,让hua大部署到网站上去。

尝试了一下对修改的mdx做 binary diff,结果失败了。原因是mdx的打包方式使得任何细微的修改,都可能使新旧文件几乎没有相同的片段,直接做二进制的patch,得到的patch文件和mdx本身几乎一样大。按词条的patch或者源文件文本的patch应该是唯一的解决方案了。

1 Like

给像我这样的小白转个GUI 工具

除非用新格式,否则只能文本 patch,文本 patch 也只限于论坛上,词典作者们之间分享。

这只是个测试脚本,不值得做 gui。

GUI 不适合处理这个问题,词典的使用者通常不需要关心,只要是作者之间交流用。