亚洲?v女人的天堂在线观看,亚洲视频中文字幕在线,亚洲第一二三四区http://m.tkk7.com/scud/category/3187.html山谷里鳥語花香,溪水潺潺zh-cnSat, 06 Nov 2010 15:45:19 GMTSat, 06 Nov 2010 15:45:19 GMT60MAVEN:如何為開發和生產環境建立不同的配置文件 --我的簡潔方案http://m.tkk7.com/scud/archive/2010/10/27/336326.htmlScud(飛云小俠)Scud(飛云小俠)Wed, 27 Oct 2010 14:31:00 GMThttp://m.tkk7.com/scud/archive/2010/10/27/336326.htmlhttp://m.tkk7.com/scud/comments/336326.htmlhttp://m.tkk7.com/scud/archive/2010/10/27/336326.html#Feedback2http://m.tkk7.com/scud/comments/commentRss/336326.htmlhttp://m.tkk7.com/scud/services/trackbacks/336326.html閱讀全文

Scud(飛云小俠) 2010-10-27 22:31 發表評論
]]>
對搜索引擎同義詞支持的實驗, 分析模擬http://m.tkk7.com/scud/archive/2010/08/16/328950.htmlScud(飛云小俠)Scud(飛云小俠)Mon, 16 Aug 2010 01:26:00 GMThttp://m.tkk7.com/scud/archive/2010/08/16/328950.htmlhttp://m.tkk7.com/scud/comments/328950.htmlhttp://m.tkk7.com/scud/archive/2010/08/16/328950.html#Feedback1http://m.tkk7.com/scud/comments/commentRss/328950.htmlhttp://m.tkk7.com/scud/services/trackbacks/328950.html 今天偶爾看到一個同義詞庫, 想到這個有什么用途哪? 肯定是用來判斷2句話, 2篇文章的相似性的.

它對搜索引擎,對論文抄襲鑒定系統肯定有用, 于是去搜索引擎試了試, 結果大失所望失望, 貌似google,bing,baidu對同義詞沒有做處理, 感覺是很簡單的東西, 竟然沒有做相關處理.

而且不僅僅是沒有做同義詞處理, 相關度方面也很差, 真是很奇怪的事情.


我們隨便找一句話: (從google的桌面工具文檔里)

A: "我們十分關注您的安全并為此推出了一項功能",

對應樣本為:

B: "我們非常關注您的安全并為此推出了一項功能"


我們搜索一下, 可以發現 A 可以匹配的很好, 如果改成B, 發現和A匹配的第一項不見了, 按照正常猜想即使改了一個詞 相關度應該還是很高, 不知道為什么會這樣.




替換其中一個同義詞: (發現原來的網頁不在前面了, 翻了幾頁也沒有找到)





我們暫且不考慮 "大勝美國隊" 和 "大敗美國隊"的語義分析, 但是上面的結果肯定不盡人意, 因為你可能需要考慮所有同義詞, 否則可能就錯過你想要的結果.



下面我們做一個簡單的分析和模擬來實現同義詞的相關邏輯, 假設的流程如下:
  •     首先我們要有同義詞對應表(多對多,可以有權重)
  •     收錄網頁時把相應同義詞映射到同一個詞, 當然可以映射多個
  •     在搜索時先預處理用戶輸入的內容
  •     根據匹配算法計算


1. 同義詞表結構如下

一百分 -->滿分, 0.8
十分 --> 滿分, 0.8

十分 --> 非常, 0.95
特別 --> 非常, 0.9
格外 --> 非常, 0.9

關心 --> 關注, 0.95
注意 --> 關注, 0.85

......

所有同義詞映射到同一個詞語, 并賦予一個權重. 當然還有多義詞的問題, 會出現多個映射.

當然同義詞的整理也是個巨大的工作量, 網上也有一些詞庫.


2. 根據同義詞庫的映射

    原內容: 我們十分關注您的安全并為此推出了一項功能
    映射后: 我們 滿分*0.8|非常*0.95|(十分) 關注您的安全并為此推出了一項功能

    "|"表示有多個選擇, "()"表示為原內容.

3. 對輸入內容的分析映射

    用戶輸入: 我們非常關心您的安全并為此推出了一項功能    
    分析映射: 我們非常 關注*0.95|(關心) 您的安全并為此推出了一項功能

4. 匹配查找
   
   匹配查找就是搜索引擎核心的邏輯了, 當然在遇到 "滿分*0.8|非常*0.95|(十分)" 需要增加同義詞判斷邏輯, 根據2個權重可以得出一個同義詞的匹配度, 然后繼續分析即可.



以上只是一個非常簡單的分析模擬, 和實際的搜索引擎邏輯差別非常大, 僅供參考.



Scud(飛云小俠) 2010-08-16 09:26 發表評論
]]>
不重復的排列組合示例http://m.tkk7.com/scud/archive/2010/07/29/327395.htmlScud(飛云小俠)Scud(飛云小俠)Thu, 29 Jul 2010 01:55:00 GMThttp://m.tkk7.com/scud/archive/2010/07/29/327395.htmlhttp://m.tkk7.com/scud/comments/327395.htmlhttp://m.tkk7.com/scud/archive/2010/07/29/327395.html#Feedback0http://m.tkk7.com/scud/comments/commentRss/327395.htmlhttp://m.tkk7.com/scud/services/trackbacks/327395.html
 1 /**
 2  * 各字符不重復的組合, 組合數小于等于最大可能性(否則就重復了).
 3  * 
 4  * @author scud(飛云)
 5  */
 6 public class ShortCombineTest
 7 {
 8     static int count = 0;
 9 
10     public static void main(String[] args)
11     {
12         String s = "123456"//all items content
13         int howmany = 3//how many object
14 
15         char[] c = s.toCharArray();
16         char[] dest = new char[howmany];
17 
18         combine(c, dest, howmany, s.length(), 0);
19 
20         System.out.println("max combine:" + count);
21     }
22 
23     public static void combine(char[] array, char[] dest, int howmany, int maxitem, int index)
24     {
25         //break & end
26         if (index == howmany)
27         {
28             System.out.println(dest);
29             count++;
30             return;
31         }
32 
33         while(array.length>0)
34         {
35             dest[index] = array[0];
36             char[] nextarray = getLeftChar(array, 0);
37             array = nextarray;
38             combine(nextarray, dest, howmany, maxitem, index + 1);
39         }
40     }
41 
42     public static char[] getLeftChar(char[] c, int index)
43     {
44         char[] left = new char[c.length - 1];
45 
46         for (int i = 0, j = 0; i < c.length; i++)
47         {
48             if (i != index)
49             {
50                 left[j] = c[i];
51                 j++;
52             }
53         }
54 
55         return left;
56     }
57 
58 
59 }
60 




Scud(飛云小俠) 2010-07-29 09:55 發表評論
]]>
最近在編寫DBHelper的文檔http://m.tkk7.com/scud/archive/2006/02/20/31634.htmlScud(飛云小俠)Scud(飛云小俠)Mon, 20 Feb 2006 05:54:00 GMThttp://m.tkk7.com/scud/archive/2006/02/20/31634.htmlhttp://m.tkk7.com/scud/comments/31634.htmlhttp://m.tkk7.com/scud/archive/2006/02/20/31634.html#Feedback0http://m.tkk7.com/scud/comments/commentRss/31634.htmlhttp://m.tkk7.com/scud/services/trackbacks/31634.html  閱讀全文

Scud(飛云小俠) 2006-02-20 13:54 發表評論
]]>
讀"Under the Hood of J2EE Clustering" J2EE集群http://m.tkk7.com/scud/archive/2005/09/28/14362.htmlScud(飛云小俠)Scud(飛云小俠)Wed, 28 Sep 2005 15:53:00 GMThttp://m.tkk7.com/scud/archive/2005/09/28/14362.htmlhttp://m.tkk7.com/scud/comments/14362.htmlhttp://m.tkk7.com/scud/archive/2005/09/28/14362.html#Feedback0http://m.tkk7.com/scud/comments/commentRss/14362.htmlhttp://m.tkk7.com/scud/services/trackbacks/14362.html閱讀全文

Scud(飛云小俠) 2005-09-28 23:53 發表評論
]]>
幾個提高代碼質量,檢查代碼規范的工具http://m.tkk7.com/scud/archive/2005/08/29/11422.htmlScud(飛云小俠)Scud(飛云小俠)Mon, 29 Aug 2005 03:49:00 GMThttp://m.tkk7.com/scud/archive/2005/08/29/11422.htmlhttp://m.tkk7.com/scud/comments/11422.htmlhttp://m.tkk7.com/scud/archive/2005/08/29/11422.html#Feedback0http://m.tkk7.com/scud/comments/commentRss/11422.htmlhttp://m.tkk7.com/scud/services/trackbacks/11422.html
1.FindBugs:查錯
 目前版本0.9.1,有for eclipse的插件. 網址是http://findbugs.sourceforge.net.
 
 工作原理:檢查程序生成的class的工具.
 
 界面:獨立運行的提供圖形界面,很友好,有bug報告.
 
 可用性:大多數提示有用,值得改
 
 插件:
  可以設置基本和檢查的錯誤類別.
  插件保存設置有問題,我是關閉項目后臺修改了配置文件,在裝入才成功改了配置的.
  bug臨時解決: 使用獨立的findbugs設置規則,然后到C:\Documents and Settings\XXX\下找.Findbugs_prefs,然后改名覆蓋eclipse project下的.fbprefs (先關閉你的project)
 
 配置沒有查找功能,不過縮寫能讓我們很快找到某個規則
 
2.PMD:主要是查錯
 目前版本3.2,有for eclipse以及其他ide的插件.網址是http://pmd.sourceforge.net
 工作原理:檢查源碼
 可用性:一部分值得修改,有些過于嚴格
 界面:獨立運行的是命令行界面,命令比較簡單.
 插件:可以配置規則,有一個獨立的窗口顯示提示,分5級提示,很友好
 
 使用:建立自己的規范,然后用于實際使用中.
 
3.CheckStyle:主要查代碼規范
 目前版本4.0 beta 5,有for eclipse的插件.網址是http://checkstyle.sourceforge.net.
 工作原理:檢查源碼,對javadoc,書寫格式等進行檢查.
 規則定義:默認的規則是sun的編碼規范.不過按照sun的規則則過于嚴格,而且每個公司也有自己的規范,和sun的不同,所以需要自定義規范. 

4.JTest 重量級的商業工具
 目前版本7.0.7,有for eclipse的插件.網址是http://www.parasoft.com/
 
 不推薦使用,不過功能強大,可以進行代碼檢查,可以自動生成單元測試和進行單元測試.(不過就是太慢了,而且生成的單元測試沒太大用途)
 
 
 

使用感覺:

 安裝上插件后,對自己的項目進行檢查,發現警告太多了,有點發蒙的感覺.不過把警告看一遍,覺得都很有道理,有些也確實是一些錯誤.
 當然PMD和CheckStyle的規范太嚴格,最后還是配置了一下.
 
 通過改正警告,感覺還是不錯,至少可以說自己的代碼可以通過工具的檢測了.
 
 當然基礎代碼和項目代碼還是不一樣的,基礎代碼往往比較復雜,所以和普通項目代碼的規范應該有所不同.有些規則只能用在普通代碼上,用在基礎類代碼上往往沒法處理.
 
其他

代碼查錯推薦使用Findbugs和PMD,代碼書寫規范推薦使用CheckStyle進行檢查.這樣不僅能查出一些基本的錯誤,也能提高項目的代碼質量.對提高自己的代碼水平也是非常好.

推薦項目組建立統一的規則,代碼復查的時候就使用這些工具,省時省力.

實乃居家旅行,殺人越貨必備之工具也.(因為肯定有人要罵你,呵呵,也是你找"差"的工具)


 
 



Scud(飛云小俠) 2005-08-29 11:49 發表評論
]]>
分析XML中的CDATA類型在RSS中的使用http://m.tkk7.com/scud/archive/2005/08/22/10723.htmlScud(飛云小俠)Scud(飛云小俠)Mon, 22 Aug 2005 10:49:00 GMThttp://m.tkk7.com/scud/archive/2005/08/22/10723.htmlhttp://m.tkk7.com/scud/comments/10723.htmlhttp://m.tkk7.com/scud/archive/2005/08/22/10723.html#Feedback0http://m.tkk7.com/scud/comments/commentRss/10723.htmlhttp://m.tkk7.com/scud/services/trackbacks/10723.html除經特別注明外,本站文章版權歸JScud Develop團隊或其原作者所有.
轉載請注明作者和來源.  scud(飛云小俠)    歡迎訪問 JScud Develop



根據XML中CDATA類型的規范可以知道:"&"和"<"不需要也不能被轉換. ">" 如果出現在"]]>" 的內容而不是表示結束時,必須被轉義為&gt;

但是這樣就存在一個問題,如果我需要輸入"]]>",正確的處理是保存為"]]&gt;",但是如果我想輸入"]]&gt;",那么應該如何保存哪? 我想了很久,除非加空格或者采用特殊的辦法,否則是沒有辦法解決的.

1.如果我們不考慮輸入"]]&gt;"的問題,來考慮一下"]]>"的處理,看看各種XML解析器是如何處理的?

xml解析器的測試包含2個部分:設置cdata類型的數據和讀出cdata類型的數據.

首先我們寫一個測試的例子,計劃使用JDom 1.0和Dom4j來測試一下:

 package com.jscud.test;
 
 public class XmlTestBase
 { 
     public static String xmlpart =
      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+
         "<xml>" +
         "<test>"+
         "<hello><![CDATA[ hello ]]&gt; ]]></hello>" +
         "</test>" +
         "</xml>";       
    
     public static void print(String str)
     {
         System.out.println(str);
     }    
 } 


JDom測試的例子如下:

package com.jscud.test;
 
 import java.io.*;
 import org.jdom.*;
 import org.jdom.input.SAXBuilder;
 import org.jdom.output.*;
 
 //@author scud http://www.jscud.com
 
 public class JDomXmlFileTest extends XmlTestBase
 {
 
     public static void main(String[] args) throws Exception
     {
         readDocument();
         print("===========================");
         createDocument();
     }
    
     public static void readDocument() throws Exception
     {
         Reader reader = new StringReader(xmlpart);
         SAXBuilder builder = new SAXBuilder();
        
         Document doc = builder.build(reader);
 
         Element aRoot = doc.getRootElement();
        
         Element anode = aRoot.getChild("test").getChild("hello");
        
         print(anode.getText());       
     }
 
     public static void createDocument() throws Exception
     {       
         Document doc = new Document();
        
         doc.setRootElement(new Element("root"));
        
         CDATA node = new CDATA("hello alt=]]&gt;");
        
         //throw Exception
         //node.setText("hello]]>");
        
         Element ele = new Element("hello");
        
         ele.setContent(node);
        
         Element root = doc.getRootElement();
        
         root.getChildren().add(ele);
        
         XMLOutputter outputter = new XMLOutputter();
         Format aFormat = Format.getCompactFormat();
         aFormat.setEncoding("GB2312");
        
         String sResult = outputter.outputString(doc.getRootElement().getChildren());
        
        print(sResult);
        
     }
 } 

 編譯并運行上面的代碼結果,我們可以看到JDom無法設置Cdata的值為"]]>",會報異常.從xml字符串讀出cdata的結果也沒有把字串"]]&gt;"翻譯為"]]>".

接著再來測試Dom4J:

package com.xml.test;
 
 import java.io.StringReader;
 
 import org.dom4j.*;
 import org.dom4j.io.SAXReader;
 import org.dom4j.tree.DefaultCDATA;
 
 /**
  * 測試XML的CData數據類型.
  *
  * @author scud http://www.jscud.com
  *
  */
 
 public class Dom4jXmlTest extends XmlTestBase
 {   
 
     public static void main(String[] args) throws Exception
     {
         readDocument();
         print("===========================");
         createDocument();       
     }
    
     public static void createDocument()
     {
         Document document = DocumentHelper.createDocument();
         Element root = document.addElement( "root" );
        
         DefaultCDATA cdata = new DefaultCDATA("sample]]>");
         DefaultCDATA cdata2 = new DefaultCDATA("sample]]&gt;");
        
         Element anode = root.addElement("cdata");
         anode.add(cdata);
        
         print(anode.getText());       
         print(anode.asXML());
        
         Element anode2 = root.addElement("cdata2");
         anode2.add(cdata2);
        
         print(anode2.getText());       
         print(anode2.asXML());
     }
    
     public static void readDocument() throws Exception
     {
         StringReader strreader = new StringReader(xmlpart);
        
         SAXReader reader = new SAXReader();
         Document document = reader.read(strreader);
        
         Node node = document.selectSingleNode( "http://test/hello" );
        
         print(node.getText());
        
         print(node.getStringValue());
     }
    
 } 

 
 我們可以看到Dom4j也是沒有做任何處理,輸入的時候不作任何轉換,原樣輸出,這樣必然導致xml錯誤.讀出的時候也沒有做轉換.

根據上面的測試我們可以得出結論:很多xml解析器沒有正確解析cdata的數據,(jdom和dom4j用的人比較多),不要太相信這些解析器.

 

2.我們再來看看閱讀RSS的RSS閱讀器吧,例如FeedDemon和POTU,我們準備了一個CData類型的description字段,來進行測試.

內容:

<?xml version="1.0" encoding="GB2312" ?>
 <rss version="2.0">
 <channel>
  <title>Some Where</title>
  <link>http://www.jscud.com/</link>
  <description />
 <item>
  <title>Test</title>
  <link>http://www.jscud.com</link>
  <author>scud</author>
  <pubDate>Mon, 22 Aug 2005 10:22:22 GMT</pubDate>
  <description><![CDATA[
  &lt;hr&gt;
  ]]&gt;
  ]]></description>
  </item>
  </channel>
  </rss>


結果
 1.POTU沒有做任何處理
 2.FeedDemon做了處理,不過同時也把其他的&gt; &lt;等等都翻譯了,這就更不對了..
 

本來我是打算在RSS里使用CDATA類型的description字段的,經過幾番試驗和測試,最后決定還是使用普通的description字段了,不在使用CDATA了.

CDATA? 雞肋乎? 呵呵

 



Scud(飛云小俠) 2005-08-22 18:49 發表評論
]]>
使用FreeMarker/Jsp(webwork)生成靜態/動態RSS文件http://m.tkk7.com/scud/archive/2005/08/19/10510.htmlScud(飛云小俠)Scud(飛云小俠)Fri, 19 Aug 2005 06:11:00 GMThttp://m.tkk7.com/scud/archive/2005/08/19/10510.htmlhttp://m.tkk7.com/scud/comments/10510.htmlhttp://m.tkk7.com/scud/archive/2005/08/19/10510.html#Feedback0http://m.tkk7.com/scud/comments/commentRss/10510.htmlhttp://m.tkk7.com/scud/services/trackbacks/10510.htmlscud(飛云小俠)  http://www.jscud.com 轉載請注明作者/來源

關鍵字:rss,freemarker,rss.xml,webwork2

RSS在網絡上大行其道,各種網站都加上RSS支持,我最近也研究了一下,給我的文章也加上了RSS訂閱.

RSS目前用的也有幾個版本,很是混亂,下面以RSS2.0為例來說明.

網絡上有個rsslibj庫,是用來生成rss支持文件的,不過已經好久沒有更新了,它是用xml的方式生成的.本文的例子不用到任何xml解析器,不過當然要知道最后生成的XML文件的格式才行,關于RSS規范,可以瀏覽一下 http://blogs.law.harvard.edu/tech/rss .

在計劃生成RSS文件的時候,順便搜索了一下JIRA和Confluence的程序,發現它們分別是用模板方式和JSP動態頁面來展示的.于是我也想到兩種方式:
1.用FreeMarker生成靜態文件,適用于更新不是很頻繁的內容.
2.用JSP動態展示,適合更新頻率高,種類繁多的內容.

還是以本站的新聞舉例,其中的新聞信息類參考 http://www.jscud.com/srun/news/viewhtml/3_2005_8/76.htm ,此處不在列出.

(一) 先說FreeMarker方式.

根據RSS的規范,得到模板如下:

<?xml version="1.0" encoding="UTF-8" ?>
 <rss version="2.0">
 <channel>
  <title>JScud Develop</title>
  <link>http://www.jscud.com/</link>
  <language>zh-cn</language>
  <description >JScud Develop By Scud</description>
  <webMaster>xxx@21cn.com(scud)</webMaster>
  <lastBuildDate>${rssutil.formatRssDate(now)}</lastBuildDate>
   
   <#list newslist as onenews>
  <item>
   <title>${onenews.title?xml}</title>
   <link>http://www.jscud.com/srun/news/viewhtml/${onenews.htmlFilePath}/${onenews.nid}.htm</link>
   <pubDate>${rssutil.formatRssDate(onenews.addtime)}</pubDate>
   <description><![CDATA[
  ${rssutil.formatRssCData(onenews.showContent)}
   ]]>
   </description>
   </item>
 </#list>
  </channel>
 </rss>

其中的網址和網站名稱可以根據自己的實際情況修改.

我每次取出最新的20條文章來生成RSS,不過內容比較多,生成的RSS文件比較大,看到有的網站的description只是放了文章摘要的內容,這樣文件就小多了.總之是根據自己的需求設計吧.

其中用到的RssUtil函數庫的函數如下(日期的函數參考上一篇文章):

    /**
     * 把]]>替換為]]&gt;
     * @param content 內容
     * @return 格式化后的內容
     */
    public static String formatRssCData(String content)
    {
        String result = StringFunc.replace(content,"\\]\\]>","]]&gt;");
       
        return result;
    }
   
    /**
     * 格式化為xml需要的字符串
     * @param field 內容
     * @return 格式化后的串
     */
    public static String formatString2XML(String field)
    {
        return StringFunc.str2TextXML(field);
    }
   
    public static String getNowDateTime()
    {
        return formatRssDate(DateTime.getNowTimestamp());
    }

利用FreeMarker生成靜態文件的代碼如下:

 private Configuration freemarker_cfg = null;
 
    protected Configuration getFreeMarkerCFG()
    {
        if (null == freemarker_cfg)
        {
            // Initialize the FreeMarker configuration;
            // - Create a configuration instance
            freemarker_cfg = new Configuration();

            freemarker_cfg.setClassForTemplateLoading(this.getClass(), "/htmlskin");

            freemarker_cfg.setDefaultEncoding("GBK");
        }

        return freemarker_cfg;
    }

    public boolean geneFileByFreeMarker(String templateFileName, Map propMap, String filePath,
                    String fileName, String encode)
    {
        try
        {
            Template t = getFreeMarkerCFG().getTemplate(templateFileName);

            File afile = new File(filePath + "/" + fileName);

            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(afile),
                            encode));

            propMap.put("baseurl", PropSet.getStringProp("url.root"));

            t.process(propMap, out);
        }
        catch (TemplateException e)
        {
            LogMan.error("Error while processing FreeMarker template " + templateFileName, e);
            return false;
        }
        catch (IOException e)
        {
            LogMan.error("Error while generate File " + fileName, e);
            return false;
        }

        return true;
    }


新聞系統中調用重新生成RSS文件的代碼如下:

    /**
     * 重新生成RSS文件.
     *
     * @param nid 更新的新聞的id,如果不包含在最新的新聞里,則不更新RSS.nid <1則更新
     *
     * @return 是否成功
     */
    private boolean renewRSS(int nid)
    {
        List newsList = 裝載新聞的代碼

        boolean shouldUpdate = false;
        if (nid > 0)
        {
            for (int i = 0; i < newsList.size(); i++)
            {
                NewsItem aNews = (NewsItem) newsList.get(i);
                if (aNews.getNid() == nid)
                {
                    shouldUpdate = true;
                    break;
                }
            }
        }
        else
        {
            shouldUpdate = true;
        }

        //不更新,則返回
        if (!shouldUpdate)
        {
            return true;
        }

        Map root = new HashMap();
       
        root.put("rssutil",new RSSUtil());

        root.put("newslist", newsList);
       
        root.put("now",DateTime.getNowTimestamp());

        geneFileByFreeMarker("/news/rss.ftl", root, PropSet.getStringProp("rss.rssdir"), PropSet
                        .getStringProp("rss.rssfile"), "UTF-8");

        return true;
    }



在增加或者更新/刪除新聞的地方需要調用這個renewRSS函數.

(二)JSP動態方式

相對靜態方式而言,簡單的多,不過效率上可能就不太好了.

webwork2的Action代碼如下:

        newsList = 裝載新聞代碼
        return SUCCESS; 


視圖Jsp如下:


<%@ page contentType="text/xml; charset=UTF-8"%>
<%@ taglib uri="jscud" prefix="jscud" %>
<%@ taglib uri="webwork" prefix="ww" %>
<ww:bean name="’com.jscud.www.util.RSSUtil’" id="rssUtil" />
<?xml version="1.0" encoding="UTF-8" ?>
 <rss version="2.0">
 <channel>
  <title>JScud Develop</title>
  <link>http://www.jscud.com/</link>
  <language>zh-cn</language>
  <description >JScud Develop By Scud</description>
  <webMaster>xxx@21cn.com(scud)</webMaster>
  <lastBuildDate><ww:property value="#rssUtil.nowDateTime" /></lastBuildDate>
   
   <ww:iterator value="newsList">
  <item>
   <title><ww:property value="#rssUtil.formatString2XML(title)"/></title>
   <link>http://www.jscud.com/srun/news/viewhtml/<ww:property  value="htmlFilePath" />/<ww:property  value="nid" />.htm</link>
   <pubDate><ww:property  value="#rssUtil.formatRssDate(addtime)" /></pubDate>
   <description><![CDATA[
  <ww:property value="#rssUtil.formatRssCData(showContent)"/>
   ]]>
   </description>
   </item>
 </ww:iterator>
  </channel>
 </rss>

jsp的方式簡單多了,上面的jsp里面還演示了ww:bean的使用 :)


上面的類里面引用了很多其他的工具類,這里不一一列出,可以自己實現它們,都是很簡單的類. :)



Scud(飛云小俠) 2005-08-19 14:11 發表評論
]]>
Rss 中日期格式的研究http://m.tkk7.com/scud/archive/2005/08/17/10370.htmlScud(飛云小俠)Scud(飛云小俠)Wed, 17 Aug 2005 10:11:00 GMThttp://m.tkk7.com/scud/archive/2005/08/17/10370.htmlhttp://m.tkk7.com/scud/comments/10370.htmlhttp://m.tkk7.com/scud/archive/2005/08/17/10370.html#Feedback1http://m.tkk7.com/scud/comments/commentRss/10370.htmlhttp://m.tkk7.com/scud/services/trackbacks/10370.htmlscud(飛云小俠) http://www.jscud.com 轉載請注明來源/作者

 

rss中日期格式要求遵守rfc822規范,其中是這么寫的:

     date-time   =  [ day "," ] date time        ; dd mm yy
                                                 ;  hh:mm:ss zzz

     day         =  "Mon"  / "Tue" /  "Wed"  / "Thu"
                 /  "Fri"  / "Sat" /  "Sun"

     date        =  1*2DIGIT month 2DIGIT        ; day month year
                                                 ;  e.g. 20 Jun 82

     month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr"
                 /  "May"  /  "Jun" /  "Jul"  /  "Aug"
                 /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"

     time        =  hour zone                    ; ANSI and Military

     hour        =  2DIGIT ":" 2DIGIT [":" 2DIGIT]
                                                 ; 00:00:00 - 23:59:59

     zone        =  "UT"  / "GMT"                ; Universal Time
                                                 ; North American : UT
                 /  "EST" / "EDT"                ;  Eastern:  - 5/ - 4
                 /  "CST" / "CDT"                ;  Central:  - 6/ - 5
                 /  "MST" / "MDT"                ;  Mountain: - 7/ - 6
                 /  "PST" / "PDT"                ;  Pacific:  - 8/ - 7
                 /  1ALPHA                       ; Military: Z = UT;
                                                 ;  A:-1; (J not used)
                                                 ;  M:-12; N:+1; Y:+12
                 / ( ("+" / "-") 4DIGIT )        ; Local differential
                                                 ;  hours+min. (HHMM)

 



可以看出,前面的星期X是可以省略的,后面的時間是要求有時區的.

示例如下(以在中國的中文操作系統機器為例):


 1.Tue, 16 Aug 2005 15:33:33 GMT
 2.Tue, 16 Aug 2005 23:33:33 +0800

其實這個rfc822應該也是電子郵件內容格式的規范,找一個郵件看看內容,也可以看出,郵件的時間格式也是遵循這個規范的.

要輸入第一種格式,使用SimpleDateFormat格式化即可,代碼如下

    public static void test1(Date date)
    {
        SimpleDateFormat sdfTemp = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z",Locale.US);
       
        SimpleTimeZone aZone = new SimpleTimeZone(8,"GMT");       
        sdfTemp.setTimeZone(aZone);
       
        System.out.println(sdfTemp.format(date));       
    } 

注意,其中設置了時區為GMT,否則會輸出:

 Tue, 16 Aug 2005 23:33:33 CST

這里的CST意思是代表"中國時間",但是一經搜索,發現CST代表了好幾個時區,太讓人混亂了.而在RTF822里面,CST僅代表美國中部時間.所以如果使用SimpleDateFormat,要設置時區以GMT表示,否則容易讓人迷惑而且不知道是那個時區.

假設你在中國,想根據當地時間輸入復合當地時間的字符串,讓人一看就能明白文章的日期,那么就使用第二種格式.(我推薦使用第二種方式,當然你的頻道主要給外國朋友瀏覽登除外)

上面說到和郵件有關,于是我們看看JavaMail包里面的javax.mail.internet.MailDateFormat,可以用來格式化日期:
(MyEclipse 3.8.4附帶的J2EE 1.3中的JavaMail包)

        MailDateFormat mdf = new MailDateFormat();
       
        SimpleTimeZone aZone = new SimpleTimeZone(8,"GMT");       
        //mdf.setTimeZone(aZone);      
       
        System.out.println(mdf.format(date));        

輸出結果為:

 Tue, 16 Aug 2005 23:33:33 +0800 (CST)
 
如果設置了時區為GMT,則輸出:

 Tue, 16 Aug 2005 15:33:33 +0000 (GMT) 
 
 
 
可以看到相對RTF822而言,好像多了一個后面的時區的說明及其括號.不知道這到底是怎么回事?

在硬盤上查找一番,發現在JIRA程序和Confluence中的RSS里都使用了這個日期格式. 

注意到這個不同,我瀏覽了一下outlook Express里面的郵件,發現兩種時間格式的郵件都存在,真是讓人迷惑,或許都可以吧,呵呵 :)

如果不想使用MailDateFormat的格式,那么就自己寫一個類來實現吧,例如

 public class RssDateFormat extends MailDateFormat
 {
     public RssDateFormat()
     {
         applyPattern("EEE, d MMM yyyy HH:mm:ss ’XXXXX’");       
     }
 
 } 

 
這是最簡單的了,當然可以把MailDateFormat的源碼拿過來改改更好一點,還不用依賴JavaMail了. :)

至此,我的RSS中的日期終于正確而且讓我滿意了. :)

 


 



Scud(飛云小俠) 2005-08-17 18:11 發表評論
]]>
使用Lucene進行全文檢索(三)---進行搜索http://m.tkk7.com/scud/archive/2005/08/12/9981.htmlScud(飛云小俠)Scud(飛云小俠)Fri, 12 Aug 2005 09:34:00 GMThttp://m.tkk7.com/scud/archive/2005/08/12/9981.htmlhttp://m.tkk7.com/scud/comments/9981.htmlhttp://m.tkk7.com/scud/archive/2005/08/12/9981.html#Feedback0http://m.tkk7.com/scud/comments/commentRss/9981.htmlhttp://m.tkk7.com/scud/services/trackbacks/9981.htmlscud(飛云小俠) http://www.jscud.com 轉載請注明來源/作者

關鍵字:lucene,html parser,全文檢索,IndexReader,Document,Field,IndexWriter,Term,HTMLPAGE


 無論是建立索引還是分析內容,都是為了用戶的搜索服務.
 
 在Lucene中,如果需要使用搜索,需要使用Searcher類,這是一個抽象類,它有2個子類:IndexSearcher和MultiSearcher.
 
 IndexSearcher是對一個索引進行搜索,如果你需要對多個索引進行搜索,可以使用MultiSearcher.下面的內容只介紹了IndexSearcher.
 
 搜索涉及到幾個問題:分頁,組合條件,根據條件過濾,排序等等.
 
 分頁:分頁在記錄列表的地方都會遇到,這里不在贅述,我也實現過一個保存分頁結果和顯示結果的類,用于自己的實際工作,下面也會用到保存分頁結果的類,代碼如下:

  package com.jscud.support;
  
  
  /**
   * 分頁顯示用的參數.
   *
   * @author scud(飛云小俠) http://www.jscud.com
   * 
   */
  
  public class DivPageInfo
  {
  
      //開始記錄數
      private int recStart;
  
      //結束記錄數
      private int recEnd;
  
      //總頁數
      private int pageCount;
  
      //當前頁
      private int page;
  
      //記錄總數
      private int recCount;
     
      //每頁記錄數
      private int perPageRows;
  
      public int getNicePageCount()
      {
          return getNicePageNum(pageCount);
      }
     
      //get,set等,不在列出
      //......
  
     
      /**
       * 得到友好的頁數數字,頁數為0時,返回1.
       *
       * @return 得到友好的頁數
       */
      public static int getNicePageNum(int nPage)
      {
              if (nPage == 0)
              {
                      return 1;
              }
              else
              {
                      return nPage;
              }
      }   
  } 

 顯示分頁結果的類就需要大家根據自己使用的框架來具體實現了.我使用的是WebWork.
 
 組合條件:在Lucene中,搜索的條件可以組合的很復雜,相關的類有BooleanQuery, FilteredQuery, MultiTermQuery, PhrasePrefixQuery, PhraseQuery, PrefixQuery, RangeQuery, SpanQuery, TermQuery 等等,從而可以組合出很復雜的條件用于查詢.
 另外QueryParser可以根據用戶輸入的字符串和設定的解析器和字段設置等,可以自動產生新的組合條件用于查詢,例如用戶輸入"john AND black",QueryParser可以自己分析出用戶是需要查詢字段中同時包含"john"和"black"的結果.
 
 過濾條件:有時候根據具體的用戶需求,有些記錄對于一些用戶是不可見的,此時就要使用過濾器來防止不合法的用戶看到不應該看到的記錄.過濾器同時也可以根據一些具體的條件來過濾掉一些用戶不想看到的記錄.如果需要實現自己的filter,只要參考QueryFilter,DateFilter實現Filter即可.
 
 排序:有時候,可能需要根據某個字段進行排序,例如按照時間排序.當然更多的時候是按照搜索結果的符合度進行排序,lucene默認的排序就是按照符合度來進行排序的.
 
 進行搜索的代碼如下,根據自己的需要進行代碼的修改:
 

 /**
 * 進行搜索.
 *
 * 參數依次為:搜索內容(支持lucene語法),當前頁,每頁記錄數,分頁信息對象
 *
 */
    public static List search(String searchText, int page, int perpage, final DivPageInfo pageinfo)
    {
        List docs = new ArrayList();
       
        if(!LuceneSearch.indexExist(indexDir)) { return docs; }

        Searcher searcher = null;
        try
        {
            StandardAnalyzer analyzer = new StandardAnalyzer();

            //處理檢索條件
            Query titleQuery = QueryParser.parse(searchText, "title", analyzer);
            Query contextQuery = QueryParser.parse(searchText, "content", analyzer);
            Query otherQuery = QueryParser.parse(searchText, "other", analyzer);

            BooleanQuery query = new BooleanQuery();
            query.add(titleQuery, false, false);
            query.add(contextQuery, false, false);
            query.add(otherQuery, false, false);

            //分頁檢索
            searcher = new IndexSearcher(indexDir);
            Hits hits = searcher.search(query);

            DivPageInfo.divPage(hits.length(), perpage, page, pageinfo);

            //取出當前頁的記錄
            for (int i = pageinfo.getRecStart(); i <= pageinfo.getRecEnd(); i++)
            {
                docs.add(LuceneDocument.getDocument(hits.doc(i - 1)));
            }
        }
        catch (IOException e)
        {
            LogMan.error("Error occur When Search Lucene", e);
        }
        catch (ParseException e)
        {
            LogMan.error("Error occur When Search Lucene", e);
        }
        finally
        {
            try
            {
                if (null != searcher)
                {
                    searcher.close();
                }
            }
            catch (IOException e)
            {
                LogMan.warn("Close searcher Error");
            }
        }

        return docs;
    }



 
 代碼中出現了一個新的類Hits,Hits是lucene的搜索結果集,是lazy load的結果集,只有你真正訪問它,它才去裝載真正的數據.
 
 代碼中還出現了一個LuceneDocument,這是為了在頁面中顯示而寫的一個輔助類,因為lucene的Document是final的,無法進行擴展,而要顯示時間字段必須要調用DateField中的函數,這樣在頁面中顯示就不太直觀了,所以寫了這個輔助類,代碼如下:
 

  package com.jscud.www.support.search;
  
  import java.sql.Timestamp;
  import java.util.Date;
  
  import org.apache.lucene.document.DateField;
  import org.apache.lucene.document.Document;
  import org.apache.lucene.document.Field;
  
  /**
   * 對Lucene的Document的封裝,用于顯示目的.
   *
   * @author scud(飛云小俠) http://www.jscud.com
   *
   */
  public class LuceneDocument
  {
      private Document doc;
     
      public LuceneDocument(Document doc)
      {
          this.doc = doc;
      }
     
      public static LuceneDocument getDocument(Document doc)
      {
          return new LuceneDocument(doc);
      }
     
      public String getValue(String name)
      {
          return doc.get(name);
      }
     
      public Field getField(String name)
      {
          return doc.getField(name);
      }
     
      public Timestamp getDateTime(String name)
      {
          String value = doc.get(name);
          return new Timestamp( DateField.stringToTime(value));
      }
     
      public Date getDate(String name)
      {
          String value = doc.get(name);
          return  DateField.stringToDate(value);       
      }    
  }


 
 使用WebWork對結果集進行了顯示,代碼如下:

          <ww:iterator value="docs">
          <tr >         
          <td>
          <a href="<jscud:contextpath /><ww:property  value="getValue('visiturl')" />"  target="_blank" >
          <ww:property value="getValue('title')" escape="true" />
          </a> &nbsp; (<jscud:datetime value="getDateTime('addtime')" />)
          </td>
          </tr>
          </ww:iterator> 
  



 然后調用分頁信息顯示tag即可.
 
 
 通過以上的應用,可以看到,其實使用lucene很簡單,以前總覺得很神秘,所以一直沒有使用過,用過之后才覺得如此簡單.
 
 
 當然,對于大容量數據下,群集情況下,在網上都有很多解決方案,在此不一一提出,感興趣的讀者可以自己去搜索. :)
 



Scud(飛云小俠) 2005-08-12 17:34 發表評論
]]>
使用Lucene進行全文檢索(二)---得到有效的內容http://m.tkk7.com/scud/archive/2005/08/12/9980.htmlScud(飛云小俠)Scud(飛云小俠)Fri, 12 Aug 2005 09:33:00 GMThttp://m.tkk7.com/scud/archive/2005/08/12/9980.htmlhttp://m.tkk7.com/scud/comments/9980.htmlhttp://m.tkk7.com/scud/archive/2005/08/12/9980.html#Feedback0http://m.tkk7.com/scud/comments/commentRss/9980.htmlhttp://m.tkk7.com/scud/services/trackbacks/9980.html

scud(飛云小俠) http://www.jscud.com 轉載請注明來源/作者

關鍵字:lucene,html parser,全文檢索,IndexReader,Document,Field,IndexWriter,Term,HTMLPAGE


  在使用lucene對相關內容進行索引時,會遇到各種格式的內容,例如Html,PDF,Word等等,那么我們如何從這么文檔中得到我們需要的內容哪?例如Html的內容,一般我們不需要對Html標簽建立索引,因為那不是我們需要搜索的內容.這個時候,我們就需要從Html內容中解析出我們所需要的內容.對于PDF,Word文檔,也是類似的要求.
 
  總之,我們只需要從內容中提取出我們需要的文本來建立索引,這樣用戶就能搜索到需要的內容,然后訪問對應的資源即可.

  Lucene本身帶的例子中有一個解析Html的代碼,不過不是純JAVA的,所以在網上我又找到了另外一個Html解析器,網址如下:http://htmlparser.sourceforge.net.
 
  對PDF解析的相關項目有很多,例如PDFBox.在PDFBox里面提出pdf的文本內容只需要一句話即可:  
  
   
Document doc = LucenePDFDocument.getDocument( file );  

  
  當然如果需要更高級的設置,就要使用PDFBox中PDFTextStripper等類來實現更高級的操作了.
 
 
  對Word文檔解析的相關有POI,網址是 http://jakarta.apache.org/poi/.
 
  HtmlParser本身提供的功能很強大,我們下面主要來關注我們需要的功能.首先給出幾個函數如下:
 

 /**
 * 解析一個Html頁面,返回一個Html頁面類.
 *
 * @param resource 文件路徑或者網址
 */
    public static SearchHtmlPage parseHtmlPage(String resource)
    {
        String title = "";
        String body = "";
        try
        {
            Parser myParser = new Parser(resource);

            //設置編碼:根據實際情況修改
            myParser.setEncoding("GBK");

            HtmlPage visitor = new HtmlPage(myParser);

            myParser.visitAllNodesWith(visitor);

            title = visitor.getTitle();

            body = combineNodeText(visitor.getBody().toNodeArray());
        }
        catch (ParserException e)
        {
            LogMan.error("Parse Html Page " + resource + " Error!");
        }

        SearchHtmlPage result = new SearchHtmlPage(title, body);

        return result;
    }

    /**
     * 解析Html內容,得到普通文本和鏈接的內容.
     *
     * @param content 要解析的內容
     * @return 返回解析后的內容
     */
    public static String parseHtmlContent(String content)
    {
        Parser myParser;
        NodeList nodeList = null;

        myParser = Parser.createParser(content, "GBK");

        NodeFilter textFilter = new NodeClassFilter(TextNode.class);
        NodeFilter linkFilter = new NodeClassFilter(LinkTag.class);

        //暫時不處理 meta
        //NodeFilter metaFilter = new NodeClassFilter(MetaTag.class);

        OrFilter lastFilter = new OrFilter();
        lastFilter.setPredicates(new NodeFilter[] { textFilter, linkFilter });

        try
        {
            nodeList = myParser.parse(lastFilter);
        }
        catch (ParserException e)
        {
            LogMan.warn("Parse Content Error", e);
        }

        //中場退出了
        if (null == nodeList)
        {
            return "";
        }

        Node[] nodes = nodeList.toNodeArray();

        String result = combineNodeText(nodes);
        return result;
    }

 //合并節點的有效內容
    private static String combineNodeText(Node[] nodes)
    {
        StringBuffer result = new StringBuffer();

        for (int i = 0; i < nodes.length; i++)
        {
            Node anode = (Node) nodes[i];

            String line = "";
            if (anode instanceof TextNode)
            {
                TextNode textnode = (TextNode) anode;
                //line = textnode.toPlainTextString().trim();
                line = textnode.getText();
            }
            else if (anode instanceof LinkTag)
            {
                LinkTag linknode = (LinkTag) anode;

                line = linknode.getLink();
                //過濾jsp標簽
                line = StringFunc.replace(line, "<%.*%>", "");
            }

            if (StringFunc.isTrimEmpty(line)) continue;

            result.append(" ").append(line);
        }

        return result.toString();
    }


  
  其中SearchHtmlPage類是表示一個Html頁面的模型,包含標題和內容,代碼如下:
  
 package com.jscud.www.support.search;
 
 /**
  * 搜索時解析Html后返回的頁面模型.
  *
  * @author scud(飛云小俠) http://www.jscud.com
  * 
  */
 public class SearchHtmlPage
 {
     /**標題*/
     private String title;
 
     /**內容*/
     private String body;
    
     public SearchHtmlPage(String title, String body)
     {
         this.title = title;
         this.body = body;
     }
    
     public String getBody()
     {
         return body;
     }
 
     public void setBody(String body)
     {
         this.body = body;
     }
 
     public String getTitle()
     {
         return title;
     }
 
     public void setTitle(String title)
     {
         this.title = title;
     }
 }
 


 
  當然,使用HtmlParser解析Html資源還有很多其他的方法,可以設置很多的條件來滿足用戶的解析要求,用戶可以閱讀其他的文章或者HtmlParser的文檔來了解,在此不多介紹.
 
  下一節講解如何進行搜索.

 



Scud(飛云小俠) 2005-08-12 17:33 發表評論
]]>
使用Lucene進行全文檢索(一)---處理索引http://m.tkk7.com/scud/archive/2005/08/12/9979.htmlScud(飛云小俠)Scud(飛云小俠)Fri, 12 Aug 2005 09:31:00 GMThttp://m.tkk7.com/scud/archive/2005/08/12/9979.htmlhttp://m.tkk7.com/scud/comments/9979.htmlhttp://m.tkk7.com/scud/archive/2005/08/12/9979.html#Feedback1http://m.tkk7.com/scud/comments/commentRss/9979.htmlhttp://m.tkk7.com/scud/services/trackbacks/9979.htmlscud(飛云小俠) http://www.jscud.com 轉載請注明來源/作者

關鍵字:lucene,html parser,全文檢索,IndexReader,Document,Field,IndexWriter,Term,HTMLPAGE


 Lucene是一個全文檢索的引擎,目前有Java和.Net 等幾個版本.Java版本的網址是http://lucene.apache.org.相關的一個項目是車東的WebLucene: http://sourceforge.net/projects/weblucene.

 首先,基于一個簡單的新聞系統,要想做全文檢索.新聞系統的管理等在這里不在具體提出,下面列出新聞對象的類:
 
 注:程序用會到一些工具類,不在此列出,用戶可以自己實現.
 
 

  package com.jscud.website.newsinfo.bean;
  
  
  import java.sql.Timestamp;
  
  import com.jscud.util.DateTime;
  import com.jscud.util.StringFunc;
  import com.jscud.website.newsinfo.NewsConst;
  
  
  /**
   * 一個新聞.
   *
   * @author scud(飛云小俠) http://www.jscud.com
   * 
   */
  public class NewsItem
  {
  
      private int nid; //新聞編號
  
      private int cid; //類別編號
  
      private String title;//標題
  
      private int showtype; //內容類型:目前支持url和html
  
      private String content;//內容
  
      private String url;//對應網址,如果內容類型是url的話
  
      private Timestamp addtime; //增加時間
  
      private int click; //點擊數
     
      //對應的get,set函數,較多不在列出,可以使用工具生成
      //......
  
     
      /**
       * 按照類型格式化
       */
      public String getShowContent()
      {
          String sRes = content;
          if(showtype == NewsConst.ShowType_HTML)
          {
          }  
          return sRes;
      }
     
      public String getTarget()
      {
          if(showtype == NewsConst.ShowType_URL)
          {
              return "_blank";
          }
          else
              return "";       
      }
     
      /**
       * 靜態Html文件的路徑及其名字
       */
      public String getHtmlFileName()
      {
          int nYear = DateTime.getYear_Date(getAddtime());
          int nMonth =  DateTime.getMonth_Date(getAddtime());
             
          String sGeneFileName =
             "/news/" + getCid() + "/" + nYear + "/" + nMonth +"/" + getNid() + ".htm";
         
          return sGeneFileName;
      }
     
      /**
       * 靜態Html文件的路徑
       */
      public String getHtmlFilePath()
      {
          int nYear = DateTime.getYear_Date(getAddtime());
          int nMonth =  DateTime.getMonth_Date(getAddtime());
             
          String sGeneFilePath =
             getCid() + "_" + nYear + "_" + nMonth;
         
          return sGeneFilePath;
      }     
  } 


 
 可以看到,我們需要對標題和內容進行檢索,為了這個目的,我們首先需要來研究一下lucene.
 
 在Lucene中,如果要進行全文檢索,必須要先建立索引然后才能進行檢索,當然實際工作中還會有刪除索引和更新索引的工作.
 
 在此之前,介紹一個最基本的類(摘抄自http://m.tkk7.com/cap/archive/2005/07/17/7849.html):
 
 Analyzer 文件的分析器(聽起來別扭,還是叫Analyzer好了)的抽象,這個類用來處理分詞(對中文尤其重要,轉換大小寫(Computer->computer,實現查詢大小寫無關),轉換詞根(computers->computer),消除stop words等,還負責把其他格式文檔轉換為純文本等.
 
 在lucene中,一般會使用StandardAnalyzer來分析內容,它支持中文等多字節語言,當然可以自己實現特殊的解析器.StandardAnalyzer目前對中文的處理是按照單字來處理的,這是最簡單的辦法,但是也有缺點,會組合出一些沒有意義的結果來. 
 
 
 首先我們來了解建立索引,建立索引包含2種情況,一種是給一條新聞建立索引,另外的情況是在開始或者一定的時間給批量的新聞建立索引,所以為了通用,我們寫一個通用的建立索引的函數:
 
 (一般一類的索引都放在一個目錄下,這個配置可以在函數中定義,也可以寫在配置文件中,通過參數傳遞給函數.)

    /**
     * 生成索引.
     *
     * @param doc 目標文檔
     * @param indexDir 索引目錄
     */
    public static void makeIndex(Document doc, String indexDir)
    {
        List aList = new ArrayList();
        aList.add(doc);
        makeIndex(aList, indexDir);
    }
 
    /**
     * 生成索引.
     *
     * @param doc 生成的document.
     * @param indexDir 索引目錄
     */
    public static void makeIndex(List docs, String indexDir)
    {
        if (null == docs)
        {
            return;
        }       
        boolean indexExist = indexExist(indexDir);

        IndexWriter writer = null;
        try
        {
            StandardAnalyzer analyzer = new StandardAnalyzer();
           
            //如果索引存在,就追加.如果不存在,就建立新的索引.lucene要是自動判決就好了.
            if(indexExist)
            {
                writer = new IndexWriter(indexDir, analyzer, false);
            }
            else
            {
                writer = new IndexWriter(indexDir, analyzer, true);
            }

            //添加一條文檔
            for (int i = 0; i < docs.size(); i++)
            {
                Document doc = (Document) docs.get(i);
                if (null != doc)
                {
                    writer.addDocument(doc);
                }
            }

            //索引完成后的處理
            writer.optimize();
        }
        catch (IOException e)
        {
            LogMan.warn("Error in Make Index", e);
        }
        finally
        {
            try
            {
                if (null != writer)
                {
                    writer.close();
                }
            }
            catch (IOException e)
            {
                LogMan.warn("Close writer Error");
            }
        }
    }



 可以看到,建立索引用到類是IndexWrite,它可以新建索引或者追加索引,但是需要自己判斷.判斷是通過IndexReader這個類來實現的,函數如下:

 

  /**
     * 檢查索引是否存在.
     * @param indexDir
     * @return
     */
    public static boolean indexExist(String indexDir)
    {
        return IndexReader.indexExists(indexDir);
    }
 


 如果每次都是新建索引的話,會把原來的記錄刪除,我在使用的時候一開始就沒有注意到,后來觀察了一下索引文件,才發現這個問題.
 
 
 還可以看到,建立索引是給用戶的Document對象建立索引,Document表示索引中的一條文檔記錄.那么我們如何建立一個文檔那?以新聞系統為例,代碼如下:
 

     /**
      * 生成新聞的Document.
      *
      * @param aNews 一條新聞.
      *
      * @return lucene的文檔對象
      */
     public static Document makeNewsSearchDocument(NewsItem aNews)
     {
         Document doc = new Document();
 
         doc.add(Field.Keyword("nid", String.valueOf(aNews.getNid())));
 
         doc.add(Field.Text("title", aNews.getTitle()));
        
         //對Html進行解析,如果不是html,則不需要解析.或者根據格式調用自己的解析方法
         String content = parseHtmlContent(aNews.getContent());
 
         doc.add(Field.UnStored("content", content));
 
         doc.add(Field.Keyword("addtime", aNews.getAddtime()));
 
         //可以加入其他的內容:例如新聞的評論等
         doc.add(Field.UnStored("other", ""));
 
         //訪問url
         String newsUrl = "/srun/news/viewhtml/" + aNews.getHtmlFilePath() + "/" + aNews.getNid()
                         + ".htm";
 
         doc.add(Field.UnIndexed("visiturl", newsUrl));
 
         return doc;
     }


 
 通過上面的代碼,我們把一條新聞轉換為lucene的Document對象,從而進行索引工作.在上面的代碼中,我們又引入了lucene中的Field(字段)類.Document文檔就像數據庫中的一條記錄,它有很多字段,每個字段是一個Field對象.
 
 從別的文章摘抄一段關于Field的說明(摘抄自http://m.tkk7.com/cap/archive/2005/07/17/7849.html):
 [quote]
    類型                               Analyzed Indexed Stored 說明
    Field.Keyword(String,String/Date)  N Y Y                    這個Field用來儲存會直接用來檢索的比如(編號,姓名,日期等)
    Field.UnIndexed(String,String)     N N Y                    不會用來檢索的信息,但是檢索后需要顯示的,比如,硬件序列號,文檔的url地址
    Field.UnStored(String,String)      Y Y N                    大段文本內容,會用來檢索,但是檢索后不需要從index中取內容,可以根據url去load真實的內容
    Field.Text(String,String)          Y Y Y                    檢索,獲取都需要的內容,直接放index中,不過這樣會增大index
    Field.Text(String,Reader)          Y Y N                    如果是一個Reader, lucene猜測內容比較多,會采用Unstored的策略.
 [/quote]
 
 我們可以看到新聞的編號是直接用來檢索的,所以是Keyword類型的字段,新聞的標題是需要檢索和顯示用的,所以是Text類型,而新聞的內容因為是Html格式的,所以在經過解析器的處理用,使用的UnStored的格式,而新聞的時間是直接用來檢索的,所以是KeyWord類型.為了在新聞索引后用戶可以訪問到完整的新聞頁面,還設置了一個UnIndexed類型的訪問地址字段.
 
 (對Html進行解析的處理稍后在進行講解)
 
 為一條新聞建立索引需要兩個步驟:獲取Document,傳給makeIndex函數,代碼如下:

    public static void makeNewsInfoIndex(NewsItem aNews)
    {
        if (null == aNews)
        {
            return;
        }
        makeIndex(makeNewsSearchDocument(aNews),indexDir);
    }  


 

 
 
 建立索引的工作就進行完了,只要在增加新聞后調用 makeNewsInfoIndex(newsitem); 就可以建立索引了.
 
 如果需要刪除新聞,那么也要刪除對應的索引,刪除索引是通過IndexReader類來完成的:
 


    /**
     * 刪除索引.
     * @param aTerm 索引刪除條件
     * @param indexDir 索引目錄
     */
    public static void deleteIndex(Term aTerm, String indexDir)
    {
        List aList = new ArrayList();
        aList.add(aTerm);
        deleteIndex(aList, indexDir);
    }

    /**
     * 刪除索引.
     *
     * @param aTerm 索引刪除條件.
     * @param indexDir 索引目錄
     * 
     */
    public static void deleteIndex(List terms, String indexDir)
    {
        if (null == terms)
        {
            return;
        }
       
        if(!indexExist(indexDir)) { return; }

        IndexReader reader = null;
        try
        {
            reader = IndexReader.open(indexDir);
            for (int i = 0; i < terms.size(); i++)
            {
                Term aTerm = (Term) terms.get(i);
                if (null != aTerm)
                {
                    reader.delete(aTerm);
                }
            }
        }
        catch (IOException e)
        {
            LogMan.warn("Error in Delete Index", e);
        }
        finally
        {
            try
            {
                if (null != reader)
                {
                    reader.close();
                }
            }
            catch (IOException e)
            {
                LogMan.warn("Close reader Error");
            }
        }
    } 


 
 刪除索引需要一個條件,類似數據庫中的字段條件,例如刪除一條新聞的代碼如下:
 

     public static void deleteNewsInfoIndex(int nid)
     {
         Term aTerm = new Term("nid", String.valueOf(nid));
         deleteIndex(aTerm,indexDir);
     }   




 通過新聞的ID,就可以刪除一條新聞.
 
 如果需要更新新聞,如何更新索引哪? 更新索引需要先刪除索引然后新建索引2個步驟,其實就是把上面的代碼組合起來,例如更新一條新聞:

     public static void updateNewsInfoIndex(NewsItem aNews)
     {
         if (null == aNews)
         {
             return;
         }
         deleteNewsInfoIndex(aNews.getNid());
         makeNewsInfoIndex(aNews);
     } 
 



 
 至此,索引的建立更新和刪除就告一段落了.其中批量更新新聞的代碼如下:
 (批量更新應該在訪問人數少或者后臺程序在夜間執行)

    public static void makeAllNewsInfoIndex(List newsList)
    {
        List terms = new ArrayList();
        List docs = new ArrayList();

        for (int i = 0; i < newsList.size(); i++)
        {
            NewsItem aitem = (NewsItem) newsList.get(i);
            if (null != aitem)
            {
                terms.add(new Term("nid", String.valueOf(aitem.getNid())));
                docs.add(makeNewsSearchDocument(aitem));
            }
        }

        deleteIndex(terms,indexDir);
        makeIndex(docs,indexDir);
    } 



 
 
 下一節講解如何對要建立索引的內容進行解析,例如解析Html等內容.



Scud(飛云小俠) 2005-08-12 17:31 發表評論
]]>
分析/解析Html頁面:HTML Parser的試用 http://m.tkk7.com/scud/archive/2005/08/11/9846.htmlScud(飛云小俠)Scud(飛云小俠)Thu, 11 Aug 2005 14:31:00 GMThttp://m.tkk7.com/scud/archive/2005/08/11/9846.htmlhttp://m.tkk7.com/scud/comments/9846.htmlhttp://m.tkk7.com/scud/archive/2005/08/11/9846.html#Feedback4http://m.tkk7.com/scud/comments/commentRss/9846.htmlhttp://m.tkk7.com/scud/services/trackbacks/9846.html作者:scud(飛云小俠)  http://www.jscud.com  轉載請注明作者來源.否則請勿轉載,謝謝.

最近在研究lucene的全文檢索,在很多地方需要解析或者說分析Html內容或者Html頁面,Lucene本身的演示程序中也提供了一個Html Parser,但是不是純Java的解決方案.于是到處搜索,在網上找到了一個"HTMLParser".

網址是: http://htmlparser.sourceforge.net ,當前版本為1.5.

下載下來,試用一番,感覺不錯,完全能滿足lucene解析Html的需求.

過幾天貼出lucene進行全文檢索的代碼.(檢索本站的文章等).

試用代碼如下,供大家參考:

package com.jscud.test;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;

import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.filters.NodeClassFilter;
import org.htmlparser.filters.OrFilter;
import org.htmlparser.nodes.TextNode;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;
import org.htmlparser.visitors.HtmlPage;
import org.htmlparser.visitors.TextExtractingVisitor;

import com.jscud.util.LogMan; //一個日志記錄類

/**
 * 演示了Html Parse的應用.
 *
 * @author scud http://www.jscud.com
 */

public class ParseHtmlTest
{

    public static void main(String[] args) throws Exception
    {
        String aFile = "e:/jscud/temp/test.htm";

        String content = readTextFile(aFile, "GBK");

        test1(content);
        System.out.println("====================================");

        test2(content);
        System.out.println("====================================");

        test3(content);
        System.out.println("====================================");

        test4(content);
        System.out.println("====================================");

        test5(aFile);
        System.out.println("====================================");

        //訪問外部資源,相對慢
        test5("        System.out.println("====================================");

    }

    /**
     * 讀取文件的方式來分析內容.
     * filePath也可以是一個Url.
     *
     * @param resource 文件/Url
     */
    public static void test5(String resource) throws Exception
    {
        Parser myParser = new Parser(resource);

        //設置編碼
        myParser.setEncoding("GBK");

        HtmlPage visitor = new HtmlPage(myParser);

        myParser.visitAllNodesWith(visitor);

        String textInPage = visitor.getTitle();

        System.out.println(textInPage);
    }

    /**
     * 按頁面方式處理.對一個標準的Html頁面,推薦使用此種方式.
     */
    public static void test4(String content) throws Exception
    {
        Parser myParser;
        myParser = Parser.createParser(content, "GBK");

        HtmlPage visitor = new HtmlPage(myParser);

        myParser.visitAllNodesWith(visitor);

        String textInPage = visitor.getTitle();

        System.out.println(textInPage);
    }

    /**
     * 利用Visitor模式解析html頁面.
     *
     * 小優點:翻譯了<>等符號
     * 缺點:好多空格,無法提取link
     *  
     */
    public static void test3(String content) throws Exception
    {
        Parser myParser;
        myParser = Parser.createParser(content, "GBK");

        TextExtractingVisitor visitor = new TextExtractingVisitor();

        myParser.visitAllNodesWith(visitor);

        String textInPage = visitor.getExtractedText();

        System.out.println(textInPage);
    }

    /**
     * 得到普通文本和鏈接的內容.
     *
     * 使用了過濾條件.
     */
    public static void test2(String content) throws ParserException
    {
        Parser myParser;
        NodeList nodeList = null;

        myParser = Parser.createParser(content, "GBK");

        NodeFilter textFilter = new NodeClassFilter(TextNode.class);
        NodeFilter linkFilter = new NodeClassFilter(LinkTag.class);

        //暫時不處理 meta
        //NodeFilter metaFilter = new NodeClassFilter(MetaTag.class);

        OrFilter lastFilter = new OrFilter();
        lastFilter.setPredicates(new NodeFilter[] { textFilter, linkFilter });

        nodeList = myParser.parse(lastFilter);

        Node[] nodes = nodeList.toNodeArray();

        for (int i = 0; i < nodes.length; i++)
        {
            Node anode = (Node) nodes[i];

            String line = "";
            if (anode instanceof TextNode)
            {
                TextNode textnode = (TextNode) anode;
                //line = textnode.toPlainTextString().trim();
                line = textnode.getText();
            }
            else if (anode instanceof LinkTag)
            {
                LinkTag linknode = (LinkTag) anode;

                line = linknode.getLink();
               
//@todo 過濾jsp標簽:可以自己實現這個函數
                //line = StringFunc.replace(line, "<%.*%>", "");
            }

            if (isTrimEmpty(line))
                continue;

            System.out.println(line);
        }
    }

    /**
     * 解析普通文本節點.
     *
     * @param content
     * @throws ParserException
     */
    public static void test1(String content) throws ParserException
    {
        Parser myParser;
        Node[] nodes = null;

        myParser = Parser.createParser(content, null);

        nodes = myParser.extractAllNodesThatAre(TextNode.class); //exception could be thrown here

        for (int i = 0; i < nodes.length; i++)
        {
            TextNode textnode = (TextNode) nodes[i];
            String line = textnode.toPlainTextString().trim();
            if (line.equals(""))
                continue;
            System.out.println(line);
        }

    }

    /**
     * 讀取一個文件到字符串里.
     *
     * @param sFileName  文件名
     * @param sEncode   String
     * @return 文件內容
     */
    public static String readTextFile(String sFileName, String sEncode)
    {
        StringBuffer sbStr = new StringBuffer();

        try
        {
            File ff = new File(sFileName);
            InputStreamReader read = new InputStreamReader(new FileInputStream(ff),
                    sEncode);
            BufferedReader ins = new BufferedReader(read);

            String dataLine = "";
            while (null != (dataLine = ins.readLine()))
            {
                sbStr.append(dataLine);
                sbStr.append("\r\n");
            }

            ins.close();
        }
        catch (Exception e)
        {
            LogMan.error("read Text File Error", e);
        }

        return sbStr.toString();
    }

    /**
     * 去掉左右空格后字符串是否為空
     * @param astr String
     * @return boolean
     */
    public static boolean isTrimEmpty(String astr)
    {
        if ((null == astr) || (astr.length() == 0))
        {
            return true;
        }
        if (isBlank(astr.trim()))
        {
            return true;
        }
        return false;
    }

    /**
     * 字符串是否為空:null或者長度為0.
     * @param astr 源字符串.
     * @return boolean
     */
    public static boolean isBlank(String astr)
    {
        if ((null == astr) || (astr.length() == 0))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

}

 




Scud(飛云小俠) 2005-08-11 22:31 發表評論
]]>
主站蜘蛛池模板: 亚洲av永久无码精品天堂久久 | 99免费视频观看| 99视频在线免费看| 日韩免费一区二区三区在线| 成人毛片18女人毛片免费| 四虎影视永久免费视频观看| 国产成人精品亚洲精品| 亚洲AV无码专区亚洲AV伊甸园| 亚洲美免无码中文字幕在线| 亚洲午夜无码毛片av久久京东热| 色偷偷亚洲男人天堂| XXX2高清在线观看免费视频| 日韩免费人妻AV无码专区蜜桃 | 华人在线精品免费观看| 99在线精品视频观看免费| 国产又大又黑又粗免费视频 | 亚洲欧美第一成人网站7777| 特级毛片aaaa级毛片免费| AAA日本高清在线播放免费观看| 嫖丰满老熟妇AAAA片免费看| 免费一级毛片不卡不收费| 国产AV无码专区亚洲AVJULIA | 91av免费在线视频| 亚洲香蕉免费有线视频| 国产gav成人免费播放视频| 亚洲AV中文无码乱人伦下载| 天堂亚洲国产中文在线| aa毛片免费全部播放完整| 久久久高清免费视频| 亚洲午夜av影院| 亚洲熟妇av一区二区三区下载| 无遮挡呻吟娇喘视频免费播放| 久久综合九色综合97免费下载| 卡一卡二卡三在线入口免费| 精品亚洲综合在线第一区| 亚洲AV无码一区二区三区在线| eeuss影院www天堂免费| 欧美大尺寸SUV免费| 亚洲一区二区三区AV无码| 亚洲中文字幕无码中文字| 在线毛片片免费观看|