从markdown到nga bbscode的转换程序

从markdown到nga bbscode的转换程序

NGA(艾泽拉斯国家地理)论坛使用一种自定义的名为 bbscode 的代码来排版帖子内容,类似HTML,它也是一种标记语言,对bbscode的详细介绍可见:[NGA常用BBS代码][奥运帖]Project N —— 《从入门到精通:排版的艺术 Ver.3》

这种论坛自定义标记语言可以和markdown、html进行相互转换。例子如下:

1
2
3
4
5
6
7
[h]标题[/h]
[b]加粗文字[/b]
[list]
[*] 列表项
[*] 列表项
[/list]
[quote]引用块[/quote]

在使用NGA论坛的时候遇到了将markdown文本转换为bbscode的需求,所以打算用python写一个简单的转换程序。由于不涉及复杂的GUI,本项目使用tkinter来编写界面。

项目地址:

效果

自定义包

自定义包 bbscode结构非常简单,只含有 __init__.py和一个python文件 converter.py

converter.py中含有 md_to_bbscode这一核心转换方法,参数为markdown文本字符串,返回bbscode字符串。

这个转换函数的原理非常简单,只用到了python的一个正则表达式替换方法 re.sub,它的参数分别为:

  • pattern:用于匹配替换内容的正则表达式

  • repl:用于替换匹配结果的字符串(在其中可以使用\匹配组下标表示匹配组,从1开始),或者用于处理匹配结果的函数

  • string:被处理的字符串

  • count:可选,模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。

  • flags:可选,正则匹配模式

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import re
def replace_quote(matched):
quote = matched.group(1)
quote = re.sub(r"> (.*?)","",quote)
quote = "[quote]\n{}\n[/quote]".format(quote)
return quote
def replace_list(matched):
l = matched.group(1)
l = re.sub(r"[-*] ","[*]",l)
l = "[list]\n{}\n[/list]".format(l)
return l
def replace_italic(matched):
# 到了第53个中文字符的时候NGA论坛就会报错:一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二三/* bbscode i too long */
# 所以需要每50个字符就分一个[i]标签
italic = matched.group(1)
step = 50
italics = [italic[i:i+step] for i in range(0,len(italic),step)]
italic = ''
for item in italics:
italic +="[i]{}[/i]".format(item)

return italic

def md_to_bbscode(md_str:str):
'''
将markdown字符串转换为bbscode字符串
'''
bbscode = md_str
# 链接
bbscode = re.sub(r"[^!]\[(.*?)\]\((.*?)\)",r"[url=\2]\1[/url]",bbscode)
# 图片
bbscode = re.sub(r"\!\[(.*?)\]\((.*?)\)",r"[img]\2[/img]",bbscode)
# 标题,注意设置flag为MULTILINE以改变^的语义
bbscode = re.sub(r"^(#+)\s?(.*)",r"[h]\2[/h]",bbscode,flags=re.MULTILINE)
# 加粗
bbscode = re.sub(r"\*\*(.*?)\*\*",r"[b]\1[/b]",bbscode)
# 斜体
bbscode = re.sub(r"\*(.*?)\*",replace_italic,bbscode)
# 下划线
bbscode = re.sub(r"<u>(.*?)</u>",r"[u]\1[/u]",bbscode)
# 删除线
bbscode = re.sub(r"~~(.*?)~~",r"[del]\1[/del]",bbscode)
# 引用块
bbscode = re.sub(r"((> (.*)\n?)+)",replace_quote,bbscode)
# 列表
bbscode = re.sub(r"(([-|*] (.*)\n?)+)",replace_list,bbscode)

return bbscode

无序列表的替换

从代码可以看出,大部分markdown代码都可以直接转换为bbscode,但对于列表、引用块来说,它们对应的bbscode除了项目符号之外,还需要在两侧加上对应的标签,这就无法直接替换,需要编写自定义repl函数了。

以无序列表的转换为例,markdown的无序列表代码如下:

1
2
3
4
5
- 列表项1
- 列表项2

* 列表项a
* 列表项b

以上两种方式都是无序列表。需要将它们转换为:

1
2
3
4
5
6
7
8
9
[list]
[*] 列表项1
[*] 列表项2
[/list]

[list]
[*] 列表项a
[*] 列表项b
[/list]

首先需要用正则表达式匹配到每一个列表项,匹配单个列表项: [-|*] (.*)\n?

如果想要匹配多行列表项,就需要使用循环匹配符号 +,至少匹配一项。即:([-|*] (.*)\n?)+

但是此时取匹配组1是取不到所有列表项的,在外面再套一层括号,这时的匹配组1就是我们要的整个无序列表了。

在替换函数 replace_list中,参数为正则表达式的匹配对象,将项目符号替换并在前后添加 [list]标签即可。

1
2
3
4
5
def replace_list(matched):
l = matched.group(1)
l = re.sub(r"[-*] ","[*]",l)
l = "[list]\n{}\n[/list]".format(l)
return l

多行引用转bbscode引用块的原理相同,不赘述。

界面

需要编写的界面只有一个多行输入框加上按钮,直接放代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import tkinter as tk
from bbscode import converter

root = tk.Tk()

root.title("bbscode转换器")
# 输入框
inputText = tk.Text(root,height=20,width=100)
inputText.pack()

# 转换按钮
def md_to_bbscode():
md_str = inputText.get('1.0',tk.END)
bbscode = converter.md_to_bbscode(md_str)
inputText.delete('1.0', tk.END)
inputText.insert('1.0',bbscode)

convertBtn = tk.Button(root,text="markdown转换为bbscode",command=md_to_bbscode)
convertBtn.pack()


root.mainloop()

从markdown到nga bbscode的转换程序

https://yxchangingself.xyz/posts/markdown-bbscode-converter/

作者

憧憬少

发布于

2021-07-09

更新于

2021-07-09

许可协议