小说下载阅读器_章节保存为XML并显示
这篇文章主要是讲程序中对XML,XSLT,XSD,JS的简单应用。
1.当小说的章节都下载完毕,章节内容经过过滤,去除多余的html标记和其它垃圾信息,变成干净的文本内容。怎么保存这些章节内容呢?
1)解决方法:将所有章节依次写入同一个文本文件,并将小说名作为文件名。
优点:
通用性最好,不管是PC平台还是手机、平板电脑都能直接阅读,且人可直接阅读。
缺点:
如果想分离每章或者将文本文件格式转成其它格式,解析相对麻烦。
2)解决方法:将所有章节,分章节依次写入同一个xml文件,并将小说名作为文件名。
优点:
由于xml文件是结构性的,通用性也很强,可轻易转成其它任意格式,且章节之间本身也是分隔的
缺点:
在于由于多了额外的信息,xml会比文本文件稍大一些。
3)解决方法:将将所有章节,分章节写入数据库。
优点:
数据库的优点,不用多说,查询检索很方便。
缺点:
当数据多了,数据库也会变大。
我采用的是第二种方法,原因如下
1.暂时减少对数据库的依赖(后面的进化版,可能会用数据库存储章节内容)
2.虽然文本文件的通用性是最高的,但我需要在window和linux上看小说,或者是手机上,手机上一般都需要安装阅读器,其实很多格式都支持,这个不是问题,关键在于window和linux上阅读,如果只是用普通编辑器来看文本小说,可以看,但是视觉效果真是很烂,为了统一windows和linux上的视觉效果,干脆就用xml作为中间格式,最终都在浏览器里面观看。
2.小说要保存的要素:小说名称,章节名,章节内容,卷名以及其它附加信息
1)XML格式例子如下:
1 <?xml version="1.0" encoding="GB2312"?> 2 <Article Name="盘龙" CreateTime="2012-01-01 00:00:00"> 3 <Part Index="第一章 小镇的早晨" Volume="盘龙之戒"> 4 章节内容....................... 5 </Part> 6 <Part Index="第二章 龙血战士家族(上)" Volume="盘龙之戒"> 7 ;;段落内容........;;段落内容。;;段落内容。;;段落内容。;;段落内容。;;段落内容。;;段落内容。 8 </Part> 9 <Part Index="第三章 龙血战士家族(下)" Volume="盘龙之戒"> 10 <img src="http://img.58xs.com/gif/195/195623/18276026/13028469.gif" />//如果有图片就是这样的 11 </Part> 12 </Article>
C#如何将字符串保存为xml,请参考System.Xml命名空间下的类,或者用linq to xml都行,更或者直接string拼凑都可以,代码就不写了。
2)用xsd 来定义xml的schema:
1 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> 2 <xs:element name="Img"> 3 <xs:complexType> 4 <xs:attribute name="Src" type="xs:anyURI" use="required"/> 5 </xs:complexType> 6 </xs:element> 7 <xs:element name="Part"> 8 <xs:complexType mixed="true"> 9 <xs:sequence> 10 <xs:element ref="Img" minOccurs="0" maxOccurs="unbounded"/> 11 </xs:sequence> 12 <xs:attribute name="Index" type="xs:string" use="required"/> 13 <xs:attribute name="Volume" type="xs:string" use="optional"/> 14 </xs:complexType> 15 </xs:element> 16 <xs:element name="Article"> 17 <xs:complexType> 18 <xs:sequence> 19 <xs:element ref="Part" maxOccurs="unbounded"/> 20 </xs:sequence> 21 <xs:attribute name="Name" type="xs:string" use="required"/> 22 <xs:attribute name="CreateTime" type="xs:dateTime" use="optional"/> 23 </xs:complexType> 24 </xs:element> 25 </xs:schema>
以下是基本的解释
Article.Name是小说名称 ,CreateTime是xml文件最后保存时间
Part.Index是章节名称,Volume是卷名
再就是Part的内容要么是文字,要么是Img图片
3.如何显示保存到文件中章节内容呢?这里用xslt将xml文件转化成html进行显示
先显示效果图:
上述两幅图可以看出,显示部分主要分为三大块:
1)第一块是第一幅图的最上的章节索引链接,可以在这块跳转到你需要观看的章节去
2)第二块是第一幅图的下半部分,就是小说的正文,每一章节对应一个区块,每一章节都可以向前一章和后一章跳转。
3)第三块是第二幅图的右半边,红色字的部分,可以对页面背景色和字体,以及页面自动滚动速度进行设置。
当你写好xslt文件的时候,可以在xml文件第一行后面加上一句话区引用这个xslt文件,就可以让xml在浏览器中按照xslt规定显示
<?xml-stylesheet type="text/xsl" href="Article.xslt"?>
下面给出xslt的源码:
1 <?xml version="1.0" encoding="GB2312"?> 2 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 3 <!-- 4 <xsl:import href="function.xsl"/> 5 --> 6 <xsl:variable name="SplitToken">;;</xsl:variable> 7 <xsl:template match="/"> 8 <html> 9 <head> 10 <title> 11 <xsl:value-of select="/Article/@Name"/> 12 </title> 13 </head> 14 <link type="text/css" href="ui_a.css" rel="stylesheet"/> 15 <body> 16 <xsl:choose> 17 <xsl:when test="count(/Article/*)>=1"> 18 <a class="title" name="0"> 19 <xsl:value-of select="/Article/@Name"/> 20 </a> 21 <br/> 22 <br/> 23 <xsl:if test="count(//Part)>1"> 24 <xsl:call-template name="PartIndex"/> 25 </xsl:if> 26 <br/> 27 <xsl:call-template name="PartList"/> 28 </xsl:when> 29 <xsl:otherwise> 30 <div class="NoPart"> 31 <xsl:text>无内容,请添加章节</xsl:text> 32 </div> 33 </xsl:otherwise> 34 </xsl:choose> 35 <!--this js must be here,if not be here,invalid in IE6\Opear\Chome--> 36 37 <div class="scroll"> 38 <input name="scrollspeed" id="scrollspeed" title="Scroll Speed" onchange="javascript:setSpeed();" value="5"/> 39 <input name="bgcolor" id="bgcolor" value="#FEF7DA" tilte="Background Color"/> 40 <input name="fcolor" id="fcolor" value="#000000" tilte="Font Color"/> 41 <input name="Fsize" id="Fsize" value="16pt" tilte="Font Size"/> 42 <input type="button" title="Save" value="Save" onclick="javascript:saveSet();"/> 43 </div> 44 45 <script src="Novel.js"/> 46 47 </body> 48 </html> 49 </xsl:template> 50 <xsl:template name="PartIndex"> 51 <div class="roundconner"> 52 <xsl:call-template name="Roundconner"> 53 <xsl:with-param name="IsTop" select="''"/> 54 </xsl:call-template> 55 <div name="Content" id="Content" class="Content"> 56 <table width="100%"> 57 <tbody style="text-align : '.'"> 58 <xsl:for-each select="/Article/Part"> 59 <xsl:choose> 60 <xsl:when test="position() mod 3=2"> 61 <xsl:call-template name="IndexGroup"> 62 <xsl:with-param name="th1"> 63 <xsl:value-of select="concat(position()-1,$SplitToken)"/> 64 <xsl:value-of select="./preceding-sibling::*[position()=1]/@Index"/> 65 66 </xsl:with-param> 67 <xsl:with-param name="th2"> 68 <xsl:value-of select="concat(concat(position(),$SplitToken),@Index)"/> 69 </xsl:with-param> 70 <xsl:with-param name="th3"> 71 <xsl:value-of select="concat(position()+1,$SplitToken)"/> 72 <xsl:value-of select="./following-sibling::*[position()=1]/@Index"/> 73 </xsl:with-param> 74 </xsl:call-template> 75 </xsl:when> 76 <xsl:when test="(position() mod 3=1) and (position()=last())"> 77 <xsl:call-template name="IndexGroup"> 78 <xsl:with-param name="th1"> 79 <xsl:value-of select="concat(concat(position(),$SplitToken),@Index)"/> 80 </xsl:with-param> 81 </xsl:call-template> 82 </xsl:when> 83 </xsl:choose> 84 </xsl:for-each> 85 </tbody> 86 </table> 87 </div> 88 <xsl:call-template name="Roundconner"> 89 <xsl:with-param name="IsTop" select="'1'"/> 90 </xsl:call-template> 91 </div> 92 </xsl:template> 93 <xsl:template name="IndexGroup"> 94 <xsl:param name="th1" select="''"/> 95 <xsl:param name="th2" select="''"/> 96 <xsl:param name="th3" select="''"/> 97 <tr> 98 <th> 99 <xsl:call-template name="AddLink"> 100 <xsl:with-param name="str" select="$th1"/> 101 </xsl:call-template> 102 <xsl:if test="./preceding::*[position()=1]/@Volume"> 103 <font class="Volume"> 104 <xsl:call-template name="AddLink"/>  [<xsl:value-of select="./preceding::*[position()=1]/@Volume"/>] 105 </font> 106 </xsl:if> 107 </th> 108 <th> 109 <xsl:call-template name="AddLink"> 110 <xsl:with-param name="str" select="$th2"/> 111 </xsl:call-template> 112 <xsl:if test="./@Volume"> 113 <font class="Volume"> 114 <xsl:call-template name="AddLink"/>  [<xsl:value-of select="./@Volume"/>] 115 </font> 116 </xsl:if> 117 </th> 118 <th> 119 <xsl:call-template name="AddLink"> 120 <xsl:with-param name="str" select="$th3"/> 121 </xsl:call-template> 122 <xsl:if test="./following::*[position()=1]/@Volume"> 123 <font class="Volume"> 124 <xsl:call-template name="AddLink"/>  [<xsl:value-of select="./following::*[position()=1]/@Volume"/>] 125 </font> 126 </xsl:if> 127 </th> 128 </tr> 129 </xsl:template> 130 <xsl:template name="AddLink"> 131 <xsl:param name="str" select="''"/> 132 <a href="#{substring-before($str,$SplitToken)}"> 133 <xsl:value-of select="substring-after($str,$SplitToken)"/> 134 </a> 135 </xsl:template> 136 <xsl:template name="PartTitle"> 137 <xsl:param name="name" select="''"/> 138 <xsl:param name="pos">0</xsl:param> 139 <div> 140 <a class="PartIndex" name="{$pos}"> 141 <xsl:value-of select="$name"/> 142 </a> 143 <xsl:if test="(count(/Article/*)>1)"> 144 <font class="PartStep"> 145 <a href="#{-1+$pos}">上章</a> 146 <a href="#{1+$pos}">下章</a> 147 <a href="#0">返回</a> 148 </font> 149 </xsl:if> 150 </div> 151 </xsl:template> 152 <xsl:template name="PartList"> 153 <div class="roundconner"> 154 <xsl:call-template name="Roundconner"> 155 <xsl:with-param name="IsTop" select="''"/> 156 </xsl:call-template> 157 <xsl:for-each select="//Part"> 158 <xsl:call-template name="PartTitle"> 159 <xsl:with-param name="name" select="@Index"/> 160 <xsl:with-param name="pos" select="position()"/> 161 </xsl:call-template> 162 <div name="Content" id="Content" class="Content"> 163 <!-- add img--> 164 <xsl:if test="(count(./img)>0)"> 165 <div class="imgDiv" style="clear:both"> 166 <xsl:for-each select="./img"> 167 <xsl:element name="img"> 168 <xsl:attribute name="src"><xsl:value-of select="@src"/></xsl:attribute> 169 </xsl:element> 170 </xsl:for-each> 171 </div> 172 </xsl:if> 173 <xsl:call-template name="ChangeLine"> 174 <xsl:with-param name="str"> 175 <xsl:choose> 176 <xsl:when test="contains(., $SplitToken)"> 177 <xsl:value-of select="substring-after(., $SplitToken)"/> 178 </xsl:when> 179 <xsl:otherwise> 180 <xsl:value-of select="."/> 181 </xsl:otherwise> 182 </xsl:choose> 183 </xsl:with-param> 184 <xsl:with-param name="pat"> 185 <xsl:value-of select="$SplitToken"/> 186 </xsl:with-param> 187 </xsl:call-template> 188 </div> 189 <xsl:if test="(count(/Article/*)>1) and ( position()!=last())"> 190 <div class="split"/> 191 </xsl:if> 192 </xsl:for-each> 193 <xsl:call-template name="Roundconner"> 194 <xsl:with-param name="IsTop" select="bot"/> 195 </xsl:call-template> 196 </div> 197 </xsl:template> 198 <xsl:template name="ChangeLine"> 199 <xsl:param name="str"/> 200 <xsl:param name="pat"/> 201    202 <xsl:choose> 203 <xsl:when test="contains($str, $pat)"> 204 <xsl:if test="not(starts-with($str, $pat))"> 205 <xsl:value-of select="substring-before($str, $pat)"/> 206 <br/> 207 </xsl:if> 208 <xsl:call-template name="ChangeLine"> 209 <xsl:with-param name="str" select="substring-after($str, $pat)"/> 210 <xsl:with-param name="pat" select="$pat"/> 211 </xsl:call-template> 212 </xsl:when> 213 <xsl:otherwise> 214 <xsl:value-of select="$str"/> 215 </xsl:otherwise> 216 </xsl:choose> 217 </xsl:template> 218 <xsl:template name="Roundconner"> 219 <xsl:param name="IsTop" select="''"/> 220 <xsl:choose> 221 <xsl:when test="$IsTop=''"> 222 <b class="rtop"> 223 <b class="r1"/> 224 <b class="r2"/> 225 <b class="r3"/> 226 <b class="r4"/> 227 </b> 228 </xsl:when> 229 <xsl:otherwise> 230 <b class="rbottom"> 231 <b class="r4"/> 232 <b class="r3"/> 233 <b class="r2"/> 234 <b class="r1"/> 235 </b> 236 </xsl:otherwise> 237 </xsl:choose> 238 </xsl:template> 239 </xsl:stylesheet>
其中xslt中用到了一点javascript来实现对cookie的读取和自动滚屏功能,这里也给出对应的novel.js代码
1 var curPos, timer,speed=5; 2 //////////// 3 var scrollspeed=document.getElementById("scrollspeed"); 4 var bgcolor=document.getElementById("bgcolor"); 5 var fcolor=document.getElementById("fcolor"); 6 var Fsize=document.getElementById("Fsize"); 7 8 function setCookies(cookieName,cookieValue, expirehours) 9 { 10 var today = new Date(); 11 var expire = new Date(); 12 expire.setTime(today.getTime() + 3600000 * 356 * 24); 13 document.cookie = cookieName+'='+escape(cookieValue)+ ';expires='+expire.toGMTString(); 14 } 15 function ReadCookies(cookieName,obj) 16 { 17 var theCookie=''+document.cookie;//alert(document.cookie); 18 var ind=theCookie.indexOf(cookieName); 19 if (ind==-1 || cookieName=='') return obj; 20 var ind1=theCookie.indexOf(';',ind); 21 if (ind1==-1) ind1=theCookie.length;//alert(ind+"\n"+ind1); 22 return unescape(theCookie.substring(ind+cookieName.length+1,ind1)); 23 } 24 function saveSet() 25 { 26 setCookies("scrollspeed",scrollspeed.value); 27 28 setCookies("bgcolor",bgcolor.value); 29 ChangeBgcolor(bgcolor.value); 30 31 setCookies("fcolor",fcolor.value); 32 ChangeFcolor(fcolor.value); 33 34 setCookies("Fsize",Fsize.value); 35 ChangeFsize(Fsize.value); 36 37 } 38 39 function Change(obj,index) 40 { 41 var oCB=document.getElementsByName('Content'); 42 for(i=0;i<oCB.length;i++) 43 { 44 oCB[i].style.backgroundColor=obj; 45 } 46 } 47 function ChangeBgcolor(color) 48 { 49 var oCB=document.getElementsByName('Content'); 50 for(i=0;i<oCB.length;i++) 51 { 52 oCB[i].style.backgroundColor=color; 53 } 54 } 55 function ChangeFcolor(color) 56 { 57 var oCB=document.getElementsByName('Content'); 58 59 for(i=0;i<oCB.length;i++) 60 { 61 62 oCB[i].style.color=color; 63 } 64 } 65 function ChangeFsize(fontSize) 66 { 67 var oCB=document.getElementsByName('Content'); 68 69 for(i=0;i<oCB.length;i++) 70 { 71 oCB[i].style.fontSize =fontSize; 72 } 73 } 74 function loadSet() 75 { 76 scrollspeed.value=ReadCookies("scrollspeed",5); 77 setSpeed(); 78 79 bgcolor.value=ReadCookies("bgcolor","#FFFFFF");; 80 ChangeBgcolor(bgcolor.value); 81 82 83 fcolor.value=ReadCookies("fcolor","#000000");; 84 ChangeFcolor(fcolor.value); 85 86 87 Fsize.value=ReadCookies("Fsize","16px");; 88 ChangeFsize(Fsize.value); 89 } 90 91 //document.oncontextmenu = new Function("return false;"); 92 //双击鼠标滚动屏幕的代码 93 //var curPos, timer,speed=5; 94 function initialize(){timer=setInterval ("scrollwindow()",300/speed);} 95 function sc(){clearInterval(timer);} 96 function scrollwindow(){ 97 curPos=document.body.scrollTop?document.body.scrollTop:document.documentElement.scrollTop; 98 window.scroll(0,++curPos); 99 var sTop=document.body.scrollTop?document.body.scrollTop:document.documentElement.scrollTop; 100 if (curPos !=sTop) sc(); 101 } 102 function setSpeed(){ 103 104 speed = parseInt(scrollspeed.value); 105 106 }; 107 document.onmousedown = sc; 108 document.ondblclick = initialize; 109 loadSet();
另外。xslt也使用到了css,这里提供css文件的源码
1 .scroll{width:70px;float:right; 2 position:fixed !important; top/**/:200px; 3 position:absolute; z-index:100; top:expression(offsetParent.scrollTop+200);right:4px;} 4 input{ 5 width:99%; 6 border :1px solid #999999; 7 font-weight:bold; 8 background:transparent ; 9 color:red; 10 height:21px; 11 font-size:10t; 12 } 13 body,b.rtop, b.rbottom,div.split{ 14 background: #4E3F3A; /*url(back.gif);*/ 15 } 16 div.roundconner,b.rtop b, b.rbottom b,div.Content,.NoPart{ 17 /*改变圆角矩形的背景色*/ 18 background:#FEF7DA; 19 } 20 *{ 21 font: 100.0% "宋体","Trebuchet MS", "Verdana", "Arial", "sans-serif"; 22 color: #000000; 23 } 24 body { 25 /*line-height : 180%;*/ 26 text-align:center; 27 } 28 div 29 { 30 /*firefox中,必须加入以下两句才能让div在body中居中显示*/ 31 margin-left:auto; 32 margin-right:auto; 33 } 34 div.Content{ 35 line-height : 180%; 36 font-size:14pt; 37 } 38 div.split{ 39 height:20px; 40 } 41 .title{ 42 color:white; 43 font-size:16pt; 44 } 45 table{ 46 border-collapse:collapse ; 47 } 48 .roundconner { 49 width:90%; 50 text-align:left; 51 52 } 53 b.rtop, b.rbottom { 54 display: block; 55 56 } 57 b.rtop b, b.rbottom b { 58 display: block; 59 height: 1px; 60 overflow: hidden; 61 62 } 63 b.r1 { 64 margin: 0 5px; 65 } 66 b.r2 { 67 margin: 0 3px; 68 } 69 b.r3 { 70 margin: 0 2px; 71 } 72 b.rtop b.r4, b.rbottom b.r4 { 73 margin: 0 1px; 74 height: 2px; 75 } 76 a{ 77 text-decoration : none; 78 } 79 a:hover { 80 color : red; 81 } 82 .imgDiv{ 83 text-align:center; 84 width:98%; 85 } 86 th{ 87 background:transparent ; 88 font-size:11pt; 89 font-weight : normal; 90 text-align : left; 91 padding-left:8px; 92 width:33%; 93 } 94 br{ 95 text-indent: 2em; 96 } 97 .Volume{ 98 font-weight:bold; 99 font-size:9pt; 100 } 101 .PartIndex, .PartStep ,.PartStep a{ 102 103 background-color:#E8E2CC; 104 /*border :1px solid #CDCDDC;*/ 105 font-size:10pt; 106 font-weight:bold; 107 height:21px; 108 vertical-align : center; 109 line-height:21px; 110 } 111 .PartIndex{ 112 text-align : left; 113 float:left; 114 width:86%; 115 } 116 .PartStep{ 117 text-align : right; 118 float:right; 119 width:14%; 120 } 121 .NoPart{ 122 height:200px; 123 vertical-align : center; 124 line-height:200px; 125 font-size:36pt; 126 }
现在我们手头上有了如下文件
1.盘龙.xml 小说文件
2.Article.xslt xml样式表文件
3.Article.xsd xml的结构定义文件,对最终显示无影响
4.novel.js 读取和设置本机cookie,和提供自动滚屏功能
5.UI.css
以上文件放置在同一个目录,双击盘龙.xml ,浏览器中就可以查看到小说内容
其实有了xml格式的章节内容,我们可以轻易的实现各种转化器,将xml格式转成txt,umd等等格式,并且可以以rss的形式发布小说内容。
这个留待以后在实现。