2006年5月18日
反向索引:

正向索引(草稿,不完全,因?yàn)槭盏絝ield info的影響,不同的field存儲(chǔ)內(nèi)容不同,且fieldInfo的有些信息,TOKENIZED BINARY COMPRESSED也是保存在.fdt的每個(gè)document相關(guān)段的bits中,而不是.fnm中):
Lucene在1.9版本的時(shí)候就已經(jīng)加入了對(duì)GCJ的支持,利用GCJ編譯Lucene,并且使用新的GCJIndexInput.java讀寫文件系統(tǒng),
直接調(diào)用操作系統(tǒng)級(jí)別的native方法,相信讀寫性能能夠極大得提高啊。
具體代碼可見Lucene的gcj目錄,編譯使用ant gcj
說明見Similarity.java的javadoc信息:
算法請(qǐng)參考javadoc的,它使用的是Vector Space Model (VSM) of Information Retrieval。
針對(duì)一條查詢語句q(query),一個(gè)d(document)的得分公式
其中,
tf(t in d) 表示某個(gè)term的出現(xiàn)頻率,定義了term t出現(xiàn)在當(dāng)前地document d的次數(shù)。 那些query中給定地term,如果出現(xiàn)越多次的,得分越高 。它在默認(rèn)實(shí)現(xiàn)DefaultSimilarity的公式為
idf(t) 表示反向文檔頻率。這個(gè)參數(shù)表示docFreq(term t一共在多少個(gè)文檔中出現(xiàn))的反向影響值。它意味著在越少文檔中出現(xiàn)的terms貢獻(xiàn)越高地分?jǐn)?shù)。它在默認(rèn)實(shí)現(xiàn)DefaultSimilarity的公式為:
idf(t) = |
1 + log ( |
numDocs |
––––––––– |
docFreq+1 |
|
) |
coord(q,d) 是一個(gè)基于在該文檔中出現(xiàn)了多少個(gè)query中的terms的得分 因素。文檔中出現(xiàn)的query中的terms數(shù)量/query總共多少個(gè)query數(shù)量。典型的,一個(gè)文檔包含越多地query中的terms會(huì)得到更高地分。This is a search time factor computed in coord(q,d) by the Similarity in effect at search time.
queryNorm(q) 是一個(gè)標(biāo)準(zhǔn)化參數(shù),它是用來區(qū)分比較不同queries時(shí)的因素,這個(gè)因素不影響document ranking (因?yàn)樗械膔anked document都會(huì)乘以相同的值),但是不同地queries(或這不同地indexes中)它會(huì)得到不同的可用于比較的值。This is a search time factor computed by the Similarity in effect at search time. 它在默認(rèn)實(shí)現(xiàn)DefaultSimilarity的公式為:
其中的sumOfSquaredWeights(of the query terms)是根據(jù)the query Weight object計(jì)算出來的. For example, a boolean query computes this value as:
t.getBoost() 是一個(gè)term t在query q中的search time boost, 它是在the query text (see query syntax)中指定的, 或者被應(yīng)用程序直接調(diào)用 setBoost() 設(shè)置的. 注意,這兒沒有直接的API去訪問在 a multi term query的一個(gè)term的boost值,但是multi terms會(huì)以multi TermQuery objects在一個(gè)query中被表示,因此the boost of a term in the query可以使用子query的 getBoost() 反問到.
norm(t,d) 封裝(encapsulates)了一些(indexing time)的boost和length factors: ???這個(gè)參數(shù)之和field中tokens的數(shù)量有關(guān),和term本身無關(guān)???
Document boost - set by calling doc.setBoost() before adding the document to the index.
Field boost - set by calling field.setBoost() before adding the field to a document.
lengthNorm(field) -。當(dāng)文檔被加入到索引時(shí)計(jì)算,,和document的field 中的tokens的數(shù)量有關(guān),因此,一個(gè)比較短的fields貢獻(xiàn)更高的分?jǐn)?shù)。LengthNorm is computed by the Similarity class in effect at indexing. DefaultSimilarity中的實(shí)現(xiàn)為(float)(1.0 / Math.sqrt(numTerms));
當(dāng)一個(gè)文檔被加入索引時(shí),上述因素會(huì)被相乘。如果文檔有多個(gè)fields同名,他們的boosts數(shù)值會(huì)被多次相乘。
但是 ,計(jì)算出的norm數(shù)值在存儲(chǔ)時(shí)是使用一個(gè)a single byte編碼的。search時(shí),這個(gè)norm byte從index directory讀取,并且被解碼回float。這個(gè)編碼/解碼算法會(huì)產(chǎn)生精度丟失。 - it is not guaranteed that decode(encode(x)) = x. For instance, decode(encode(0.89)) = 0.75. Also notice that search time is too late to modify this norm part of scoring, e.g. by using a different Similarity for search.
Lucene的索引文件,會(huì)包含很多個(gè)segments文件,每個(gè)segment中包含多個(gè)documents文件,一個(gè)segment中會(huì)有完整的正向索引和反向索引。
在搜索時(shí),Lucene會(huì)遍歷這些segments,以segments為基本單位獨(dú)立搜索每個(gè)segments文件,而后再把搜索結(jié)果合并。
建立索引文件的過程,實(shí)際就是把documents文件一個(gè)個(gè)加入索引中,Lucene的做法是最開始為每個(gè)新加入的document獨(dú)立生成一個(gè)segment,放在內(nèi)存中。而后,當(dāng)內(nèi)存中segments數(shù)量到達(dá)一個(gè)闕值時(shí),合并這些segments,新生成一個(gè)segment加入文件系統(tǒng)的segments列表中。
而當(dāng)文件系統(tǒng)的segments數(shù)量過多時(shí),勢(shì)必影響搜索效率,因此需要不斷合并這些segments文件。
那么Lucene的合并策略是什么?如何保證合適的segments數(shù)量呢?
其實(shí)Lucene有兩套基本的策略:
第一種策略實(shí)現(xiàn)代碼位于IndexWriter#optimize()函數(shù),其實(shí)就是把所有segments文件合并成一個(gè)文件。合并的算法是遞歸合并列表最后的mergeFactor個(gè)segments文件直到合并成一個(gè)文件。這兒mergeFactor是Lucene的一個(gè)參數(shù)。
btw: 從算法細(xì)節(jié)上看,其實(shí)我不是喜歡這段實(shí)現(xiàn),因?yàn)榱斜淼淖詈髆ergeFactor個(gè)文件內(nèi)容實(shí)際被掃描了segmens_count/mergeFactor次。如果分段歸納合并的方式不知道是否更好,每個(gè)segment文件內(nèi)容都將被掃描 ceil(Log_mergeFactor(segmens_count)) 或ceil(Log_mergeFactor(segmens_count)) +1次,是否更好?
第二種策略實(shí)現(xiàn)代碼位于IndexWriter#maybeMergeSegments()函數(shù)中,這個(gè)代碼就復(fù)雜了,它的基本策略就是要求確保合并后兩個(gè)公式的成立:
假定segments是個(gè)有序列表,B表示maxBufferedDocs,f(n)=ceil(log_M(ceil(n/B))),M表示mergeFactor,這兒maxBufferedDocs和mergeFactor是兩個(gè)參數(shù)
1. 如果第i個(gè)segment的documents數(shù)量為x,第i+1個(gè)segment的documents數(shù)量為y,那么f(x)>f(y)一定成立
2. f(n)值相同的segments的數(shù)量不得超過M。
那么maybeMergeSegments()函數(shù)是如何確保這兩個(gè)公式成立的呢?
1.首先,從代碼:
protected final void maybeFlushRamSegments() throws IOException {
// A flush is triggered if enough new documents are buffered or
// if enough delete terms are buffered
if (ramSegmentInfos.size() >= minMergeDocs
|| numBufferedDeleteTerms >= maxBufferedDeleteTerms) {
flushRamSegments();
}
}
這兒minMergeDocs=maxBufferedDocs, 因此可以看出,當(dāng)內(nèi)存中緩存的segments被合并寫回磁盤時(shí)生成的segment的document count等于或小于maxBufferedDocs(即minMergeDocs)。
補(bǔ)充:因?yàn)閙aybeMergeSegments()運(yùn)行在同步代碼中,因此只要ramSegmentInfos.size==minMergerDocs(即maxBufferedDocs)就會(huì)寫回磁盤,因此應(yīng)該不存在ramSegmentInfos.size>maxBufferedDocs才寫回的情況。而且,但如果是這種情況,因?yàn)楹喜⒑蟮膕egment文件的document count過大,后面的maybeMergeSegments()將不做合并處理直接退出,上述公式就可能不成立,那么算法將有錯(cuò)。
----
2.
2.1 因此maybeMergeSegments()第一次執(zhí)行時(shí),所有segments的document count都小于等于maxBufferedDocs。此時(shí),從i=0開始,合并i~i+mergeFactor-1個(gè)文件,如果合并后的doc count>maxBufferedDocs時(shí),保留第i個(gè)segment,否則繼續(xù)合并改變后的i~i+mergeFactor-1,直到doc count>maxBufferedDocs或所有segments文件個(gè)數(shù)已經(jīng)<mergeFactor了。經(jīng)過這樣一輪的合并,除了最后<mergeFactor個(gè)的doc counts<=maxBufferedDocs文件外,其它文件的doc counts一定都>maxBufferedDocs并<maxBufferedDocs*mergeFactor了。
2.2 這時(shí),不理會(huì)最后<mergeFactor個(gè)doc count<maxBufferedDocs的文件,而后按2.1的同理規(guī)則,合并之前的文件,讓這些文件的最后<mergerFactor個(gè)segment符合 maxBufferedDocs<doc counts<=maxBufferedDocs*mergeFactor,之前的segment文件都符合maxBufferedDocs*mergeFactor<doc counts<=maxBufferedDocs*mergeFactor^2
2.3 重復(fù)2.2,最后得到的列表就會(huì)滿足上述兩等式的成立
---
3
之后,每次從內(nèi)存緩存中flush出一個(gè)新的segment時(shí),也就是往這個(gè)segments列表的最后增加一個(gè)doc_count<=maxBufferedDocs的文件,同樣執(zhí)行上述步驟2進(jìn)行合并,能夠始終保證上述兩公式的成立。
----
4
4.1
IndexWriter#addIndexesNoOptimize同樣借鑒使用了maybeMergeSegments()算法,區(qū)別此時(shí),實(shí)際是已有一個(gè)符合兩公式的segments序列T,在T之后追加上隨意順序的segments序列S。這時(shí),我們先找到S中doc count值最大的那個(gè)segment,計(jì)算出它屬于的區(qū)間f(x),使得maxBufferedDocs*mergeFactor^x<doc counts<=maxBufferedDocs*mergeFactor^(x+1),而后按2.2的算法合并出除了最后<mergerFactor個(gè)segments外, 之前所有segments都符合 a. doc count>maxBufferedDocs*mergeFactor^(x+1) b.符合上述2等式。
btw: 因?yàn)檫@兒調(diào)用IndexWriter#addIndexesNoOptimize傳入的參數(shù)是maxBufferedDocs*mergeFactor^(x+1),因?yàn)镾所有segment的doc count都一定小于maxBufferedDocs*mergeFactor^(x+1),因此S的所有元素都會(huì)參與收縮合并。
4.2 將最后<mergerFactor個(gè)doc count<maxBufferedDocs*mergeFactor^(x+1)的segments合并,如果合并后的segment的doc count大于maxBufferedDocs*mergeFactor^(x+1),就繼續(xù)2.2步驟,使得整個(gè)隊(duì)列符合上述兩公式
-----
上述兩種策略,最終確保了Lucene中的segments不會(huì)太多,確保效率。
BTW:實(shí)際上,如果documents太多時(shí),Lucene還支持把docuements分成幾個(gè)組,每個(gè)組用獨(dú)立的進(jìn)程或電腦進(jìn)行索引,而后再多個(gè)目錄的索引合并起來,具體可參考IndexWriter#addIndexesNoOptimize和IndexWriter#addIndexes函數(shù)
FieldSelectorResult:枚舉,分別為
LOAD Document#getFieldable和Document#getField不會(huì)返回null
LAZY_LOAD :Lazy的Field意味著在搜索結(jié)果里這個(gè)Field的值缺省是不讀取的,只有當(dāng)你真正對(duì)這個(gè)Field取值的時(shí)候才會(huì)去取。所以如果你要對(duì)它取值,你得保證IndexReader還沒有close。 Document#getField不能使用,只能使用Document#getFieldable
NO_LOAD Document#getField和Document#getFieldable都返回null,Document#add不被調(diào)用。
LOAD_AND_BREAK 類似LOAD,Document#getField和Document#getFieldable都可用,但返回后就結(jié)束,Document可能沒有完整的field的Set,參考LoadFirstFieldSelector 。
LOAD_FOR_MERGE 類似LOAD,但不壓縮任何數(shù)據(jù)。只被SegmentMerger的一個(gè)FieldSelector匿名內(nèi)嵌實(shí)現(xiàn)類使用。Document#getField和Document#getFieldable可返回null.
SIZE 返回Field的size而不是value. Size表示存儲(chǔ)這個(gè)field需要的bytes數(shù),string數(shù)值使用2*chars。size被存儲(chǔ)為a binary value,表現(xiàn)為as an int in a byte[],with the higher order byte first in [0]。
SIZE_AND_BREAK 類似SIZE,但立刻break from the field loading loop, i.e. stop loading further fields, after the size is loaded
======================================
Field中三大enum: Store Index和TermVector:
------------------------------------
Store.COMPRESS Store the original field value in the index in a compressed form. This is useful for long documents and for binary valued fields.壓縮存儲(chǔ);
Store.YES Store the original field value in the index. This is useful for short texts like a document's title which should be displayed with the results. The value is stored in its original form, i.e. no analyzer is used before it is stored. 索引文件本來只存儲(chǔ)索引數(shù)據(jù), 此設(shè)計(jì)將原文內(nèi)容直接也存儲(chǔ)在索引文件中,如文檔的標(biāo)題。
Store.NO Do not store the field value in the index. 原文不存儲(chǔ)在索引文件中,搜索結(jié)果命中后,再根據(jù)其他附加屬性如文件的Path,數(shù)據(jù)庫的主鍵等,重新連接打開原文,適合原文內(nèi)容較大的情況。
決定了Field對(duì)象的 this.isStored 和 this.isCompressed
------------------------------------
Index.NO Do not index the field value. This field can thus not be searched, but one can still access its contents provided it is Field.Store stored. 不進(jìn)行索引,存放不能被搜索的內(nèi)容如文檔的一些附加屬性如文檔類型, URL等。
Index.TOKENIZED Index the field's value so it can be searched. An Analyzer will be used to tokenize and possibly further normalize the text before its terms will be stored in the index. This is useful for common text. 分詞索引
Index.UN_TOKENIZED Index the field's value without using an Analyzer, so it can be searched. As no analyzer is used the value will be stored as a single term. This is useful for unique Ids like product numbers. 不分詞進(jìn)行索引,如作者名,日期等,Rod Johnson本身為一單詞,不再需要分詞。
Index.NO_NORMS 不分詞,建索引。norms是什么???字段值???。但是Field的值不像通常那樣被保存,而是只取一個(gè)byte,這樣節(jié)約存儲(chǔ)空間???? Index the field's value without an Analyzer, and disable the storing of norms. No norms means that index-time boosting and field length normalization will be disabled. The benefit is less memory usage as norms take up one byte per indexed field for every document in the index.Note that once you index a given field <i>with</i> norms enabled, disabling norms will have no effect. In other words, for NO_NORMS to have the above described effect on a field, all instances of that field must be indexed with NO_NORMS from the beginning.
決定了Field對(duì)象的 this.isIndexed this.isTokenized this.omitNorms
------------------------------------
Lucene 1.4.3新增的:
TermVector.NO Do not store term vectors. 不保存term vectors
TermVector.YES Store the term vectors of each document. A term vector is a list of the document's terms and their number of occurences in that document. 保存term vectors。
TermVector.WITH_POSITIONS Store the term vector + token position information 保存term vectors。(保存值和token位置信息)
TermVector.WITH_OFFSETS Store the term vector + Token offset information
TermVector.WITH_POSITIONS_OFFSETS Store the term vector + Token position and offset information 保存term vectors。(保存值和Token的offset)
決定了Field對(duì)象的this.storeTermVector this.storePositionWithTermVector this.storeOffsetWithTermVector
以下內(nèi)容均為轉(zhuǎn)載,url見具體鏈接:
最常見的四個(gè)Analyzer,說明: http://windshowzbf.bokee.com/3016397.html
WhitespaceAnalyzer 僅僅是去除空格,對(duì)字符沒有l(wèi)owcase化,不支持中文
SimpleAnalyzer :功能強(qiáng)于WhitespaceAnalyzer,將除去letter之外的符號(hào)全部過濾掉,并且將所有的字符lowcase化,不支持中文
StopAnalyzer: StopAnalyzer的功能超越了SimpleAnalyzer,在SimpleAnalyzer的基礎(chǔ)上.增加了去除StopWords的功能,不支持中文.類中使用一個(gè)static數(shù)組保存了ENGLISH_STOP_WORDS, 太常見不index的words
StandardAnalyzer: 用Javacc定義的一套EBNF,嚴(yán)禁的語法。有人說英文的處理能力同于StopAnalyzer.支持中文采用的方法為單字切分。未仔細(xì)比較,不敢確定。
其他的擴(kuò)展:
ChineseAnalyzer:來自于Lucene的sand box.性能類似于StandardAnalyzer,缺點(diǎn)是不支持中英文混和分詞.
CJKAnalyzer:chedong寫的CJKAnalyzer的功能在英文處理上的功能和StandardAnalyzer相同.但是在漢語的分詞上,不能過濾掉標(biāo)點(diǎn)符號(hào),即使用二元切分
TjuChineseAnalyzer: http://windshowzbf.bokee.com/3016397.html寫的,功能最為強(qiáng)大.TjuChineseAnlyzer的功能相當(dāng)強(qiáng)大,在中文分詞方面由于其調(diào)用的為ICTCLAS的java接口.所以其在中文方面性能上同與ICTCLAS.其在英文分詞上采用了Lucene的StopAnalyzer,可以去除 stopWords,而且可以不區(qū)分大小寫,過濾掉各類標(biāo)點(diǎn)符號(hào).
例子:
http://www.langtech.org.cn/index.php/uid-5080-action-viewspace-itemid-68, 還有簡單的代碼分析
Analyzing "The quick brown fox jumped over the lazy dogs"
WhitespaceAnalyzer:
[The] [quick] [brown] [fox] [jumped] [over] [the] [lazy] [dogs]
SimpleAnalyzer:
[the] [quick] [brown] [fox] [jumped] [over] [the] [lazy] [dogs]
StopAnalyzer:
[quick] [brown] [fox] [jumped] [over] [lazy] [dogs]
StandardAnalyzer:
[quick] [brown] [fox] [jumped] [over] [lazy] [dogs]
Analyzing "XY&Z Corporation - xyz@example.com"
WhitespaceAnalyzer:
[XY&Z] [Corporation] [-] [xyz@example.com]
SimpleAnalyzer:
[xy] [z] [corporation] [xyz] [example] [com]
StopAnalyzer:
[xy] [z] [corporation] [xyz] [example] [com]
StandardAnalyzer:
[xy&z] [corporation] [xyz@example.com]
參考連接:
http://macrochen.blogdriver.com/macrochen/1167942.html
http://macrochen.blogdriver.com/macrochen/1153507.html
http://my.dmresearch.net/bbs/viewthread.php?tid=8318
http://windshowzbf.bokee.com/3016397.html
推薦
http://gceclub.sun.com.cn/developer/technicalArticles/Intl/Supplementary/index_zh_CN.html
http://www.linuxpk.com/3821.html
=======================================
BMP的解釋:
http://zh.wikipedia.org/w/index.php?title=%E5%9F%BA%E6%9C%AC%E5%A4%9A%E6%96%87%E7%A8%AE%E5%B9%B3%E9%9D%A2&variant=zh-cn
http://zh.wikipedia.org/w/index.php?title=%E8%BE%85%E5%8A%A9%E5%B9%B3%E9%9D%A2&variant=zh-cn#.E7.AC.AC.E4.B8.80.E8.BC.94.E5.8A.A9.E5.B9.B3.E9.9D.A2
1個(gè)BMP和16個(gè)輔助plane,需要21個(gè)bits.
======================================
ISO-10646與Unicode關(guān)系
http://zh.wikipedia.org/wiki/%E9%80%9A%E7%94%A8%E5%AD%97%E7%AC%A6%E9%9B%86
ISO-10646術(shù)語
|
Unicode術(shù)語
|
UCS-2 |
BMP UTF-16 |
UCS-4 |
UTF-32 |
|
|
注意:UTF-16可看成是UCS-2的 父集。在沒有輔助平面字符前,UTF-16與UCS-2所指的是同一的意思。但當(dāng)引入輔助平面字符後,就只稱為UTF-16了,因?yàn)槲覀儠?huì)使用2個(gè)UTF-16,也就似乎4bytes保存一個(gè)輔助平面字符。現(xiàn)在若有軟件聲稱自己支援UCS-2編碼,那其實(shí)是暗指它不能支援輔助平面字符的委婉語。
======================================
UTF-8要完整表達(dá)unicode需要4bytes,表達(dá)BMP需要3bytes,見 http://en.wikipedia.org/wiki/UTF-8,注意“The range D800-DFFF is disallowed by Unicode. The encoding scheme reliably transforms values in that range, but they are not valid scalar values in Unicode. See Table 3-7 in the Unicode 5.0 standard. ”
======================================
BOM Byte Order Mark,在UCS編碼中有一個(gè)叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的編碼是FEFF。而FFFE在UCS中是不存在的字符,所以不應(yīng)該出現(xiàn)在實(shí)際傳輸中。UCS規(guī)范建議我們?cè)趥鬏斪止?jié)流前,先傳輸字符"ZERO WIDTH NO-BREAK SPACE"。
這樣如果接收者收到FEFF,就表明這個(gè)字節(jié)流是Big-Endian的;如果收到FFFE,就表明這個(gè)字節(jié)流是Little-Endian的。
字符"ZERO WIDTH NO-BREAK SPACE"又被稱作BOM。UTF-8不需要BOM來表明字節(jié)順序,但可以用BOM來表明編碼方式。字符"ZERO WIDTH NO-BREAK SPACE"(也就是U+FEFF)的UTF-8編碼是EF BB BF(就是11101111,10111011,10111111)。所以如果接收者收到以EF BB BF開頭的字節(jié)流,就知道這是UTF-8編碼了。
Windows就是使用BOM來標(biāo)記文本文件的編碼方式的。
除了FEFF,英文wiki http://en.wikipedia.org/wiki/UTF-8還解釋說明了一些目前不會(huì)出現(xiàn)在utf-8字節(jié)流中的byte值。
=========================================
Java
http://www.jorendorff.com/articles/unicode/java.html
http://gceclub.sun.com.cn/developer/technicalArticles/Intl/Supplementary/index_zh_CN.html 完美解釋java中的unicode。另外提到j(luò)ava中utf-8其實(shí)有兩種格式,分別是標(biāo)準(zhǔn)utf-8和改良utf-8。對(duì)于文本輸入, Java 2 SDK 提供用于接受“\Uxxxxxx”格式字符串的代碼點(diǎn)輸入方法,這里大寫的“U”表示轉(zhuǎn)義序列包含六個(gè)十六進(jìn)制數(shù)字,因此允許使用增補(bǔ)字符。小寫的“u”表示轉(zhuǎn)義序列“\uxxxx”的原始格式。
http://dlog.cn/html/diary/showlog.vm?sid=2&cat_id=-1&log_id=557 介紹了String的JDK5新增方法
http://blog.csdn.net/qinysong/archive/2006/09/05/1179480.aspx 連著三篇用實(shí)例說明,語言比較亂,說的也不盡正確,但他用了做試驗(yàn)的java代碼有點(diǎn)意思,能幫助思考代碼中一些tricky的現(xiàn)象。
http://topic.csdn.net/u/20070928/22/5207088c-c47d-43ed-8416-26f850631cff.html 有一些回答,
http://topic.csdn.net/u/20070515/14/57af3319-28de-4851-b4cf-db65b2ead01c.html 有些試驗(yàn)代碼,價(jià)值不大
http://www.w3china.org/blog/more.asp?name=hongrui&id=24817 有些java實(shí)例代碼,沒細(xì)看。
另:
Java 1.0 supports Unicode version 1.1.
Java 1.1 onwards supports Unicode version 2.0.
J2SE 1.4中的字符處理是基于Unicode 3.0標(biāo)準(zhǔn)的。
J2SE v 1.5 supports Unicode 4.0 character set.
而:
Unicode 3.0:1999年九月;涵蓋了來自ISO 10646-1的十六位元通用字元集(UCS)基本多文種平面(Basic Multilingual Plane)
Unicode 3.1:2001年三月;新增從ISO 10646-2定義的輔助平面(Supplementary Planes)
所以:
代碼點(diǎn)在U+0000到U+FFFF之間的就用\u0000到\uffff表示
U+10000到U+1FFFF之間的用 \ud800到\udbff中的作為第一個(gè)單元, 用\udc00到\udfff作為第二單元,組合起來表示
char這個(gè)概念就是指\u0000到\uffff,這是占兩個(gè)字節(jié)
其余的用code point這個(gè)概念
JDK 1.5 以上支持 Unicode 4.0,也就是 Unicode 的范圍是 U+0000~U+10FFFF,
超過 U+FFFF 的字符采用代碼點(diǎn)(也就是 int 類型的數(shù)據(jù))來表示,具體的可以
參考一下下面這個(gè)鏈接的文章《Java 平臺(tái)中的增補(bǔ)字符》,對(duì)此作了很詳細(xì)的介
紹。 http://gceclub.sun.com.cn/developer/technicalArticles/Intl/Supplementary/index_zh_CN.html
================================
http://m.tkk7.com/tim-wu/archive/2007/09/12/144550.html
================================
U-00000000 - U-0000007F: |
0xxxxxxx |
U-00000080 - U-000007FF: |
110xxxxx 10xxxxxx |
U-00000800 - U-0000FFFF: |
1110xxxx 10xxxxxx 10xxxxxx |
U-00010000 - U-001FFFFF: |
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
U-00200000 - U-03FFFFFF: |
111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx |
U-04000000 - U-7FFFFFFF: |
1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx |
但目前ISO和Unicode組織都不會(huì)規(guī)定10FFFF以上的字符
代碼範(fàn)圍
十六進(jìn)制 |
標(biāo)量值(scalar value
二進(jìn)制 |
UTF-8
二進(jìn)制 / 十六進(jìn)制 |
註釋 |
000000 - 00007F
128個(gè)代碼 |
00000000 00000000 0zzzzzzz |
0zzzzzzz(00-7F) |
ASCII等值範(fàn)圍,位元組由零開始 |
七個(gè)z |
七個(gè)z |
000080 - 0007FF
1920個(gè)代碼 |
00000000 00000yyy yyzzzzzz |
110yyyyy(C2-DF) 10zzzzzz(80-BF) |
第一個(gè)位元組由110開始,接著的位元組由10開始 |
三個(gè)y;二個(gè)y;六個(gè)z |
五個(gè)y;六個(gè)z |
000800 - 00FFFF
63488個(gè)代碼 |
00000000 xxxxyyyy yyzzzzzz |
1110xxxx(E0-EF) 10yyyyyy 10zzzzzz |
第一個(gè)位元組由1110開始,接著的位元組由10開始 |
四個(gè)x;四個(gè)y;二個(gè)y;六個(gè)z |
四個(gè)x;六個(gè)y;六個(gè)z |
010000 - 10FFFF
1048576個(gè)代碼 |
000wwwxx xxxxyyyy yyzzzzzz |
11110www(F0-F4) 10xxxxxx 10yyyyyy 10zzzzzz |
由11110開始,接著的位元組由10開始 |
三個(gè)w;二個(gè)x;四個(gè)x;四個(gè)y;二個(gè)y;六個(gè)z |
三個(gè)w;六個(gè)x;六個(gè)y;六個(gè)z |
================================
參考:http://blog.csdn.net/qinysong/archive/2006/09/05/1179480.aspx,但該文對(duì)unicode版本說明有誤,說明見上
在大約 1993 年之后開發(fā)的大多數(shù)現(xiàn)代編程語言都有一個(gè)特別的數(shù)據(jù)類型, 叫做 Unicode/ISO 10646-1 字符. 在 Ada95 中叫 Wide_Character, 在 Java 中叫 char.
ISO C 也詳細(xì)說明了處理多字節(jié)編碼和寬字符 (wide characters) 的機(jī)制, 1994 年 9 月 Amendment 1 to ISO C 發(fā)表時(shí)又加入了更多. 這些機(jī)制主要是為各類東亞編碼而設(shè)計(jì)的, 它們比處理 UCS 所需的要健壯得多. UTF-8 是 ISO C 標(biāo)準(zhǔn)調(diào)用多字節(jié)字符串的編碼的一個(gè)例子, wchar_t 類型可以用來存放 Unicode 字符.
代碼為QueryParser.jj,語法為JavaCC實(shí)現(xiàn)的LL():
完整文檔:http://lucene.apache.org/java/2_0_0/queryparsersyntax.html
和正則一樣:
?表示0個(gè)或1個(gè)
+表示一個(gè)或多個(gè)
*表示0個(gè)或多個(gè)
以下是Token部分:
_NUM_CHAR::=["0"-"9"] //數(shù)字
_ESCAPED_CHAR::= "\\" [ "\\", "+", "-", "!", "(", ")", ":", "^", "[", "]", "\"", "{", "}", "~", "*", "?" ] > //特殊字符,
_TERM_START_CHAR ::=( ~[ " ", "\t", "\n", "\r", "+", "-", "!", "(", ")", ":", "^","[", "]", "\"", "{", "}", "~", "*", "?" ] //TERM的起始字符,除了列出的其它字符都可以
_TERM_CHAR::=( <_TERM_START_CHAR> | <_ESCAPED_CHAR> | "-" | "+" ) > //TERM可使用字符
_WHITESPACE::= ( " " | "\t" | "\n" | "\r") //空格和回車,
<DEFAULT> TOKEN:
AND::=("AND" | "&&")
OR::=("OR" | "||")
NOT::=("NOT" | "!")
PLUS::="+"
MINUS::="-"
LPAREN::="("
RPAREN::=")"
COLON::=":"
STAR::="*"
CARAT::="^" //后接Boost,原文<CARAT: "^" > : Boost,后面Boost說明什么沒明白
QUOTED::="\"" (~["\""] | "\\\"")+ "\"" // 表示用"包起來的字符串,字符"開始,中間由不是"的符號(hào)或者連著的這兩個(gè)符號(hào)\"組成,字符"結(jié)束,
TERM::=<_TERM_START_CHAR> (<_TERM_CHAR>)*
FUZZY_SLOP::="~" ( (<_NUM_CHAR>)+ ( "." (<_NUM_CHAR>)+ )? )? //字符~開始,而后是數(shù)字.Lucene支持模糊查詢,例如"roam~"或"roam~0.8",The value is between 0 and 1,算法為the Levenshtein Distance, or Edit Distance algorithm
PREFIXTERM::=(<_TERM_START_CHAR> | "*") (<_TERM_CHAR>)* "*" > //模糊查找,表示以某某開頭的查詢, 字符表示為"something*",前綴允許模糊符號(hào)*,中間可有字符也可沒有, 結(jié)尾必須是*
WILDTERM::=(<_TERM_START_CHAR> | [ "*", "?" ]) (<_TERM_CHAR> | ( [ "*", "?" ] ))* > //類似上面,但同時(shí)支持?字符,結(jié)尾可以是字符也可以是* ?。使用[]表示or關(guān)系時(shí),不需要使用|,只要,號(hào)分割即可
RANGEIN_START::="[" //在RangeQuery中,[或{表示了是否包含邊界條件本身, 用字符表示為"[begin TO end]" 或者"{begin TO end}",后接RangeIn
RANGEEX_START::="{" //同上,后接RangeEx
<Boost> TOKEN:
NUMBER::=(<_NUM_CHAR>)+ ( "." (<_NUM_CHAR>)+ )? //后接DEFAULT, 整數(shù)或小數(shù)
<RangeIn> TOKEN:
RANGEIN_TO::="TO"
RANGEIN_END::="]" //后接DEFAULT, RangIn的結(jié)束
RANGEIN_QUOTED::= "\"" (~["\""] | "\\\"")+ "\"" //同上述QUOTED,表示用"包起來的字符串,
RANGEIN_GOOP::= (~[ " ", "]" ])+ //1個(gè)或多個(gè)不是空格和]的符號(hào),這樣就能提取出[]中的內(nèi)容
<RangeEx> TOKEN :
RANGEEX_TO::="TO">
RANGEEX_END::="}" //后接DEFAULT, RangeEx的結(jié)束
RANGEEX_QUOTED::="\"" (~["\""] | "\\\"")+ "\"" //同上述QUOTED,表示用"包起來的字符串,
RANGEEX_GOOP::=(~[ " ", "}" ])+ //1個(gè)或多個(gè)不是空格和]的符號(hào),這樣就能提取出[]中的內(nèi)容
<DEFAULT, RangeIn, RangeEx> SKIP : {
< <_WHITESPACE>>
} //所有空格和回車被忽略
以下為解析部分
Conjunction::=[ <AND> { ret = CONJ_AND; } | <OR> { ret = CONJ_OR; } ] //連接
Modifiers::=[ <PLUS> { ret = MOD_REQ; } | <MINUS> { ret = MOD_NOT; } | <NOT> { ret = MOD_NOT; } ] //+ - !符號(hào)
Query::=Modifiers Clause (Conjunction Modifiers Clause)*
Clause::=[(<TERM> <COLON>|<STAR> <COLON>)] //btw:代碼中LOOKAHEAD[2]表示使用LL(2)
(Term|<LPAREN> Query <RPAREN> (<CARAT> <NUMBER>)?) //子句. ???????這兒語法有點(diǎn),仿佛允許 *:(*:dog)這樣的語法,很奇怪
Term::=(
(<TERM>|<STAR>|<PREFIXTERM>|<WILDTERM>|<NUMBER>) [<FUZZY_SLOP>] [<CARAT><NUMBER>[<FUZZY_SLOP>]}
| ( <RANGEIN_START> (<RANGEIN_GOOP>|<RANGEIN_QUOTED>) [ <RANGEIN_TO> ] (<RANGEIN_GOOP>|<RANGEIN_QUOTED> <RANGEIN_END> ) [ <CARAT> boost=<NUMBER> ] //這兒看出range必須同時(shí)有兩端,不能只有有一端
| ( <RANGEEX_START> <RANGEEX_GOOP>|<RANGEEX_QUOTED> [ <RANGEEX_TO> ] <RANGEEX_GOOP>|<RANGEEX_QUOTED> <RANGEEX_END> )[ <CARAT> boost=<NUMBER> ] //在RangeQuery中,[或{表示了是否包含邊界條件本身, 用字符表示為"[begin TO end]" 或者"{begin TO end}",后接RangeIn
| <QUOTED> [ <FUZZY_SLOP> ] [ <CARAT> boost=<NUMBER> ] //被""包含的內(nèi)容
btw: 猜測(cè): javacc中,如果使用[],則允許出現(xiàn)0次或1次
今天讀了lucent中的PriorityQueue.java, 一個(gè)很巧妙的復(fù)雜度為log(n)的排序堆棧.
始終確保數(shù)組A[1...n]中:
A[i]<A[2*i] & A[i] < A[2*i +1]
很容易推論出A[1]一定是最小數(shù)值, 并且每次put()和pop()至多移動(dòng)log(n)個(gè)數(shù)值
真是好久沒接觸算法了:)
ruby.exe提供了一個(gè)參數(shù)-r, 允許ruby在允許之前加載你指定的庫
1 如果你安裝了gem,那么環(huán)境變量RUBYOPT將為-rubygems,這個(gè)參數(shù)說明了ruby將提前加載ubygem.rb(注意,沒有r,不是rubygem.rb,而是ubygem:)
2 這時(shí),如果你運(yùn)行 ruby -e "puts $:",可以查看到ruby查詢lib庫的目錄順序,其中第一個(gè)就是類似"..\ruby\site_ruby\1.8"目錄
3 因此,ubygem.rb將在ruby\site_ruby\1.8\ubygems.rb位置中被ruby定位到,而ubygem.rb只有一句話require 'rubygems',這次才真正調(diào)用了rubygems.rb
4 接著rubygems.rb的最后一句require 'rubygems/custom_require'將加載custom_require.rb
5 最后custom_require.rb中替換了原先的require()函數(shù)的實(shí)現(xiàn),這之后,庫的加載,將遵循gem的目錄約定。
記得上學(xué)時(shí)學(xué)數(shù)據(jù)庫,書中說過:“數(shù)據(jù)庫建模一般實(shí)現(xiàn)到3NF和BCNF,4NF 5NF基本沒用”,造成多年對(duì)4NF和5NF置之不理。
離開學(xué)校7年后的今天,無事有把數(shù)據(jù)庫的范式定義拿起來翻翻,發(fā)覺好象我對(duì)4NF和5NF的理解一直有誤?
在做業(yè)務(wù)建模時(shí),不少情況我們會(huì)盡可能地完全反映顯示業(yè)務(wù)關(guān)系,以 廠 采購員 和 訂單 為例:
如果業(yè)務(wù)認(rèn)為訂單不屬于特定采購員,也即關(guān)系如下:
廠和訂單為1:n的關(guān)系;
廠和采購員也為1:n的關(guān)系;
采購員與訂單無關(guān)。
此時(shí),那么我們肯定ORM簡單得處理為兩個(gè)關(guān)聯(lián)表,這時(shí),不正是符合了4NF么?(如果只建立一個(gè)關(guān)聯(lián)表,表中三個(gè)字段廠id,訂單id,采購員id,而后三個(gè)id組成聯(lián)合主鍵,那就是符合了BCNF但不符合4NF了)
而如果關(guān)系更復(fù)雜,一個(gè)訂單可以被多個(gè)采購員處理,一個(gè)訂單還可以同屬于多個(gè)廠共享,那么我們一定是建立三個(gè)關(guān)聯(lián)表,獨(dú)立記錄三者間互相的關(guān)系,這不就是遵循了5NF么?
今天注冊(cè)了個(gè)Google Code的項(xiàng)目,還是google code爽啊,夠簡單,
http://code.google.com/hosting/createProject 注冊(cè)一下就成功,不用得著什么審核
svn速度爆快,相比sf.net就是個(gè)牛車。
其它沒試,不評(píng)論,這兩點(diǎn)就足夠了。
不知道rubyforge怎么樣。
 final byte[] addBuffer(int size) {
byte[] buffer = new byte[size];
if (directory!=null)
 synchronized (directory) { // Ensure addition of buffer and adjustment to directory size are atomic wrt directory
buffers.add(buffer);
directory.sizeInBytes += size;
sizeInBytes += size;
}
else
buffers.add(buffer);
return buffer;
}
今天讀了 http://infolab.stanford.edu/~backrub/google.html 一文,發(fā)現(xiàn)我畢業(yè)那年(2000年)google已有如此成果。
PageRank or PR(A) can be calculated using a simple iterative algorithm, and corresponds to the principal eigenvector of the normalized link matrix of the web. Also, a PageRank for 26 million web pages can be computed in a few hours on a medium size workstation.
最酷的就是這句話了,真酷的algorithm啊,我想破頭想不出頭緒來達(dá)到這個(gè)效率。
呵呵,從wikipedia開始,發(fā)現(xiàn)很有意思的文章越來越多:)
http://en.wikipedia.org/wiki/HITS_algorithm
今天翻了util包,發(fā)現(xiàn)自己對(duì)float的匱乏知識(shí),好在有偉大的wiki百科:
http://en.wikipedia.org/wiki/IEEE_754
很清楚得解釋了IEEE 754規(guī)范
可惜看不懂lucene對(duì)small float的結(jié)構(gòu)定義。
如果按我的理解,似乎類SmallFloat的函數(shù)byteToFloat()和byte315ToFloat()有bug,通過編寫測(cè)試代碼:
SmallFloat.byte315ToFloat(Byte.parseByte("01111000", 2)得到的float值為0.5f,
但按照byte315ToFloat()函數(shù)的說明,"01111000"的mantissaBits尾數(shù)長度為3, zeroExponent為15,無負(fù)數(shù),
而01111按IEEE 754規(guī)范,應(yīng)該理解為1,所以01111000表達(dá)的數(shù)值應(yīng)該為1.0×2^0=1,而不是0.5f
非常之tricky。
通過讀byte315ToFloat()函數(shù)實(shí)現(xiàn),發(fā)現(xiàn)"01111000"轉(zhuǎn)換為32位值0,01111110,00000000000000000000000,
該值按IEEE 754規(guī)范的確為0.5f, 其中正負(fù)位為1位,指數(shù)(exponent)位為8位,尾數(shù)(mantissa)位為23位。
而在byteToFloat()和byte315ToFloat()的實(shí)現(xiàn)中,我們可以常看到作者將尾數(shù)位偏移24-mantissa位,
也就是說,他理解的IEEE 754規(guī)范中尾數(shù)位不是23位而是24位。
以上僅為我的猜測(cè),因?yàn)檫€沒有看到byteToFloat()的具體使用環(huán)境。
而且通過測(cè)試代碼,發(fā)現(xiàn)byte和float對(duì)應(yīng)關(guān)系:
10000,000==2.0f= 1*2^1
10001,000==8.0f=1*2^3
10010,000==32.0f=1*2^5
01111,000 = 0.5f = 1*2^-1
01110,000==0.125f= 1* 2^-3
完全看不出合理的small float的結(jié)構(gòu),tricky!!!
因?yàn)楣ぷ餍枰裉扉_始學(xué)習(xí)lucene,
因?yàn)橛锌赡芤院笠钊胙芯縡ull text的搜索,決定暫時(shí)不看in action,
采用最愚笨的方法,直接爬代碼。憑借以前的門外漢理解一步一步爬吧,
不知道還能衍生出多少知識(shí)。
swt中,默認(rèn)只有用戶線程被允許訪問UI圖形控件和一些圖形API,其他非用戶線程如果直接訪問這類資源時(shí),SWTException直接被拋出。 如果真有這種需求,必須使用*.widget.Display類中的兩個(gè)線程同步方法:syncExec(Runnable)和asyncExec(Runnable)。方法syncExec()和asyncExec()的區(qū)別在于前者要在指定的線程執(zhí)行結(jié)束后才返回,而后者則無論指定的線程是否執(zhí)行都會(huì)立即返回到當(dāng)前線程。
例子: Display.getCurrent().asyncExec(new Runnable() { public void run() { butt.setText("Push"); } });
以下載自某論壇(sorry,忘記了出處),關(guān)于內(nèi)存釋放:
Rule 1: If you created it, you dispose it. Rule 2: Disposing the parent disposes the children. (from http://www.eclipse.org/articles/swt-design-2/swt-design-2.html)
當(dāng)使用構(gòu)造函數(shù)來創(chuàng)建小窗口或圖形對(duì)象時(shí),使用完時(shí)必須用手工來除掉它。 如果沒有使用構(gòu)造函數(shù)就獲取小窗口或圖形對(duì)象,則一定不能用手工來除掉它,因?yàn)槟捶峙渌? 如果將對(duì)小窗口或圖形對(duì)象的引用傳送至另一個(gè)對(duì)象,則一定要小心,仍在使用它時(shí)一定不要除掉它。與在使用圖像的插件模式中所描述的規(guī)則相似。) 當(dāng)用戶關(guān)閉Shell時(shí),將遞歸地銷毀 shell 及其所有子代小窗口。在此情況下,不需要除掉小窗口本身。然而,必須釋放與那些小窗口一起分配的所有圖形資源。 如果創(chuàng)建圖形對(duì)象以便在其中一個(gè)小窗口的生命周期內(nèi)使用它,則在除掉小窗口時(shí)必須除掉圖形對(duì)象。這可以通過向小窗口注冊(cè)銷毀偵聽器,并在接收到銷毀事件時(shí)釋放圖形對(duì)象來實(shí)現(xiàn)。
這些規(guī)則有一個(gè)例外。簡單的數(shù)據(jù)對(duì)象(例如,矩形和點(diǎn))不使用操作系統(tǒng)資源。它們沒有 dispose() 方法,您也不需要釋放它們。如果有疑問,則檢查特定類的 javadoc。
?
第一種方式,zkjbeyond的文章中有很詳細(xì)的說明http://m.tkk7.com/zkjbeyond/archive/2006/05/05/44676.html 簡單說,就是利用Ajax讀取包的.js文件后,直接使用eval()運(yùn)行代碼。
除此外,dojo還有另一種加載方式,用在調(diào)試階段(例如使用firebug活Venkman調(diào)試dojo時(shí)都需要用到,不過我沒試過使用MSE7或Visual Studio時(shí)是否需要使用這種加載方式)。
你在加載包前,將djConfig.debugAtAllCosts設(shè)置為true,那么包的加載方式會(huì)有所變化,采用第二種方式。例如:
djConfig.debugAtAllCosts=true; dojo.require("dojo.widget.TimBar"); dojo.hostenv.writeIncludes();
此時(shí),dojo會(huì)使用加載brower_debug.js文件, brower_debug.js重載了函數(shù)dojo.hostenv.loadUri,這兒,不再直接使用eval()運(yùn)行,而只是把需要的包push到dojo.hostenv.loadedUris數(shù)組中。實(shí)現(xiàn)代碼:
var text = this.getText(uri, null, true); var requires = dojo.hostenv.getRequiresAndProvides(text); eval(requires.join(";")); dojo.hostenv.loadedUris.push(uri); dojo.hostenv.loadedUris[uri] = true; var delayRequires = dojo.hostenv.getDelayRequiresAndProvides(text); eval(delayRequires.join(";"));
從代碼可以看出,dojo為了保證加載的順序,使用dojo.hostenv.getRequiresAndProvides函數(shù)將提前依賴的包摳出來,用遞歸提前push到dojo.hostenv.loadedUris中。 而后,在代碼最后,我們只要主動(dòng)使用dojo.hostenv.writeIncludes();函數(shù),就可一次性將被依賴的包利用document.write包含進(jìn)來。
btw: 當(dāng)然,這種方法有不少缺陷: 1、使用document.write方式加載包,會(huì)有延遲效應(yīng),會(huì)等待本頁面中代碼都執(zhí)行完才執(zhí)行包中的代碼,自己編碼時(shí)有點(diǎn)繞,影響可讀性; 2、從上面代碼中看出,dojo會(huì)有更多的IO操作,第一dojo已經(jīng)完整讀取了包代碼,但是不馬上執(zhí)行,只是用來分析依賴關(guān)系;之后,瀏覽器又要加載一次包代碼。 3、包絕對(duì)不允許循環(huán)引用。當(dāng)然,從OO角度,不循環(huán)引用是好的方式
所以,這種方式只能用在調(diào)試期間
摘要: 常規(guī)循環(huán)引用內(nèi)存泄漏和Closure內(nèi)存泄漏
要了解javascript的內(nèi)存泄漏問題,首先要了解的就是javascript的GC原理。
我記得原來在犀牛書《JavaScript: The Definitive
Guide》中看到過,IE使用的GC算法是計(jì)數(shù)器,因此只碰到循環(huán) 引用就會(huì)造成memory
leakage。后來一直覺得和觀察到的現(xiàn)象很不一致,直到看到Eric的文... 閱讀全文
今天發(fā)現(xiàn)偶的MapEasy的demo中有個(gè)bug,查了半天發(fā)現(xiàn)了一個(gè)在IE 6下特別古怪的問題:
使用IE6,這個(gè)頁面無法正確顯示 http://mapeasy.sourceforge.net/demo/mapapi0.4alpha20060510/demo2_google_amuse.html
而這個(gè)可以 http://mapeasy.sourceforge.net/demo/mapapi0.4alpha20060510/demo2_google.html
而這兩個(gè)頁面的唯一區(qū)別就是:第一個(gè)頁面中有一個(gè)函數(shù)定義的第一行注釋中,帶上了"層"這個(gè)字。 如果去了“層”這個(gè)字,則一切ok。
另外,這兩個(gè)頁面的編碼均使用utf8
hoho,IE的bug真是多而古怪了,以后函數(shù)的第一行,偶再也不敢寫注釋了..............
|
|
|
| 日 | 一 | 二 | 三 | 四 | 五 | 六 |
---|
30 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|
導(dǎo)航
統(tǒng)計(jì)
- 隨筆: 28
- 文章: 0
- 評(píng)論: 38
- 引用: 1
常用鏈接
留言簿(4)
我參與的團(tuán)隊(duì)
隨筆檔案
搜索
最新評(píng)論

閱讀排行榜
評(píng)論排行榜
|
|