UBB (Universal Bulletin Board) 碼是論壇常用的輕量標記語言,以方括號標籤包裹內容(如 [b]粗體[/b]),最終轉換為 HTML 顯示。雖然現代富文本編輯器(如 FreeTextBox、CKEditor)已主流,但 UBB 解析器在舊系統維護、輕量評論系統、安全過濾場景仍有價值。
using System;
using System.Text;
using System.Text.RegularExpressions;
namespace TextProcessing {
///
/// UBB 碼轉 HTML 處理器
///
public static class UbbParser {
public static string ToHtml(string input) {
if (string.IsNullOrEmpty(input)) return string.Empty;
var result = input;
// 1. 空白字元處理
result = result.Replace(" ", " ");
// 2. HTML 編碼防 XSS
result = result.Replace("<", "<").Replace(">", ">");
// 3. 定義標籤轉換規則:Pattern → Replacement
var rules = new (string Pattern, string Replacement)[] {
// 基本樣式
(@"(\[b\])([\s\S]*?)(\[/b\])", "<b>$2</b>"),
(@"(\[i\])([\s\S]*?)(\[/i\])", "<i>$2</i>"),
(@"(\[u\])([\s\S]*?)(\[/u\])", "<u>$2</u>"),
// 字型屬性
(@"(\[size=([1-7])\])([\s\S]*?)(\[/size\])", "<font size=\"$2\">$3</font>"),
(@"(\[color=([^\]]+)\])([\s\S]*?)(\[/color\])", "<font color=\"$2\">$3</font>"),
(@"(\[font=([^\]]+)\])([\s\S]*?)(\[/font\])", "<font face=\"$2\">$3</font>"),
// 超連結
(@"(\[url\])([\s\S]*?)(\[/url\])", "<a href=\"$2\" target=\"_blank\">$2</a>"),
(@"(\[url=([^\]]+)\])([\s\S]*?)(\[/url\])", "<a href=\"$2\" target=\"_blank\">$3</a>"),
// Email
(@"(\[email\])([\s\S]*?)(\[/email\])", "<a href=\"mailto:$2\" target=\"_blank\">$2</a>"),
(@"(\[email=([^\]]+)\])([\s\S]*?)(\[/email\])", "<a href=\"mailto:$2\" target=\"_blank\">$3</a>"),
// 上下標
(@"(\[sup\])([\s\S]*?)(\[/sup\])", "<sup>$2</sup>"),
(@"(\[sub\])([\s\S]*?)(\[/sub\])", "<sub>$2</sub>"),
// 對齊
(@"(\[align=([^\]]+)\])([\s\S]*?)(\[/align\])", "<p align=\"$2\">$3</p>"),
// 標題
(@"(\[h=([1-6])\])([\s\S]*?)(\[/h\])", "<h$2>$3</h$2>"),
// 圖片(特定系統整合)
(@"\[picture\](\d+?)\[/picture\]",
"<a href=\"ShowImage.aspx?Type=ALL&Action=forumImage&ImageID=$1\" target=\"_blank\">" +
"<img border=\"0\" title=\"點擊查看大圖\" src=\"ShowImage.aspx?Action=forumImage&ImageID=$1\" /></a>"),
// 段落
(@"((\r\n)*\[p\])([\s\S]*?)((\r\n)*\[/p\])", "<p class=\"pstyle\">$3</p>"),
};
// 套用所有簡單標籤規則
foreach (var (pattern, replacement) in rules) {
result = Regex.Replace(result, pattern, replacement, RegexOptions.IgnoreCase);
}
// 4. 列表處理(需特殊邏輯處理巢狀 * 項目)
result = ProcessLists(result);
// 5. 換行處理
result = Regex.Replace(result, @"(\r\n)(( |\s)*)(?<text>\S)", "<br> ${text}");
result = result.Replace("\r\n", "<br>");
return result;
}
private static string ProcessLists(string input) {
// 簡化版:處理 [list]...[*]item\r\n...[/list]
var pattern = @"(\[list(=(A|a|I|i| ))?\])([\s\S]*?)(\[/list\])";
return Regex.Replace(input, pattern, m => {
var listType = m.Groups[3].Success ? m.Groups[3].Value : "disc";
var content = m.Groups[4].Value;
// 處理 [*] 項目
content = Regex.Replace(content, @"\[\*\]([\s\S]*?)(\r\n|$)", "<li>$1</li>");
return $"<ul type=\"{listType}\">{content}</ul>";
}, RegexOptions.IgnoreCase | RegexOptions.Singleline);
}
}
}
string ubb = @"
[b]重要公告[/b]
[color=red]系統將於今晚維護[/color]
[url=https://example.com]官方網站[/url]
[list]
[*]項目一
[*]項目二
[/list]
";
string html = UbbParser.ToHtml(ubb);
// 輸出對應 HTML
[\s\S]*? 確保跨行匹配且不跨標籤RegexOptions.IgnoreCase 兼容使用者輸入習慣[b][i]text[/i][/b] 等巢狀結構Markdig、MarkdownSharp)HtmlSanitizer 套件)UBB 解析器雖屬經典技術,但其規則驅動、正則轉換、安全優先的設計思維,對理解標記語言處理、建構自訂 DSL 解析器極具參考價值。