<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<atom:link href="http://gentoo-zh.org/extern.php?action=feed&amp;tid=582&amp;type=rss" rel="self" type="application/rss+xml" />
		<title><![CDATA[Gentoo中文社区 / ANSI Common Lisp 第十六章：示例：生成 HTML]]></title>
		<link>http://www.gentoo-zh.org/viewtopic.php?id=582</link>
		<description><![CDATA[ANSI Common Lisp 第十六章：示例：生成 HTML 最近发表的帖子。]]></description>
		<lastBuildDate>Fri, 18 Nov 2022 13:15:20 +0000</lastBuildDate>
		<generator>FluxBB</generator>
		<item>
			<title><![CDATA[ANSI Common Lisp 第十六章：示例：生成 HTML]]></title>
			<link>http://www.gentoo-zh.org/viewtopic.php?pid=622#p622</link>
			<description><![CDATA[<p>本章的目标是完成一个简单的 HTML 生成器 —— 这个程序可以自动生成一系列包含超文本链接的网页。除了介绍特定 Lisp 技术之外，本章还是一个典型的自底向上编程（bottom-up programming）的例子。 我们以一些通用 HTML 实用函数作为开始，继而将这些例程看作是一门编程语言，从而更好地编写这个生成器。</p><p>&#160; &#160; &#160; &#160; 16.1 超文本标记语言 (HTML)<br />&#160; &#160; &#160; &#160; 16.2 HTML 实用函数 (HTML Utilities)<br />&#160; &#160; &#160; &#160; 16.3 迭代式实用函数 (An Iteration Utility)<br />&#160; &#160; &#160; &#160; 16.4 生成页面 (Generating Pages)</p><p>16.1 超文本标记语言 (HTML)</p><p>HTML （HyperText Markup Language，超文本标记语言）用于构建网页，是一种简单、易学的语言。本节就对这种语言作概括性介绍。</p><p>当你使用网页浏览器阅览网页时，浏览器从远程服务器获取 HTML 文件，并将它们显示在你的屏幕上。每个 HTML 文件都包含任意多个标签（tag），这些标签相当于发送给浏览器的指令。</p><p>../_images/Figure-16.1.png</p><p>图 16.1 一个 HTML 文件</p><p>图 16.1 给出了一个简单的 HTML 文件，图 16.2 展示了这个 HTML 文件在浏览器里显示时大概是什么样子。</p><p>../_images/Figure-16.2.png</p><p>图 16.2 一个网页</p><p>注意在尖角括号之间的文本并没有被显示出来，这些用尖角括号包围的文本就是标签。 HTML 的标签分为两种，一种是成双成对地出现的：</p><p>&lt;tag&gt;...&lt;/tag&gt;</p><p>第一个标签标志着某种情景（environment）的开始，而第二个标签标志着这种情景的结束。 这种标签的一个例子是 &lt;h2&gt; ：所有被&lt;h2&gt; 和 &lt;/h2&gt; 包围的文本，都会使用比平常字体尺寸稍大的字体来显示。</p><p>另外一些成双成对出现的标签包括：创建带编号列表的 &lt;ol&gt; 标签（ol 代表 ordered list，有序表），令文本居中的 &lt;center&gt; 标签，以及创建链接的 &lt;a&gt; 标签（a 代表 anchor，锚点）。</p><p>被 &lt;a&gt; 和 &lt;/a&gt; 包围的文本就是超文本（hypertext）。 在大多数浏览器上，超文本都会以一种与众不同的方式被凸显出来 —— 它们通常会带有下划线 —— 并且点击这些文本会让浏览器跳转到另一个页面。 在标签 a 之后的部分，指示了链接被点击时，浏览器应该跳转到的位置。</p><p>一个像</p><p>&lt;a href=&quot;foo.html&quot;&gt;</p><p>这样的标签，就标识了一个指向另一个 HTML 文件的链接，其中这个 HTML 文件和当前网页的文件夹相同。 当点击这个链接时，浏览器就会获取并显示 foo.html 这个文件。</p><p>当然，链接并不一定都要指向相同文件夹下的 HTML 文件，实际上，一个链接可以指向互联网的任何一个文件。</p><p>和成双成对出现的标签相反，另一种标签没有结束标记。 在图 16.1 里有一些这样的标签，包括：创建一个新文本行的 &lt;br&gt; 标签（br 代表 break ，断行），以及在列表情景中，创建一个新列表项的 &lt;li&gt; 标签（li 代表 list item ，列表项）。</p><p>HTML 还有不少其他的标签，但是本章要用到的标签，基本都包含在图 16.1 里了。<br />16.2 HTML 实用函数 (HTML Utilities)</p><p>(defmacro as (tag content)<br />&#160; `(format t &quot;&lt;~(~A~)&gt;~A&lt;/~(~A~)&gt;&quot;<br />&#160; &#160; &#160; &#160; &#160; &#160;&#039;,tag ,content &#039;,tag))</p><p>(defmacro with (tag &amp;rest body)<br />&#160; `(progn<br />&#160; &#160; &#160;(format t &quot;~&amp;&lt;~(~A~)&gt;~%&quot; &#039;,tag)<br />&#160; &#160; &#160;,@body<br />&#160; &#160; &#160;(format t &quot;~&amp;&lt;/~(~A~)&gt;~%&quot; &#039;,tag)))</p><p>(defmacro brs (&amp;optional (n 1))<br />&#160; (fresh-line)<br />&#160; (dotimes (i n)<br />&#160; &#160; (princ &quot;&lt;br&gt;&quot;))<br />&#160; (terpri))</p><p>图 16.3 标签生成例程</p><p>本节会定义一些生成 HTML 的例程。 图 16.3 包含了三个基本的、生成标签的例程。 所有例程都将它们的输出发送到 *standard-output* ；可以通过重新绑定这个变量，将输出重定向到一个文件。</p><p>宏 as 和 with 都用于在标签之间生成表达式。其中 as 接受一个字符串，并将它打印在两个标签之间：</p><p>&gt; (as center &quot;The Missing Lambda&quot;)<br />&lt;center&gt;The Missing Lambda&lt;/center&gt;<br />NIL</p><p>with 则接受一个代码体（body of code），并将它放置在两个标签之间：</p><p>&gt; (with center<br />&#160; &#160; (princ &quot;The Unbalanced Parenthesis&quot;))<br />&lt;center&gt;<br />The Unbalanced Parenthesis<br />&lt;/center&gt;<br />NIL</p><p>两个宏都使用了 ~(...~) 来进行格式化，从而将标签转化为小写字母的标签。 HTML 并不介意标签是大写还是小写，但是在包含许许多多标签的 HTML 文件中，小写字母的标签可读性更好一些。</p><p>除此之外， as 倾向于将所有输出都放在同一行，而 with 则将标签和内容都放在不同的行里。 （使用 ~&amp; 来进行格式化，以确保输出从一个新行中开始。） 以上这些工作都只是为了让 HTML 更具可读性，实际上，标签之外的空白并不影响页面的显示方式。</p><p>图 16.3 中的最后一个例程 brs 用于创建多个文本行。 在很多浏览器中，这个例程都可以用于控制垂直间距。</p><p>(defun html-file (base)<br />&#160; (format nil &quot;~(~A~).html&quot; base))</p><p>(defmacro page (name title &amp;rest body)<br />&#160; (let ((ti (gensym)))<br />&#160; &#160; `(with-open-file (*standard-output*<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; (html-file ,name)<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; :direction :output<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; :if-exists :supersede)<br />&#160; &#160; &#160; &#160;(let ((,ti ,title))<br />&#160; &#160; &#160; &#160; &#160;(as title ,ti)<br />&#160; &#160; &#160; &#160; &#160;(with center<br />&#160; &#160; &#160; &#160; &#160; &#160;(as h2 (string-upcase ,ti)))<br />&#160; &#160; &#160; &#160; &#160;(brs 3)<br />&#160; &#160; &#160; &#160; &#160;,@body))))</p><p>图 16.4 HTML 文件生成例程</p><p>图 16.4 包含用于生成 HTML 文件的例程。 第一个函数根据给定的符号（symbol）返回一个文件名。 在一个实际应用中，这个函数可能会返回指向某个特定文件夹的路径（path）。 目前来说，这个函数只是简单地将 .html 后缀追加到给定符号名的后边。</p><p>宏 page 负责生成整个页面，它的实现和 with-open-file 很相似： body 中的表达式会被求值，求值的结果通过 *standard-output* 所绑定的流，最终被写入到相应的 HTML 文件中。</p><p>6.7 小节展示了如何临时性地绑定一个特殊变量。 在 113 页的例子中，我们在 let 的体内将 *print-base* 绑定为 16 。 这一次，通过将 *standard-output* 和一个指向 HTML 文件的流绑定，只要我们在 page 的函数体内调用 as 或者 princ ，输出就会被传送到 HTML 文件里。</p><p>page 宏的输出先在顶部打印 title ，接着求值 body 中的表达式，打印 body 部分的输出。</p><p>如果我们调用</p><p>(page &#039;paren &quot;The Unbalanced Parenthesis&quot;<br />&#160; (princ &quot;Something in his expression told her...&quot;))</p><p>这会产生一个名为 paren.html 的文件（文件名由 html-file 函数生成），文件中的内容为：</p><p>&lt;title&gt;The Unbalanced Parenthesis&lt;/title&gt;<br />&lt;center&gt;<br />&lt;h2&gt;THE UNBALANCED PARENTHESIS&lt;/h2&gt;<br />&lt;/center&gt;<br />&lt;br&gt;&lt;br&gt;&lt;br&gt;<br />Something in his expression told her...</p><p>除了 title 标签以外，以上输出的所有 HTML 标签在前面已经见到过了。 被 &lt;title&gt; 标签包围的文本并不显示在网页之内，它们会显示在浏览器窗口，用作页面的标题。</p><p>(defmacro with-link (dest &amp;rest body)<br />&#160; `(progn<br />&#160; &#160; &#160;(format t &quot;&lt;a href=\&quot;~A\&quot;&gt;&quot; (html-file ,dest))<br />&#160; &#160; &#160;,@body<br />&#160; &#160; &#160;(princ &quot;&lt;/a&gt;&quot;)))</p><p>(defun link-item (dest text)<br />&#160; (princ &quot;&lt;li&gt;&quot;)<br />&#160; (with-link dest<br />&#160; &#160; (princ text)))</p><p>(defun button (dest text)<br />&#160; (princ &quot;[ &quot;)<br />&#160; (with-link dest<br />&#160; &#160; (princ text))<br />&#160; (format t &quot; ]~%&quot;))</p><p>图 16.5 生成链接的例程</p><p>图片 16.5 给出了用于生成链接的例程。 with-link 和 with 很相似：它根据给定的地址 dest ，创建一个指向 HTML 文件的链接。 而链接内部的文本，则通过求值 body 参数中的代码段得出：</p><p>&gt; (with-link &#039;capture<br />&#160; &#160; (princ &quot;The Captured Variable&quot;))<br />&lt;a href=&quot;capture.html&quot;&gt;The Captured Variable&lt;/a&gt;<br />&quot;&lt;/a&gt;&quot;</p><p>with-link 也被用在 link-item 当中，这个函数接受一个字符串，并创建一个带链接的列表项：</p><p>&gt; (link-item &#039;bq &quot;Backquote!&quot;)<br />&lt;li&gt;&lt;a href=&quot;bq.html&quot;&gt;Backquote!&lt;/a&gt;<br />&quot;&lt;/a&gt;&quot;</p><p>最后， button 也使用了 with-link ，从而创建一个被方括号包围的链接：</p><p>&gt; (button &#039;help &quot;Help&quot;)<br />[ &lt;a href=&quot;help.html&quot;&gt;Help&lt;/a&gt; ]<br />NIL</p><p>16.3 迭代式实用函数 (An Iteration Utility)</p><p>在这一节，我们先暂停一下编写 HTML 生成器的工作，转到编写迭代式例程的工作上来。</p><p>你可能会问，怎样才能知道，什么时候应该编写主程序，什么时候又应该编写子例程？</p><p>实际上，这个问题，没有答案。</p><p>通常情况下，你总是先开始写一个程序，然后发现需要写一个新的例程，于是你转而去编写新例程，完成它，接着再回过头去编写原来的程序。 时间关系，要在这里演示这个开始-完成-又再开始的过程是不太可能的，这里只展示这个迭代式例程的最终形态，需要注意的是，这个程序的编写并不如想象中的那么简单。 程序通常需要经历多次重写，才会变得简单。</p><p>(defun map3 (fn lst)<br />&#160; (labels ((rec (curr prev next left)<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160;(funcall fn curr prev next)<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160;(when left<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;(rec (car left)<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; curr<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; (cadr left)<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; (cdr left)))))<br />&#160; &#160; (when lst<br />&#160; &#160; &#160; (rec (car lst) nil (cadr lst) (cdr lst)))))</p><p>图 16.6 对树进行迭代</p><p>图 16.6 里定义的新例程是 mapc 的一个变种。它接受一个函数和一个列表作为参数，对于传入列表中的每个元素，它都会用三个参数来调用传入函数，分别是元素本身，前一个元素，以及后一个元素。（当没有前一个元素或者后一个元素时，使用 nil 代替。）</p><p>&gt; (map3 #&#039;(lambda (&amp;rest args) (princ args))<br />&#160; &#160; &#160; &#160; &#039;(a b c d))<br />(A NIL B) (B A C) (C B D) (D C NIL)<br />NIL</p><p>和 mapc 一样， map3 总是返回 nil 作为函数的返回值。需要这类例程的情况非常多。在下一个小节就会看到，这个例程是如何让每个页面都实现“前进一页”和“后退一页”功能的。</p><p>map3 的一个常见功能是，在列表的两个相邻元素之间进行某些处理：</p><p>&gt; (map3 #&#039;(lambda (c p n)<br />&#160; &#160; &#160; &#160; &#160; &#160; (princ c)<br />&#160; &#160; &#160; &#160; &#160; &#160; (if n (princ &quot; | &quot;)))<br />&#160; &#160; &#160; &#160; &#039;(a b c d))<br />A | B | C | D<br />NIL</p><p>程序员经常会遇到上面的这类问题，但只要花些功夫，定义一些例程来处理它们，就能为后续工作节省不少时间。<br />16.4 生成页面 (Generating Pages)</p><p>一本书可以有任意数量的大章，每个大章又有任意数量的小节，而每个小节又有任意数量的分节，整本书的结构呈现出一棵树的形状。</p><p>尽管网页使用的术语和书本不同，但多个网页同样可以被组织成树状。</p><p>本节要构建的是这样一个程序，它生成多个网页，这些网页带有以下结构： 第一页是一个目录，目录中的链接指向各个节点（section）页面。 每个节点包含一些指向项（item）的链接。 而一个项就是一个包含纯文本的页面。</p><p>除了页面本身的链接以外，根据页面在树状结构中的位置，每个页面都会带有前进、后退和向上的链接。 其中，前进和后退链接用于在同级（sibling）页面中进行导航。 举个例子，点击一个项页面中的前进链接时，如果这个项的同一个节点下还有下一个项，那么就跳到这个新项的页面里。 另一方面，向上链接将页面跳转到树形结构的上一层 —— 如果当前页面是项页面，那么返回到节点页面；如果当前页面是节点页面，那么返回到目录页面。 最后，还会有索引页面：这个页面包含一系列链接，按字母顺序排列所有项。</p><p>../_images/Figure-16.7.png</p><p>图 16.7 网站的结构</p><p>图 16.7 展示了生成程序创建的页面所形成的链接结构。</p><p>(defparameter *sections* nil)</p><p>(defstruct item<br />&#160; id title text)</p><p>(defstruct section<br />&#160; id title items)</p><p>(defmacro defitem (id title text)<br />&#160; `(setf ,id<br />&#160; &#160; &#160; &#160; &#160;(make-item :id&#160; &#160; &#160;&#039;,id<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; :title&#160; ,title<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; :text&#160; &#160;,text)))</p><p>(defmacro defsection (id title &amp;rest items)<br />&#160; `(setf ,id<br />&#160; &#160; &#160; &#160; &#160;(make-section :id&#160; &#160; &#039;,id<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;:title ,title<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;:items (list ,@items))))</p><p>(defun defsite (&amp;rest sections)<br />&#160; (setf *sections* sections))</p><p>图 16.8 定义一个网站</p><p>图 16.8 包含定义页面所需的数据结构。程序需要处理两类对象：项和节点。这两类对象的结构很相似，不过节点包含的是项的列表，而项包含的是文本块。</p><p>节点和项两类对象都带有 id 域。 标识符（id）被用作符号（symbol），并达到以下两个目的：在 defitem 和 defsection 的定义中， 标识符会被设置到被创建的项或者节点当中，作为我们引用它们的一种手段；另一方面，标识符还会作为相应文件的前缀名（base name），比如说，如果项的标识符为 foo ，那么项就会被写到 foo.html 文件当中。</p><p>节点和项也同时带有 title 域。这个域的值应该为字符串，并且被用作相应页面的标题。</p><p>在节点里，项的排列顺序由传给 defsection 的参数决定。 与此类似，在目录里，节点的排列顺序由传给 defsite 的参数决定。</p><p>(defconstant contents &quot;contents&quot;)<br />(defconstant index&#160; &#160; &quot;index&quot;)</p><p>(defun gen-contents (&amp;optional (sections *sections*))<br />&#160; (page contents contents<br />&#160; &#160; (with ol<br />&#160; &#160; &#160; (dolist (s sections)<br />&#160; &#160; &#160; &#160; (link-item (section-id s) (section-title s))<br />&#160; &#160; &#160; &#160; (brs 2))<br />&#160; &#160; &#160; (link-item index (string-capitalize index)))))</p><p>(defun gen-index (&amp;optional (sections *sections*))<br />&#160; (page index index<br />&#160; &#160; (with ol<br />&#160; &#160; &#160; (dolist (i (all-items sections))<br />&#160; &#160; &#160; &#160; (link-item (item-id i) (item-title i))<br />&#160; &#160; &#160; &#160; (brs 2)))))</p><p>(defun all-items (sections)<br />&#160; (let ((is nil))<br />&#160; &#160; (dolist (s sections)<br />&#160; &#160; &#160; (dolist (i (section-items s))<br />&#160; &#160; &#160; &#160; (setf is (merge &#039;list (list i) is #&#039;title&lt;))))<br />&#160; &#160; is))</p><p>(defun title&lt; (x y)<br />&#160; (string-lessp (item-title x) (item-title y)))</p><p>图 16.9 生成索引和目录</p><p>图 16.9 包含的函数用于生成索引和目录。 常量 contents 和 index 都是字符串，它们分别用作 contents 页面的标题和 index 页面的标题；另一方面，如果有其他页面包含了目录和索引这两个页面，那么这两个常量也会作为这些页面文件的前缀名。</p><p>函数 gen-contents 和 gen-index 非常相似。 它们都打开一个 HTML 文件，生成标题和链接列表。 不同的地方是，索引页面的项必须是有序的。 有序列表通过 all-items 函数生成，它遍历各个项并将它加入到保存已知项的列表当中，并使用 title&lt; 函数作为排序函数。 注意，因为 title&lt; 函数对大小写敏感，所以在对比标题前，输入必须先经过 string-lessp 处理，从而忽略大小写区别。</p><p>实际程序中的对比操作通常更复杂一些。举个例子，它们需要忽略无意义的句首词汇，比如 &quot;a&quot; 和 &quot;the&quot; 。</p><p>(defun gen-site ()<br />&#160; (map3 #&#039;gen-section *sections*)<br />&#160; (gen-contents)<br />&#160; (gen-index))</p><p>(defun gen-section (sect &lt;sect sect&gt;)<br />&#160; (page (section-id sect) (section-title sect)<br />&#160; &#160; (with ol<br />&#160; &#160; &#160; (map3 #&#039;(lambda (item &lt;item item&gt;)<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; (link-item (item-id item)<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;(item-title item))<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; (brs 2)<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; (gen-item sect item &lt;item item&gt;))<br />&#160; &#160; &#160; &#160; &#160; &#160; (section-items sect)))<br />&#160; &#160; (brs 3)<br />&#160; &#160; (gen-move-buttons (if &lt;sect (section-id &lt;sect))<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; contents<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; (if sect&gt; (section-id sect&gt;)))))</p><p>(defun gen-item (sect item &lt;item item&gt;)<br />&#160; (page (item-id item) (item-title item)<br />&#160; &#160; (princ (item-text item))<br />&#160; &#160; (brs 3)<br />&#160; &#160; (gen-move-buttons (if &lt;item (item-id &lt;item))<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; (section-id sect)<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; (if item&gt; (item-id item&gt;)))))</p><p>(defun gen-move-buttons (back up forward)<br />&#160; (if back (button back &quot;Back&quot;))<br />&#160; (if up (button up &quot;Up&quot;))<br />&#160; (if forward (button forward &quot;Forward&quot;)))</p><p>图 16.10 生成网站、节点和项</p><p>图 16.10 包含其余的代码： gen-site 生成整个页面集合，并调用相应的函数，生成节点和项。</p><p>所有页面的集合包括目录、索引、各个节点以及各个项的页面。 目录和索引的生成由图 16.9 中的代码完成。 节点和项由分别由生成节点页面的 gen-section 和生成项页面的 gen-item 完成。</p><p>这两个函数的开头和结尾非常相似。 它们都接受一个对象、对象的左兄弟、对象的右兄弟作为参数；它们都从对象的 title 域中提取标题内容；它们都以调用 gen-move-buttons 作为结束，其中 gen-move-buttons 创建指向左兄弟的后退按钮、指向右兄弟的前进按钮和指向双亲（parent）对象的向上按钮。 它们的不同在于函数体的中间部分： gen-section 创建有序列表，列表中的链接指向节点包含的项，而 gen-item 创建的项则链接到相应的文本页面。</p><p>项所包含的内容完全由用户决定。 比如说，将 HTML 标签作为内容也是完全没问题的。 项的文本当然也可以由其他程序来生成。</p><p>图 16.11 演示了如何手工地定义一个微型网页。 在这个例子中，列出的项都是 Fortune 饼干公司新推出的产品。</p><p>(defitem des &quot;Fortune Cookies: Dessert or Fraud?&quot; &quot;...&quot;)</p><p>(defitem case &quot;The Case for Pessimism&quot; &quot;...&quot;)</p><p>(defsection position &quot;Position Papers&quot; des case)</p><p>(defitem luck &quot;Distribution of Bad Luck&quot; &quot;...&quot;)</p><p>(defitem haz &quot;Health Hazards of Optimism&quot; &quot;...&quot;)</p><p>(defsection abstract &quot;Research Abstracts&quot; luck haz)</p><p>(defsite position abstract)</p>]]></description>
			<author><![CDATA[dummy@example.com (batsom)]]></author>
			<pubDate>Fri, 18 Nov 2022 13:15:20 +0000</pubDate>
			<guid>http://www.gentoo-zh.org/viewtopic.php?pid=622#p622</guid>
		</item>
	</channel>
</rss>
