目錄
內容簡介
園子里面很多博主都會為自己的博文創建目錄,方便大家瀏覽。我很好奇大家是怎么做的,是不是有自動生成目錄的工具可以推薦一下(我知道word可以,但是直接貼word文檔會生成很多多余的html tag)。
前幾天寫前端網頁最佳實踐目錄項實在有點多,手動加起來太麻煩了,我嘗試搜了下沒有找到,於是寫了幾行代碼來完成這工作。拿出來分享給有同樣需要的朋友。
工具代碼
using System; using System.IO; using System.Text; using System.Text.RegularExpressions; namespace HtmlIndexGenerator { class Program { const string HeaderPattern = @"<h(?<level>[1-6])[\s\S]*?>[\s\S]*?</h([1-6])>"; const string TagPattern = @"<[\s\S]*?>"; const string IdPattern = "(id|name)=\"(?<id>[\\s\\S]*?)\""; const int MaxHeaderLimit = 6; const string H1Style = @"font-weight:bold"; const string H2Style = @""; const string H3Style = @""; const string H4Style = @""; const string H5Style = @""; const string H6Style = @"font-size:10px;"; static string[] HeaderStyles = new string[]{ H1Style, H2Style, H3Style, H4Style, H5Style, H6Style }; static void Main(string[] args) { string fileName; int limit; ParseParameter(args, out fileName, out limit); string html = GetHtml(fileName); if (string.IsNullOrEmpty(html)) return; string index = GenerateIndex(html, limit); string outputFile = "index.htm"; File.WriteAllText(outputFile, index, Encoding.UTF8); Console.WriteLine("{0} generated.", outputFile); } /// <summary> /// Prints help document. /// </summary> private static void PrintHelp() { Console.WriteLine("Usage: IndexGen.exe [filename] [-l] level"); Console.WriteLine("-l: header level limit, -l 3 limit the output to <h3>"); Console.WriteLine("Example: IndexGen.exe page.htm"); } /// <summary> /// Parses command line paramters. /// </summary> /// <param name="args">Input parameters</param> /// <param name="fileName">Output parameter for parsed file name. Null if parse failed.</param> /// <param name="limit">Output parameter for header level limit.</param> private static void ParseParameter(string[] args, out string fileName, out int limit) { fileName = null; limit = MaxHeaderLimit; for (int i = 0; i < args.Length; i++) { if (args[i].Equals("-l", StringComparison.InvariantCultureIgnoreCase)) { if (i + 1 >= args.Length || !int.TryParse(args[i + 1], out limit)) { Console.WriteLine("Invalid parameter for -l"); PrintHelp(); return; } } } if (args.Length > 0) { fileName = args[args.Length - 1]; } } /// <summary> /// Reads html content according to specified file name. /// </summary> /// <param name="fileName">File name</param> /// <returns>Html content of the specific file.</returns> private static string GetHtml(string fileName) { string html = null; if (string.IsNullOrEmpty(fileName)) { Console.WriteLine("Specify a file name"); PrintHelp(); return html; } if (!File.Exists(fileName)) { Console.WriteLine("File {0} dose not exist", fileName); PrintHelp(); return html; } // Auto defect file encoding. using (StreamReader reader = new StreamReader(fileName, detectEncodingFromByteOrderMarks: true)) { Encoding encoding = reader.CurrentEncoding; html = File.ReadAllText(fileName, encoding); } return html; } /// <summary> /// Generates the index html. /// </summary> /// <param name="html">Html content of specified file.</param> /// <param name="limit">Header limit</param> /// <returns>Generated index html</returns> private static string GenerateIndex(string html, int limit) { Regex regex = new Regex(HeaderPattern, RegexOptions.IgnoreCase); Regex regexId = new Regex(IdPattern, RegexOptions.IgnoreCase); MatchCollection headerMatches = regex.Matches(html); int previousLevel = 1; StringBuilder indexBuilder = new StringBuilder(); indexBuilder.Append("<div id=\"doc-index\">"); indexBuilder.Append("<ul>"); foreach (Match headerMatch in headerMatches) { int currentLevel = int.Parse(headerMatch.Groups["level"].Value); string header = Regex.Replace(headerMatch.Value, TagPattern, string.Empty); Match idMatch = regexId.Match(headerMatch.Value); string id = idMatch.Success ? idMatch.Groups["id"].Value : null; string link = string.IsNullOrEmpty(id) ? header : string.Format("<a href=\"#{0}\">{1}</a>", id, header); if (currentLevel == previousLevel) { indexBuilder.AppendFormat("<li style=\"{1}\">{0}</li>", link, HeaderStyles[currentLevel - 1]); } else if (currentLevel > previousLevel && currentLevel <= limit) { indexBuilder.AppendFormat("<ul><li style=\"{1}\">{0}</li>", link, HeaderStyles[currentLevel - 1]); previousLevel = currentLevel; } else if (currentLevel < previousLevel) { indexBuilder.AppendFormat("</ul><li style=\"{1}\">{0}</li>", link, HeaderStyles[currentLevel - 1]); previousLevel = currentLevel; } } indexBuilder.Append("</ul></div>"); return indexBuilder.ToString(); } } }
使用方法
將程序編譯成執行文件,把博文存成本地文件,注意要存成unicode或utf-8,通過命令行運行。一個名叫index.htm的文件會生成在相同目錄下。
如果你只希望限制生成目錄的級數,可以用 -l 參數指定,-l 3代表只生成<h1> 到<h3>的目錄。
雙擊打開后是這個樣子,
接下來需要做的是將生成的內容復制粘貼到博文你想放目錄的地方。簡單的目錄就生成了,參看本文目錄。
如果你想更改樣式,可以直接修改代碼中對不同的header的樣式定義。
工具改進
這只是個小工具,肯定有很多讓小伙伴們驚呆的不足,
- 首先不應該用正則表達式解析html,具體原因可以看這里,如果真的要分析html,.net推薦使用htmlagilitypack,python推薦使用beautifulsoup,我這里不想再引入外部庫,所以假設我們解析的html都是標准格式。
- 另外我沒寫代碼去生成標題的id屬性,因為很多朋友希望id是有意義的名字而不簡單的header1、lable2之類的,所以id還是需要你自己添加,不然超鏈接出不來。 <h1 id="intro"></h1>
- 也嘗試把這段代碼轉換成powershell腳本省了大家編譯,這里有介紹如何做的方法,可惜插件也有硬傷,有些語法還不支持,比如using, out 參數等。
另外如果大家有好的工具也請推薦下,這里拋磚引玉了。