构建实时索引

所谓事务性,本多指数据库的属性,包括ACID四个基本要素:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。

我们这里主要讨论隔离性,Lucene的IndexReader和IndexWriter具有隔离性。

  • 当IndexReader.open打开一个索引的时候,相对于给当前索引进行了一次snapshot,此后的任何修改都不会被看到。
  • 仅当IndexReader.open打开一个索引后,才有可能看到从上次打开后对索引的修改。
  • 当IndexWriter没有调用Commit的时候,其修改的内容是不能够被看到的,哪怕IndexReader被重新打开。
  • 欲使最新的修改被看到,一方面IndexWriter需要commit,一方面IndexReader重新打开。

下面我们举几个例子来说明上述隔离性:

(1) 首先做准备,索引十篇文档

File indexDir = new File("TestIsolation/index");

IndexWriter writer = new IndexWriter(FSDirectory.open(indexDir), new StandardAnalyzer(Version.LUCENE_CURRENT), true, IndexWriter.MaxFieldLength.LIMITED);

for(int i =0; i < 10; i++){

  indexDocs(writer);

}

writer.close();

(2) 然后再索引十篇文档,并不commit

writer = new IndexWriter(FSDirectory.open(indexDir), new StandardAnalyzer(Version.LUCENE_CURRENT), IndexWriter.MaxFieldLength.LIMITED);

for(int i =0; i < 10; i++){

  indexDocs(writer);

}

(3) 打开一个IndexReader,但是由于IndexWriter没有commit,所以仍然仅看到十篇文档。

IndexReader reader = IndexReader.open(FSDirectory.open(indexDir));

IndexSearcher searcher = new IndexSearcher(reader);

TopDocs docs = searcher.search(new TermQuery(new Term("contents","hello")), 50);

System.out.println(docs.totalHits);

(4) IndexWriter进行提交commit

writer.commit();

(5) 不重新打开IndexReader,进行搜索,仍然仅看到十篇文档。

docs = searcher.search(new TermQuery(new Term("contents","hello")), 50);

System.out.println(docs.totalHits);

(6) IndexReader重新打开,则可以看到二十篇文档。

reader = IndexReader.open(FSDirectory.open(indexDir));

searcher = new IndexSearcher(reader);

docs = searcher.search(new TermQuery(new Term("contents","hello")), 50);

System.out.println(docs.totalHits);

由于前一章所述的Lucene的事务性,使得Lucene可以增量的添加一个段,我们知道,倒排索引是有一定的格式的,而这个格式一旦写入是非常难以改变的,那么如何能够增量建索引呢?Lucene使用段这个概念解决了这个问题,对于每个已经生成的段,其倒排索引结构不会再改变,而增量添加的文档添加到新的段中,段之间在一定的时刻进行合并,从而形成新的倒排索引结构。

然而也正因为Lucene的事务性,使得Lucene的索引不够实时,如果想Lucene实时,则必须新添加的文档后IndexWriter需要commit,在搜索的时候IndexReader需要重新的打开,然而当索引在硬盘上的时候,尤其是索引非常大的时候,IndexWriter的commit操作和IndexReader的open操作都是非常慢的,根本达不到实时性的需要。

好在Lucene提供了RAMDirectory,也即内存中的索引,能够很快的commit和open,然而又存在如果索引很大,内存中不能够放下的问题。

所以要构建实时的索引,就需要内存中的索引RAMDirectory和硬盘上的索引FSDirectory相互配合来解决问题。

1、初始化阶段

首先假设我们硬盘上已经有一个索引FileSystemIndex,由于IndexReader打开此索引非常的慢,因而其是需要事先打开的,并且不会时常的重新打开。

我们在内存中有一个索引MemoryIndex,新来的文档全部索引到内存索引中,并且是索引完IndexWriter就commit,IndexReader就重新打开,这两个操作时非常快的。

如下图,则此时新索引的文档全部能被用户看到,达到实时的目的。

点击查看原图

2、合并索引阶段

然而经过一段时间,内存中的索引会比较大了,如果不合并到硬盘上,则可能造成内存不够用,则需要进行合并的过程。

当然在合并的过程中,我们依然想让我们的搜索是实时的,这是就需要一个过渡的索引,我们称为MergingIndex。

一旦内存索引达到一定的程度,则我们重新建立一个空的内存索引,用于合并阶段索引新的文档,然后将原来的内存索引称为合并中索引,并启动一个后台线程进行合并的操作。

在合并的过程中,如果有查询过来,则需要三个IndexReader,一个是内存索引的IndexReader打开,这个过程是很快的,一个是合并中索引的IndexReader打开,这个过程也是很快的,一个是已经打开的硬盘索引的IndexReader,无需重新打开。这三个IndexReader可以覆盖所有的文档,唯一有可能重复的是,硬盘索引中已经有一些从合并中索引合并过去的文档了,然而不用担心,根据Lucene的事务性,在硬盘索引的IndexReader没有重新打开的情况下,背后的合并操作它是看不到的,因而这三个IndexReader所看到的文档应该是既不少也不多。合并使用IndexWriter(硬盘索引).addIndexes(IndexReader(合并中索引)),合并结束后Commit。

如下图:

点击查看原图

3、重新打开硬盘索引的IndexReader

当合并结束后,是应该重新打开硬盘索引的时候了,然而这是一个可能比较慢的过程,在此过程中,我们仍然想保持实时性,因而在此过程中,合并中的索引不能丢弃,硬盘索引的IndexReader也不要动,而是为硬盘索引打开一个临时的IndexReader,在打开的过程中,如果有搜索进来,返回的仍然是上述的三个IndexReader,仍能够不多不少的看到所有的文档,而将要打开的临时的IndexReader将能看到合并中索引和原来的硬盘索引所有的文档,此IndexReader并不返回给客户。如下图:

点击查看原图

4、替代IndexReader

当临时的IndexReader被打开的时候,其看到的是合并中索引的IndexReader和硬盘索引原来的IndexReader之和,下面要做的是:

(1) 关闭合并中索引的IndexReader

(2) 抛弃合并中索引

(3) 用临时的IndexReader替换硬盘索引原来的IndexReader

(4) 关闭硬盘索引原来的IndexReader。

上面说的这几个操作必须是原子性的,如果做了(2)但没有做(3),如果来一个搜索,则将少看到一部分数据,如果做了(3)没有做(2)则,多看到一部分数据。

所以在进行上述四步操作的时候,需要加一个锁,如果这个时候有搜索进来的时候,或者在完全没有做的时候得到所有的IndexReader,或者在完全做好的时候得到所有的IndexReader,这时此搜索可能被block,但是没有关系,这四步是非常快的,丝毫不影响替代性。

如下图:

点击查看原图

经过这几个过程,又达到了第一步的状态,则进行下一个合并的过程。

5、多个索引

有一点需要注意的是,在上述的合并过程中,新添加的文档是始终添加到内存索引中的,如果存在如下的情况,索引速度实在太快,在合并过程没有完成的时候,内存索引又满了,或者硬盘上的索引实在太大,合并和重新打开要花费太长的时间,使得内存索引以及满的情况下,还没有合并完成。

为了处理这种情况,我们可以拥有多个合并中的索引,多个硬盘上的索引,如下图:

点击查看原图

  • 新添加的文档永远是进入内存索引
  • 当内存索引到达一定的大小的时候,将其加入合并中索引链表
  • 有一个后台线程,每隔一定的时刻,将合并中索引写入一个新的硬盘索引中取。这样可以避免由于硬盘索引过大而合并较慢的情况。硬盘索引的IndexReader也是写完并重新打开后才替换合并中索引的IndexReader,新的硬盘索引也可保证打开的过程不会花费太长时间。
  • 这样会造成硬盘索引很多,所以,每隔一定的时刻,将硬盘索引合并成一个大的索引。也是合并完成后方才替换IndexReader

大家可能会发现,此合并的过程和Lucene的段的合并很相似。然而Lucene的一个函数IndexReader.reopen一直是没有实现的,也即我们不能选择哪个段是在内存中的,可以被打开,哪些是硬盘中的,需要在后台打开然后进行替换,而IndexReader.open是会打开所有的内存中的和硬盘上的索引,因而会很慢,从而降低了实时性。

在有关Lucene的问题(7),讨论了使用Lucene内存索引和硬盘索引构建实时索引的问题。

然而有的读者提到,如果涉及到文档的删除及更新,那么如何构建实时的索引呢?本节来讨论这个问题。

1、Lucene删除文档的几种方式

  • IndexReader.deleteDocument(int docID)是用 IndexReader 按文档号删除。  
  • IndexReader.deleteDocuments(Term  term)是用 IndexReader 删除包含此词(Term)的文档。  
  • IndexWriter.deleteDocuments(Term  term)是用 IndexWriter 删除包含此词(Term)的文档。  
  • IndexWriter.deleteDocuments(Term[]  terms)是用 IndexWriter 删除包含这些词(Term)的文档。  
  • IndexWriter.deleteDocuments(Query  query)是用 IndexWriter 删除能满足此查询(Query)的文档。  
  • IndexWriter.deleteDocuments(Query[] queries)是用 IndexWriter 删除能满足这些查询(Query)的文档。

删除文档既可以用reader进行删除,也可以用writer进行删除,不同的是,reader进行删除后,此reader马上能够生效,而用writer删除后,会被缓存,只有写入到索引文件中,当reader再次打开的时候,才能够看到。

2、Lucene文档更新的几个问题

2.1、使用IndexReader还是IndexWriter进行删除

既然IndexReader和IndexWriter都能够进行文档删除,那么到底是应该用哪个来进行删除呢?

本文的建议是,用IndexWriter来进行删除。

因为用IndexReader可能存在以下的问题:

(1) 当有一个IndexWriter打开的时候,IndexReader的删除操作是不能够进行的,否则会报LockObtainFailedException

(2) 当IndexReader被多个线程使用的时候,一个线程用其进行删除,会使得另一个线程看到的索引有所改变,使得另一个线程的结果带有不确定性。

(3) 对于更新操作,在Lucene中是先删除,再添加的,然而删除的被立刻看到的,而添加却不能够立刻看到,造成了数据的不一致性。

(4) 即便以上问题可以通过锁来解决,然而背后的操作影响到了搜索的速度,是我们不想看到的。

2.2、如何在内存中缓存文档的删除

在上一节中,为了能够做到实时性,我们使用内存中的索引,而硬盘上的索引则不经常打开,即便打开也在背后线程中打开。

而要删除的文档如果在硬盘索引中,如果不重新打开则看不到新的删除,则需要将删除的文档缓存到内存中。

那如何将缓存在内存中的文档删除在不重新打开IndexReader的情况下应用于硬盘上的索引呢?

在Lucene中,有一种IndexReader为FilterIndexReader,可以对一个IndexReader进行封装,我们可以实现一个自己的FilterIndexReader来过滤掉删除的文档。

一个例子如下:

public class MyFilterIndexReader extends FilterIndexReader {

  OpenBitSet dels;

  public MyFilterIndexReader(IndexReader in) {

    super(in);

    dels = new OpenBitSet(in.maxDoc());

  }

  public MyFilterIndexReader(IndexReader in, List<String> idToDelete) throws IOException {

    super(in);

    dels = new OpenBitSet(in.maxDoc());

    for(String id : idToDelete){

      TermDocs td = in.termDocs(new Term("id", id)); //如果能在内存中Cache从Lucene的ID到应用的ID的映射,Reader的生成将快得多。

      if(td.next()){

        dels.set(td.doc());

      }

    }

  }

  @Override

  public int numDocs() {

    return in.numDocs() - (int) dels.cardinality();

  }

  @Override

  public TermDocs termDocs(Term term) throws IOException {

    return new FilterTermDocs(in.termDocs(term)) {

      @Override

      public boolean next() throws IOException {

        boolean res;

        while ((res = super.next())) {

          if (!dels.get(doc())) {

            break;

          }

        }

        return res;

      }

    };

  }

  @Override

  public TermDocs termDocs() throws IOException {

    return new FilterTermDocs(in.termDocs()) {

      @Override

      public boolean next() throws IOException {

        boolean res;

        while ((res = super.next())) {

          if (!dels.get(doc())) {

            break;

          }

        }

        return res;

      }

    };

  }

}

2.3、文档更新的顺序性问题

Lucene的文档更新其实是删除旧的文档,然后添加新的文档。如上所述,删除的文档是缓存在内存中的,并通过FilterIndexReader应用于硬盘上的索引,然而新的文档也是以相同的id加入到索引中去的,这就需要保证缓存的删除不会将新的文档也过滤掉,将缓存的删除合并到索引中的时候不会将新的文档也删除掉。

Lucene的两次更新一定要后一次覆盖前一次,而不能让前一次覆盖后一次。

所以内存中已经硬盘中的多个索引是要被保持一个顺序的,哪个是老的索引,哪个是新的索引,缓存的删除自然是应该应用于所有比他老的索引的,而不应该应用于他自己以及比他新的索引。

3、具有更新功能的Lucene实时索引方案

3.1、初始化

首先假设我们硬盘上已经有一个索引FileSystemIndex,被事先打开的,其中包含文档1,2,3,4,5,6。

我们在内存中有一个索引MemoryIndex,新来的文档全部索引到内存索引中,并且是索引完IndexWriter就commit,IndexReader就重新打开,其中包含文档7,8。

点击查看原图

3.2、更新文档5

这时候来一个新的更新文档5, 需要首先将文档5删除,然后加入新的文档5。

需要做的事情是:

  • 首先在内存索引中删除文档5,当然没有文档5,删除无效。
  • 其次将对文档5的删除放入内存文档删除列表,并与硬盘的IndexReader组成FilterIndexReader
  • 最后,将新的文档5加入内存索引,这时候,用户可以看到的就是新的文档5了。
  • 将文档5放入删除列表以及将文档5提交到内存索引两者应该是一个原子操作,好在这两者都是比较块的。

注:此处对硬盘上的索引,也可以进行对文档5的删除,由于IndexReader没有重新打开,此删除是删不掉的,我们之所以没有这样做,是想保持此次更新要么全部在内存中,要么全部在硬盘中,而非删除部分已经应用到硬盘中,而新文档却在内存中,此时,如果系统crash,则新的文档5丢失了,而旧的文档5也已经在硬盘上被删除。我们将硬盘上对文档5的删除放到从内存索引向硬盘索引的合并过程。

点击查看原图

如果再有一次对文档5的更新,则首先将内存索引中的文档5删除,添加新的文档5,然后将文档5加入删除列表,发现已经存在,则不必删除。

3.3、合并索引

然而经过一段时间,内存中的索引需要合并到硬盘上。

在合并的过程中,需要重新建立一个空的内存索引,用于合并阶段索引新的文档,而合并中的索引的IndexReader以及硬盘索引和删除列表所组成的FilterIndexReader仍然保持打开,对外提供服务,而合并阶段从后台进行。

后台的合并包括以下几步:

  • 将删除列表应用到硬盘索引中。
  • 将内存索引合并到硬盘索引中。
  • IndexWriter提交。

点击查看原图

3.4、合并的过程中更新文档5

在合并的过程中,如果还有更新那怎么办呢?

  • 首先将合并中索引的文档5删除,此删除不会影响合并,因为合并之前,合并中索引的IndexReader已经打开,索引合并中索引的文档5还是会合并到硬盘中去的。此删除影响的是此后的查询在合并中索引是看不到文档5的。
  • 然后将文档5的删除放入删除列表,并同合并中索引的删除列表,已经硬盘索引一起构成FilterIndexReader。
  • 将新的文档5添加到内存中索引。
  • 提交在合并中索引对文档5的删除,将文档5添加到删除列表,提交在内存索引中对文档5的添加三者应该是一个原子操作,好在三者也是很快的。

点击查看原图

3.5、重新打开硬盘索引的IndexReader

当合并中索引合并到硬盘中的时候,是时候重新打开硬盘上的索引了,新打开的IndexReader是可以看到文档5的删除的。

如果这个时候有新的更新,也是添加到内存索引和删除列表的,比如我们更新文档6.

点击查看原图

3.6、替代IndexReader 

当IndexReader被重新打开后,则需要删除合并中的索引及其删除列表,将硬盘索引原来的IndexReader关闭,使用新的IndexReader。

 
点击查看原图
发表在 search | 标签为 | 构建实时索引已关闭评论

NOTEPAD++添加右键菜单

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\*\Shell\Open With NotePad++]
[HKEY_CLASSES_ROOT\*\Shell\Open With NotePad++\Command]
@="\"C:\\Program Files (x86)\\Notepad++\\notepad++.exe\" \"%1\""

发表在 info | NOTEPAD++添加右键菜单已关闭评论

jquery array

1. $.each(array, [callback]) 遍历[常用]

解释: 不同于例遍 jQuery 对象的 $().each() 方法,此方法可用于例遍任何对象(不仅仅是数组哦~). 回调函数拥有两个参数:第一个为对象的成员或数组的索引, 第二个为对应变量或内容. 如果需要退出 each 循环可使回调函数返回 false, 其它返回值将被忽略.

each遍历,相信都不陌生,在平常的事件处理中,是for循环的变体,但比for循环强大.在数组中,它可以轻松的攻取数组索引及对应的值.例:

var _mozi=['墨家','墨子','墨翟','兼爱非攻','尚同尚贤']//本文所用到的数组, 下同
$.
each(_mozi,function(key,val){
    
//回调函数有两个参数,第一个是元素索引,第二个为当前值
    
alert('_mozi数组中 ,索引 : '+key+' 对应的值为: '+val);
});

相对于原生的for..in,each更强壮一点. for..in也可以遍历数组,并返回对应索引,但值是需要通过arrName[key]来获取;

2. $.grep(array, callback, [invert]) 过滤数组[常用]

解释: 使用过滤函数过滤数组元素.此函数至少传递两个参数(第三个参数为true或false,对过滤函数返回值取反,个人觉得用处不大): 待过滤数组和过滤函数. 过滤函数必须返回 true 以保留元素或 false 以删除元素. 另外,过滤函数还可以是可设置为一个字条串(个人不推荐,欲了解自行查阅);

$.grep(_mozi,function(val,key){
    
//过滤函数有两个参数,第一个为当前元素,第二个为元素索引
    
if(val=='墨子'){
        
alert('数组值为 墨子 的下标是: '+key);
    
}
});
 
var _moziGt1=$.grep(_mozi,function(val,key){
    
return key>1;
});
alert('_mozi数组中索引值大于1的元素为: '+_moziGt1);
 
var _moziLt1=$.grep(_mozi,function(val,key){
    
return key>1;
},true);
//此处传入了第三个可靠参数,对过滤函数中的返回值取反
alert('_mozi数组中索引值小于等于1的元素为: '+_moziLt1);

3. $.map(array,[callback])按给定条件转换数组 [一般]

解释:作为参数的转换函数会为每个数组元素调用, 而且会给这个转换函数传递一个表示被转换的元素作为参数. 转换函数可以返回转换后的值、null(删除数组中的项目)或一个包含值的数组, 并扩展至原始数组中.

这个是个很强大的方法,但并不常用. 它可以根据特定条件,更新数组元素值,或根据原值扩展一个新的副本元素.

var _mapArrA=$.map(_mozi,function(val){
    
return val+'[新加]';
});
var _mapArrB=$.map(_mozi,function(val){
    
return val=='墨子' ? '[只给墨子加]'+val : val;
});
var _mapArrC=$.map(_mozi,function(val){
    
//为数组元素扩展一个新元素
    
return [val,(val+'[扩展]')];
});
alert('在每个元素后面加\'[新加]\'字符后的数组为: '_mapArrA);
alert('只给元素 墨子 添加字符后的数组为: '_mapArrB);
alert('为原数组中每个元素,扩展一个添加字符\'[新加]\'的元素,返回的数组为 '+_mapArrC);

4 .$.inArray(val,array)判断值是否存在于数组中[常用]

解释: 确定第一个参数在数组中的位置, 从0开始计数(如果没有找到则返回 -1 ).

记得indexOf()方法了吗? indexOf()返回字符串的首次出现位置,而$.inArray()返回的是传入参数在数组中的位置,同样的,如果找到的,返回的是一个大于或等于0的值,若未找到则返回-1.现在, 知道怎么用了吧. 有了它, 判断某个值是否存在于数组中,就变得轻而易举了.

var _exist=$.inArray('墨子',_mozi);
var _inexistence=$.inArray('卫鞅',_mozi)
if(_exist>=0){
    
alert('墨子 存在于数组_mozi中,其在数组中索引值是: '+_exist);
}
if(_inexistence<0){
    
alert('卫鞅 不存在于数组_mozi中!,返回值为: '+_inexistence+'!');
}

5 .$.merge(first,second)合并两个数组[一般]

解释: 返回的结果会修改第一个数组的内容——第一个数组的元素后面跟着第二个数组的元素.

这个方法是用jQuery的方法替代原生concat()方法, 但功能并没有concat()强大, concat()可以同时合并多个数组.

//原生concat()可能比它还简洁点
_moziNew=$.merge(_mozi,['鬼谷子','商鞅','孙膑','庞涓','苏秦','张仪'])
alert('合并后新数组长度为: '+_moziNew.length+'. 其值为: '+_moziNew);

6 .$.unique(array)过滤数组中重复元素[不常用]

解释: 删除数组中重复元素. 只处理删除DOM元素数组,而不能处理字符串或者数字数组.

第一次看到这个方法,觉得这是个很便捷的方法, 可以过滤重复, 哈, 多完美, 但仔细一看, 仅限处理DOM元素. 功能8折了.所以, 我给它定义成了一个不常用的元素, 至少, 我用jQuery以来没用到过它.

var _h2Arr=$.makeArray(h2obj);
//将数组_h2Arr重复一次
_h2Arr=$.merge(_h2Arr,_h2Arr);
var _curLen=_h2Arr.length;
_h2Arr=$.unique(_h2Arr);
var _newLen=_h2Arr.length;
alert('数组_h2Arr原长度值为: '+_curLen+' ,过滤后为: '+_newLen
      +
' .共过滤 '+(_curLen-_newLen)+'个重复元素')

7. $.makeArray(obj) 将类数组对象转换为数组[不常用]

解释: 将类数组对象转换为数组对象, 类数组对象有 length 属性,其成员索引为0至 length-1.

这是个多余的方法, 无所不能的$本来就包含了这个功能. jQuery官网上解释的非常模糊. 其实, 它就是将某个类数组对象(比如用getElementsByTagName获取的元素对象集合)转换成数组对象.

var _makeArr=$.makeArray(h2obj);
alert('h2元素对象集合的数据类型转换为: '+_makeArr.constructor.name);//输出Array

8. $(dom).toArray()将所有DOM元素恢复成数组[不常用]

解释: 把jQuery集合中所有DOM元素恢复成一个数组;

并不常用的方法, 个人甚至觉得它和$.makeArray一样多余.

var _toArr=$('h2').toArray();
alert('h2元素集合恢复后的数据类型是: '+_toArr.constructor.name);
发表在 article | 标签为 , | jquery array已关闭评论

js array 方法

Array 对象的方法

FF: Firefox, N: Netscape, IE: Internet Explorer

方法 描述 FF N IE
concat() 连接两个或更多的数组,并返回结果。 1 4 4
join() 把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。 1 3 4
pop() 删除并返回数组的最后一个元素 1 4 5.5
push() 向数组的末尾添加一个或更多元素,并返回新的长度。 1 4 5.5
reverse() 颠倒数组中元素的顺序。 1 3 4
shift() 删除并返回数组的第一个元素 1 4 5.5
slice() 从某个已有的数组返回选定的元素 1 4 4
sort() 对数组的元素进行排序 1 3 4
splice() 删除元素,并向数组添加新元素。 1 4 5.5
toSource() 代表对象的源代码 1 4 -
toString() 把数组转换为字符串,并返回结果。 1 3 4
toLocaleString() 把数组转换为本地数组,并返回结果。 1 3 4
unshift() 向数组的开头添加一个或更多元素,并返回新的长度。 1 4 6
valueOf() 返回数组对象的原始值 1 2 4

Array 对象的属性

FF: Firefox, N: Netscape, IE: Internet Explorer

属性 描述 FF N IE
constructor 对创建此对象的函数的一个引用 1 2 4
index   1 3 4
input   1 3 4
length 设置或返回数组中元素的数目。 1 2 4
prototype 使您有能力向对象添加属性和方法 1 2 4

 

发表在 article | 标签为 | js array 方法已关闭评论

Unicode – CSS中文字体转编码

“\5b8b\4f53″  是 “宋体”。用 unicode 表示,不用 SimSun, 是因为 Firefox 的某些版本和 Opera 不支持 SimSun 的写法

 

中文名 英文名 Unicode Unicode 2
Mac OS
华文细黑 STHeiti Light [STXihei] \534E\6587\7EC6\9ED1 &#x534E;&#x6587;&#x7EC6;&#x9ED1;
华文黑体 STHeiti \534E\6587\9ED1\4F53 &#x534E;&#x6587;&#x9ED1;&#x4F53;
华文楷体 STKaiti \534E\6587\6977\4F53 &#x534E;&#x6587;&#x6977;&#x4F53;
华文宋体 STSong \534E\6587\5B8B\4F53 &#x534E;&#x6587;&#x5B8B;&#x4F53;
华文仿宋 STFangsong \534E\6587\4EFF\5B8B &#x534E;&#x6587;&#x4EFF;&#x5B8B;
丽黑 Pro LiHei Pro Medium \4E3D\9ED1 Pro &#x4E3D;&#x9ED1; Pro
丽宋 Pro LiSong Pro Light \4E3D\5B8B Pro &#x4E3D;&#x5B8B; Pro
标楷体 BiauKai \6807\6977\4F53 &#x6807;&#x6977;&#x4F53;
苹果丽中黑 Apple LiGothic Medium \82F9\679C\4E3D\4E2D\9ED1 &#x82F9;&#x679C;&#x4E3D;&#x4E2D;&#x9ED1;
苹果丽细宋 Apple LiSung Light \82F9\679C\4E3D\7EC6\5B8B &#x82F9;&#x679C;&#x4E3D;&#x7EC6;&#x5B8B;
Windows
新细明体 PMingLiU \65B0\7EC6\660E\4F53 &#x65B0;&#x7EC6;&#x660E;&#x4F53;
细明体 MingLiU \7EC6\660E\4F53 &#x7EC6;&#x660E;&#x4F53;
标楷体 DFKai-SB \6807\6977\4F53 &#x6807;&#x6977;&#x4F53;
黑体 SimHei \9ED1\4F53 &#x9ED1;&#x4F53;
宋体 SimSun \5B8B\4F53 &#x5B8B;&#x4F53;
新宋体 NSimSun \65B0\5B8B\4F53 &#x65B0;&#x5B8B;&#x4F53;
仿宋 FangSong \4EFF\5B8B &#x4EFF;&#x5B8B;
楷体 KaiTi \6977\4F53 &#x6977;&#x4F53;
仿宋_GB2312 FangSong_GB2312 \4EFF\5B8B_GB2312 &#x4EFF;&#x5B8B;_GB2312
楷体_GB2312 KaiTi_GB2312 \6977\4F53_GB2312 &#x6977;&#x4F53;_GB2312
微软正黑体 Microsoft JhengHei \5FAE\x8F6F\6B63\9ED1\4F53 &#x5FAE;&#x8F6F;&#x6B63;&#x9ED1;&#x4F53;
微软雅黑 Microsoft YaHei \5FAE\8F6F\96C5\9ED1 &#x5FAE;&#x8F6F;&#x96C5;&#x9ED1;
Office
隶书 LiSu \96B6\4E66 &#x96B6;&#x4E66;
幼圆 YouYuan \5E7C\5706 &#x5E7C;&#x5706;
华文细黑 STXihei \534E\6587\7EC6\9ED1 &#x534E;&#x6587;&#x7EC6;&#x9ED1;
华文楷体 STKaiti \534E\6587\6977\4F53 &#x534E;&#x6587;&#x6977;&#x4F53;
华文宋体 STSong \534E\6587\5B8B\4F53 &#x534E;&#x6587;&#x5B8B;&#x4F53;
华文中宋 STZhongsong \534E\6587\4E2D\5B8B &#x534E;&#x6587;&#x4E2D;&#x5B8B;
华文仿宋 STFangsong \534E\6587\4EFF\5B8B &#x534E;&#x6587;&#x4EFF;&#x5B8B;
方正舒体 FZShuTi \65B9\6B63\8212\4F53 &#x65B9;&#x6B63;&#x8212;&#x4F53;
方正姚体 FZYaoti \65B9\6B63\59DA\4F53 &#x65B9;&#x6B63;&#x59DA;&#x4F53;
华文彩云 STCaiyun \534E\6587\5F69\4E91 &#x534E;&#x6587;&#x5F69;&#x4E91;
华文琥珀 STHupo \534E\6587\7425\73C0 &#x534E;&#x6587;&#x7425;&#x73C0;
华文隶书 STLiti \534E\6587\96B6\4E66 &#x534E;&#x6587;&#x96B6;&#x4E66;
华文行楷 STXingkai \534E\6587\884C\6977 &#x534E;&#x6587;&#x884C;&#x6977;
华文新魏 STXinwei \534E\6587\65B0\9B4F &#x534E;&#x6587;&#x65B0;&#x9B4F;
发表在 article | 标签为 | Unicode – CSS中文字体转编码已关闭评论

CSS+DIV布局中的自适应宽度

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>无标题文档</title>
<style type="text/css">
<!--
body{margin:0;padding:0px;text-align:center;}
#wrap{margin:0 auto;text-align:left; width:900px;}

/*左边栏,固定宽度*/
.wrap_l{float:left;background:#FF0000;margin-right:-150px;width:150px;}
/*中间栏,自适应宽度*/
.wrap_m{width:auto;background:#00FF00;margin:0 140px 0 150px;}
/*右边栏,固定宽度*/
.wrap_r{float:right;background:#0000FF;margin-left:-140px;width:140px;}

.wrap_inner_l{float:left;background:#00FFFF;margin-right:-200px;width:200px;}
.wrap_inner_m{width:auto;background:#FFFF00;margin:0 0 0 200px;}

-->
</style>
</head>
<body>
<div id="wrap">
<div class="wrap_l">左边栏</div>
<div class="wrap_r">右边栏</div>
<div class="wrap_m">中间栏</div>
</div>
</body>
</html>

发表在 article | 标签为 , | CSS+DIV布局中的自适应宽度已关闭评论

mongo

mongodb与mysql命令对比

传统的关系数据库一般由数据库(database)、表(table)、记录(record)三个层次概念组成,MongoDB是由数据库(database)、集合(collection)、文档对象(document)三个层次组成。MongoDB对于关系型数据库里的表,但是集合中没有列、行和关系概念,这体现了模式自由的特点。 

MySQL

MongoDB

说明

mysqld

mongod

服务器守护进程

mysql

mongo

客户端工具

mysqldump

mongodump

逻辑备份工具

mysql

mongorestore

逻辑恢复工具

 

db.repairDatabase()

修复数据库

mysqldump

mongoexport

数据导出工具

source

mongoimport

数据导入工具

grant * privileges on *.* to …

Db.addUser()

Db.auth()

新建用户并权限

show databases

show dbs

显示库列表

Show tables

Show collections

显示表列表

Show slave status

Rs.status

查询主从状态

Create table users(a int, b int)

db.createCollection("mycoll", {capped:true,

size:100000}) 另:可隐式创建表。

创建表

Create INDEX idxname ON users(name)

db.users.ensureIndex({name:1})

创建索引

Create INDEX idxname ON users(name,ts DESC)

db.users.ensureIndex({name:1,ts:-1})

创建索引

Insert into users values(1, 1)

db.users.insert({a:1, b:1})

插入记录

Select a, b from users

db.users.find({},{a:1, b:1})

查询表

Select * from users

db.users.find()

查询表

Select * from users where age=33

db.users.find({age:33})

条件查询

Select a, b from users where age=33

db.users.find({age:33},{a:1, b:1})

条件查询

select * from users where age<33

db.users.find({'age':{$lt:33}})

条件查询

select * from users where age>33 and age<=40

db.users.find({'age':{$gt:33,$lte:40}})

条件查询

select * from users where a=1 and b='q'

db.users.find({a:1,b:'q'})

条件查询

select * from users where a=1 or b=2

db.users.find( { $or : [ { a : 1 } , { b : 2 } ] } )

条件查询

select * from users limit 1

db.users.findOne()

条件查询

select * from users where name like "%Joe%"

db.users.find({name:/Joe/})

模糊查询

select * from users where name like "Joe%"

db.users.find({name:/^Joe/})

模糊查询

select count(1) from users

Db.users.count()

获取表记录数

select count(1) from users where age>30

db.users.find({age: {'$gt': 30}}).count()

获取表记录数

select DISTINCT last_name from users

db.users.distinct('last_name')

去掉重复值

select * from users ORDER BY name

db.users.find().sort({name:-1})

排序

select * from users ORDER BY name DESC

db.users.find().sort({name:-1})

排序

EXPLAIN select * from users where z=3

db.users.find({z:3}).explain()

获取存储路径

update users set a=1 where b='q'

db.users.update({b:'q'}, {$set:{a:1}}, false, true)

更新记录

update users set a=a+2 where b='q'

db.users.update({b:'q'}, {$inc:{a:2}}, false, true)

更新记录

delete from users where z="abc"

db.users.remove({z:'abc'})

删除记录

 

db. users.remove()

删除所有的记录

drop database IF EXISTS test;

use test

db.dropDatabase()

删除数据库

drop table IF EXISTS test;

db.mytable.drop()

删除表/collection

 

db.addUser(‘test’, ’test’)

添加用户

readOnly-->false

 

db.addUser(‘test’, ’test’, true)

添加用户

readOnly-->true

 

db.addUser("test","test222")

更改密码

 

db.system.users.remove({user:"test"})

或者db.removeUser('test')

删除用户

 

use admin

超级用户

 

db.auth(‘test’, ‘test’)

用户授权

 

db.system.users.find()

查看用户列表

 

show users

查看所有用户

 

db.printCollectionStats()

查看各collection的状态

 

db.printReplicationInfo()

查看主从复制状态

 

show profile

查看profiling

 

db.copyDatabase('mail_addr','mail_addr_tmp')

拷贝数据库

 

db.users.dataSize()

查看collection数据的大小

 

db. users.totalIndexSize()

查询索引的大小

 

 

mongodb语法

 

MongoDB的好处挺多的,比如多列索引,查询时可以用一些统计函数,支持多条件查询,但是目前多表查询是不支持的,可以想办法通过数据冗余来解决多表查询的问题。

MongoDB对数据的操作很丰富,下面做一些举例说明,内容大部分来自官方文档,另外有部分为自己理解。

 

查询colls所有数据

db.colls.find() //select * from colls 

通过指定条件查询

db.colls.find({‘last_name’: ‘Smith’});//select * from colls where last_name=’Smith’ 

指定多条件查询

db.colls.find( { x : 3, y : “foo” } );//select * from colls where x=3 and y=’foo’

 

指定条件范围查询

db.colls.find({j: {$ne: 3}, k: {$gt: 10} });//select * from colls where j!=3 and k>10

 

查询不包括某内容

db.colls.find({}, {a:0});//查询除a为0外的所有数据

 

支持<, <=, >, >=查询,需用符号替代分别为$lt,$lte,$gt,$gte

db.colls.find({ “field” : { $gt: value } } ); 

db.colls.find({ “field” : { $lt: value } } ); 

db.colls.find({ “field” : { $gte: value } } );

db.colls.find({ “field” : { $lte: value } } );

 

也可对某一字段做范围查询

db.colls.find({ “field” : { $gt: value1, $lt: value2 } } );

 

不等于查询用字符$ne

db.colls.find( { x : { $ne : 3 } } );

 

in查询用字符$in

db.colls.find( { “field” : { $in : array } } );

db.colls.find({j:{$in: [2,4,6]}});

 

not in查询用字符$nin

db.colls.find({j:{$nin: [2,4,6]}});

 

取模查询用字符$mod

db.colls.find( { a : { $mod : [ 10 , 1 ] } } )// where a % 10 == 1

 

$all查询

db.colls.find( { a: { $all: [ 2, 3 ] } } );//指定a满足数组中任意值时

 

$size查询

db.colls.find( { a : { $size: 1 } } );//对对象的数量查询,此查询查询a的子对象数目为1的记录

 

$exists查询

db.colls.find( { a : { $exists : true } } ); // 存在a对象的数据

db.colls.find( { a : { $exists : false } } ); // 不存在a对象的数据

 

$type查询$type值为bsonhttp://bsonspec.org/数 据的类型值

db.colls.find( { a : { $type : 2 } } ); // 匹配a为string类型数据

db.colls.find( { a : { $type : 16 } } ); // 匹配a为int类型数据

 

使用正则表达式匹配

db.colls.find( { name : /acme.*corp/i } );//类似于SQL中like

 

内嵌对象查询

db.colls.find( { “author.name” : “joe” } );

 

1.3.3版本及更高版本包含$not查询

db.colls.find( { name : { $not : /acme.*corp/i } } );

db.colls.find( { a : { $not : { $mod : [ 10 , 1 ] } } } );

 

sort()排序

db.colls.find().sort( { ts : -1 } );//1为升序2为降序

 

limit()对限制查询数据返回个数

db.colls.find().limit(10)

 

skip()跳过某些数据

db.colls.find().skip(10)

 

snapshot()快照保证没有重复数据返回或对象丢失

 

count()统计查询对象个数

db.students.find({‘address.state’ : ‘CA’}).count();//效率较高

db.students.find({‘address.state’ : ‘CA’}).toArray().length;//效率很低

 

group()对查询结果分组和SQL中group by函数类似

distinct()返回不重复值

---------------------------------------------------------------------------------------

mongodb查询的语法

1 ) . 大于,小于,大于或等于,小于或等于

$gt:大于
$lt:小于
$gte:大于或等于
$lte:小于或等于

例子:

db.collection.find({ "field"
: { $gt: value } } );   // greater than  : field > value

db.collection.find({ "field"
: { $lt: value } } );   // less than  :  field < value

db.collection.find({ "field"
: { $gte: value } } );  // greater than or equal to : field >= value

db.collection.find({ "field"
: { $lte: value } } );  // less than or equal to : field <= value

 

如查询j大于3,小于4:

db.things.find({j : {$lt: 3}});
db.things.find({j : {$gte: 4}});

也可以合并在一条语句内:

db.collection.find({ "field"
 : { $gt: value1, $lt: value2 } } );    // value1 < field < value

2) 不等于 $ne

例子:

db.things.find( { x : { $ne : 3 } } );


3) in 和 not in ($in $nin)

语法:
db.collection.find( { "field"
 : { $in : array } } );

例子:

db.things.find({j:{$in: [2,4,6]}});
db.things.find({j:{$nin: [2,4,6]}});


4) 取模运算$mod

如下面的运算:
db.things.find( "this
.a % 10 == 1"
)

可用$mod代替:

db.things.find( { a : { $mod : [ 10 , 1 ] } } )


5)  $all

$all和$in类似,但是他需要匹配条件内所有的值:

如有一个对象:
{ a: [ 1, 2, 3 ] }

下面这个条件是可以匹配的:

db.things.find( { a: { $all: [ 2, 3 ] } } );

但是下面这个条件就不行了:

db.things.find( { a: { $all: [ 2, 3, 4 ] } } );


6)  $size

$size是匹配数组内的元素数量的,如有一个对象:{a:["foo"] },他只有一个元素:

下面的语句就可以匹配:
db.things.find( { a : { $size: 1 } } );

官网上说不能用来匹配一个范围内的元素,如果想找$size<5之类的,他们建议创建一个字段来保存元素的数量。

You cannot use $size  to find a range of sizes (for example: arrays with more than 1 element). If you need to query for a range, create an extrasize  field that you increment when you add elements.

 

7)$exists

$exists用来判断一个元素是否存在:

如:

db.things.find( { a : { $exists : true
 } } ); // 如果存在

元素

a,就返回

db.things.find( { a : { $exists : false
 } } ); // 如果不存在元素a,就返回


8)  $type

$type  基于 bson  type来匹配一个元素的类型,像是按照类型ID来匹配,不过我没找到bson类型和id对照表。

db.things.find( { a : { $type : 2 } } ); // matches if
 a is a string

db.things.find( { a : { $type : 16 } } ); // matches if
 a is an int

9)正则表达式

mongo支持正则表达式,如:
 

db.customers.find( { name : /acme.*corp/i } ); // 后面的i的意思是区分大小写


10)  查询数据内的值

下面的查询是查询colors内red的记录,如果colors元素是一个数据,数据库将遍历这个数组的元素来查询。
db.things.find( { colors : "red"
 } );

11) $elemMatch

如果对象有一个元素是数组,那么$elemMatch可以匹配内数组内的元素:

> t.find( { x : { $elemMatch : { a : 1, b : { $gt : 1 } } } } ) 
{ "_id"
: ObjectId("4b5783300334000000000aa9"
), 
"x"
: [ { "a"
: 1, "b"
: 3 }, 7, { "b"
: 99 }, { "a"
: 11 } ]
}
 

$elemMatch : { a : 1, b : { $gt : 1 } } 所有的条件都要匹配上才行。

注意,上面的语句和下面是不一样的。

> t.find( { "x.a"
 : 1, "x.b"
 : { $gt : 1 } } )

$elemMatch是匹配{ "a"  : 1, "b"  : 3 },而后面一句是匹配{ "b"  : 99 }, { "a"  : 11 } 


12)  查询嵌入对象的值
db.postings.find( { "author.name"
 : "joe"
 } );

注意用法是author.name ,用一个点就行了。更详细的可以看这个链接: dot notation

举个例子:

> db.blog.save({ title : "My First Post"
, author: {name : "Jane"
, id : 1}})

如果我们要查询 authors name 是Jane的, 我们可以这样:

> db.blog.findOne({"author.name"
 : "Jane"
})

如果不用点,那就需要用下面这句才能匹配:

db.blog.findOne({"author"
 : {"name"
 : "Jane"
, "id"
 : 1}})

下面这句:

db.blog.findOne({"author"
 : {"name"
 : "Jane"
}})

是不能匹配的,因为mongodb对于子对象,他是精确匹配。

 

13) 元操作符 $not 取反

如:

db.customers.find( { name : { $not : /acme.*corp/i } } );
db.things.find( { a : { $not : { $mod : [ 10 , 1 ] } } } );

mongodb还有很多函数可以用,如排序,统计等,请参考原文。

mongodb目前没有或(or)操作符,只能用变通的办法代替

--------------------------------------------------------------------------------------------

GUI:

http://download.csdn.net/download/icejoywoo/3822577

发表在 article | 标签为 | mongo已关闭评论

使用SharpZipLib实现zip压缩

使用国外开源加压解压库ICSharpCode.SharpZipLib实现加压,该库的官方网站为
http://www.icsharpcode.net/OpenSource/SharpZipLib/Download.aspx


 

#region 加压解压方法
        
/// <summary>
        
/// 功能:压缩文件(暂时只压缩文件夹下一级目录中的文件,文件夹及其子级被忽略)
        
/// </summary>
        
/// <param name="dirPath">被压缩的文件夹夹路径</param>
        
/// <param name="zipFilePath">生成压缩文件的路径,为空则默认与被压缩文件夹同一级目录,名称为:文件夹名+.zip</param>
        
/// <param name="err">出错信息</param>
        
/// <returns>是否压缩成功</returns>
        public bool ZipFile(string dirPath, string zipFilePath, out string err)
        {
            err 
= "";
            
if (dirPath == string.Empty)
            {
                err 
= "要压缩的文件夹不能为空!";
                
return false;
            }
            
if (!Directory.Exists(dirPath))
            {
                err 
= "要压缩的文件夹不存在!";
                
return false;
            }
            
//压缩文件名为空时使用文件夹名+.zip
            if (zipFilePath == string.Empty)
            {
                
if (dirPath.EndsWith("\\"))
                {
                    dirPath 
= dirPath.Substring(0, dirPath.Length - 1);
                }
                zipFilePath 
= dirPath + ".zip";
            }

            try
            {
                
string[] filenames = Directory.GetFiles(dirPath);
                
using (ZipOutputStream s = new ZipOutputStream(File.Create(zipFilePath)))
                {
                    s.SetLevel(
9);
                    
byte[] buffer = new byte[4096];
                    
foreach (string file in filenames)
                    {
                        ZipEntry entry 
= new ZipEntry(Path.GetFileName(file));
                        entry.DateTime 
= DateTime.Now;
                        s.PutNextEntry(entry);
                        
using (FileStream fs = File.OpenRead(file))
                        {
                            
int sourceBytes;
                            
do
                            {
                                sourceBytes 
= fs.Read(buffer, 0, buffer.Length);
                                s.Write(buffer, 
0, sourceBytes);
                            } 
while (sourceBytes > 0);
                        }
                    }
                    s.Finish();
                    s.Close();
                }
            }
            
catch (Exception ex)
            {
                err 
= ex.Message;
                
return false;
            }
            
return true;
        }

        /// <summary>
        
/// 功能:解压zip格式的文件。
        
/// </summary>
        
/// <param name="zipFilePath">压缩文件路径</param>
        
/// <param name="unZipDir">解压文件存放路径,为空时默认与压缩文件同一级目录下,跟压缩文件同名的文件夹</param>
        
/// <param name="err">出错信息</param>
        
/// <returns>解压是否成功</returns>
        public bool UnZipFile(string zipFilePath, string unZipDir, out string err)
        {
            err 
= "";
            
if (zipFilePath == string.Empty)
            {
                err 
= "压缩文件不能为空!";
                
return false;
            }
            
if (!File.Exists(zipFilePath))
            {
                err 
= "压缩文件不存在!";
                
return false;
            }
            
//解压文件夹为空时默认与压缩文件同一级目录下,跟压缩文件同名的文件夹
            if (unZipDir == string.Empty)
                unZipDir 
= zipFilePath.Replace(Path.GetFileName(zipFilePath), Path.GetFileNameWithoutExtension(zipFilePath));
            
if (!unZipDir.EndsWith("\\"))
                unZipDir 
+= "\\";
            
if (!Directory.Exists(unZipDir))
                Directory.CreateDirectory(unZipDir);

            try
            {
                
using (ZipInputStream s = new ZipInputStream(File.OpenRead(zipFilePath)))
                {

                    ZipEntry theEntry;
                    while ((theEntry = s.GetNextEntry()) != null)
                    {
                        
string directoryName = Path.GetDirectoryName(theEntry.Name);
                        
string fileName = Path.GetFileName(theEntry.Name);
                        
if (directoryName.Length > 0)
                        {
                            Directory.CreateDirectory(unZipDir 
+ directoryName);
                        }
                        
if (!directoryName.EndsWith("\\"))
                            directoryName 
+= "\\";
                        
if (fileName != String.Empty)
                        {
                            
using (FileStream streamWriter = File.Create(unZipDir + theEntry.Name))
                            {

                                int size = 2048;
                                
byte[] data = new byte[2048];
                                
while (true)
                                {
                                    size 
= s.Read(data, 0, data.Length);
                                    
if (size > 0)
                                    {
                                        streamWriter.Write(data, 
0, size);
                                    }
                                    
else
                                    {
                                        
break;
                                    }
                                }
                            }
                        }
                    }
//while
                }
            }
            
catch (Exception ex)
            {
                err 
= ex.Message;
                
return false;
            }
            
return true;
        }
//解压结束
        #endregion

需要添加对SharpZipLib的引用:

using ICSharpCode.SharpZipLib.Zip;

 

案列二:

1、创建zip文件,并添加文件:

using (ZipFile zip = ZipFile.Create(@"E:\test.zip")) 

    zip.BeginUpdate(); 
    zip.Add(
@"E:\文件1.txt"); 
    zip.Add(
@"E:\文件2.txt"); 
    zip.CommitUpdate(); 

 

2、将文件夹压缩为文件

(new FastZip()).CreateZip(@"E:\test.zip"@"E:\test\"true"");

 

最后一个参数是使用正则表达式表示的过滤文件规则。CreateZip方法有3个重载版本,其中有目录过滤参数、文件过滤参数及用于指定是否进行子目录递归的一个bool类型的参数。

3、将文件添加到已有zip文件中

using (ZipFile zip = new ZipFile(@"E:\test.zip"))
{
    zip.BeginUpdate();
    zip.Add(
@"E:\test.doc");
    zip.CommitUpdate();
}

 

4、列出zip文件中文件

using (ZipFile zip = new ZipFile(@"E:\test.zip"))
{
    
string list = string.Empty;
    
foreach (ZipEntry entry in zip)
    {
        list 
+= entry.Name + "\r\n";
    }
    MessageBox.Show(list);
}

 

5、删除zip文件中的一个文件

using (ZipFile zip = new ZipFile(@"E:\test.zip"))
{
    zip.BeginUpdate();
    zip.Delete(
@"test.doc");
    zip.Delete(
@"test22.txt");
    zip.CommitUpdate();
}

 

 6、解压zip文件中文件到指定目录下

(new FastZip()).ExtractZip(@"E:\test.zip"@"E:\test\""");

 

7、常用类

ZipInputStream、GZipInputStream用于解压缩Deflate、GZip格式流,ZipOutputStream、GZipOutputStream用于压缩Deflate、GZip格式流。
StreamUtil类包含了几个Stream处理辅助方法:
  1) Copy(Stream, Stream, Byte[])用于从一个Stream对象中复制数据到另一Stream对象。有多个重写。
  2) ReadFully(Stream, Byte [])用于从Stream对象中读取所有的byte数据。有多个重写

 

发表在 article | 标签为 , | 使用SharpZipLib实现zip压缩已关闭评论

PHP生成PDF文档的两种方式及HTML2PDF

 

PHP生成PDF文档的两种方式及HTML2PDF

一、PHP手册中提到的,用dll文件的扩展库,使用pdflib(下载地址:http://www.pdflib.com/download/pdflib-family/pdflib-7/)。

1.下载dll组件。

2.放在AppServ\php5\ext(即php扩展文件夹)下。

3.修改PHP.INI,增加extension=libpdf_php.dll这句,一般都在最后添加。

4.重启服务器。

缺点:因为pdflib是盈利的,但提供免费下载,只不过免费版的会在生成的PDF文件上面有水印,影响美观。

二、使用fpdf类,比较常见的方法,因为开源,文件下载地址:http://www.fpdf.org/,还有多国语言的手册,很不错。

无需修改任何设置,只要引入文件即可。

缺点:对中文支持不是很好,需要自己扩展。

三、HTML2FPDF:http://html2fpdf.sourceforge.net/下载类文件

实例:

<?

require_once('html2fpdf.php');

// activate Output-Buffer:

ob_start();

?>

<html>

<head>

<title>HTML 2 (F)PDF Project</title>

</head>

<body>

<div align="center"><img src="logo.gif" alt="HTML 2 PDF logo" /></div>

<h2 align="center">HTML 2 PDF Project</h2>

Below are the descriptions of each MENU button:

<ul>

<li><b><font color="red">HOME</font></b> - Returns to the main page (this one)</li>

<li><b>FEATURES</b> - Explains the script's main features</li>

<li><b>EXAMPLES</b> - A series of examples in which the script works</li>

<li><b>CLASS-DOC</b> - Some documentation about the used classes</li>

<li><b>LICENSE</b> - In case you want to distribute or modify the code</li>

<li><b>CREDITS</b> - (the name says it all)</li>

<li><b>FAQ</b> - Frequently asked questions...and answers!</li>

</ul>

<p>This project is hosted on Source Forge. DOWNLOAD IT <a

href="http://sourceforge.net/projects/html2fpdf" target="_blank">THERE</a>!</p>

This script was written on PHP 4.3.3 (should work on 4.0 and greater)

<br /><br />

<div style='background:#ccc;border:thin dashed black'>

This page was dinamically created using PHP ob_get_contents and HTML2FPDF

class.<br />

Read more on FAQ on how to make this or check the 2<sup>nd</sup> page (use the

'PageDown' keyboard key)</div><newpage>

<div style='background:#eee;font-weight:bold'><code>

<? $metaphp = htmlspecialchars($metaphp);

$metaphp = str_replace("\n",'<br>',$metaphp);

echo "&lt;?".$metaphp."?&gt;"; ?>

</div></code>

</body>

</html>

<?

// Output-Buffer in variable:

$html=ob_get_contents();

// delete Output-Buffer

ob_end_clean();

$pdf = new HTML2FPDF();

$pdf->DisplayPreferences('HideWindowUI');

$pdf->AddPage();

$pdf->WriteHTML($html);

$pdf->Output('doc.pdf','I');

?>

发表在 article | 标签为 , | PHP生成PDF文档的两种方式及HTML2PDF已关闭评论

MySqli 预处理

   $mysqli = new mysqli("localhost","root","root","test");
   $sql = "insert into book (bookn,bookp,booka,addr)
           values(?,?,?,?)";
   //预处理
   $stmt = $mysqli -> prepare($sql);
   $stmt->bind_param("siss",$bookn,$bookp,$booka,$addr);
   $bookn = "计算机科学导论";
   $bookp = 11;
   $booka = "王玲";
   $addr = "清华大学出版社";
   $stmt->execute();
  
   $bookn = "建筑给水排水工程(第5版)";
   $bookp = 10;
   $booka = "王增长";
   $addr = "中国建筑工业出版社";
   $stmt -> execute();
   echo($stmt -> insert_id ."<br>");
   echo($stmt -> affected_rows);

发表在 db | 标签为 , | MySqli 预处理已关闭评论

红马版验证码实现(中文+变形+噪点)

 

  本文示例源代码或素材下载

  为了应付越来越多的自动发帖机、恶意攻击等情形,验证码技术在大量的网站上得到使用。我在近期开发一个注册网站的时候,也使用了这一技术。当然,我并不想完完全全自己重新实现,而是参考了网上能够找到的实现,做了若干改进而已。下面谈谈我的实现。

  补两张图片:

 点击查看原图点击查看原图

  首先看验证码图片输出页的代码:

<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
  protected void Page_Load(object sender, EventArgs e)
  {
    VryImgGen gen = new VryImgGen();
    string verifyCode = gen.CreateVerifyCode(5, 1);
    Session["VerifyCode"] = verifyCode.ToUpper();
    System.Drawing.Bitmap bitmap = gen.CreateImage(verifyCode);
    System.IO.MemoryStream ms = new System.IO.MemoryStream();
    bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
    Response.Clear();
    Response.ContentType = "image/Png";
    Response.BinaryWrite(ms.GetBuffer());
    bitmap.Dispose();
    ms.Dispose();
    ms.Close();
    Response.End();
  }
</script> 

  功能很简单,初始化一个验证码生成对象,生成图片。然后保存到一个MemoryStream里。得到字节流,输出字节流。验证码的数据是保存在Session中的,这是最简单的方法。或者可以加密储存在cookie里,也是可以的。

  再来看看验证码生成对象:

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Drawing;
using System.Text;
/// <summary>
/// Summary description for VryImgGen
/// </summary>
public partial class VryImgGen
{
  /// <summary>
  /// 供验证码生成汉字时选取的汉字集,若为空则按照《GB2312简体中文编码表》编码规则构造汉字
  /// </summary>
  public static string ChineseChars = String.Empty;
  /// <summary>
  /// 英文与数字串
  /// </summary>
  protected static readonly string EnglishOrNumChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  public VryImgGen()
  {
    rnd = new Random(unchecked((int)DateTime.Now.Ticks));
  }
  /// <summary>
  /// 全局随机数生成器
  /// </summary>
  private Random rnd;
  int length = 5;
  /// <summary>
  /// 验证码长度(默认6个验证码的长度)
  /// </summary>
  public int Length
  {
    get { return length; }
    set { length = value; }
  }
  int fontSize = 18;
  /// <summary>
  /// 验证码字体大小(为了显示扭曲效果,默认30像素,可以自行修改)
  /// </summary>
  public int FontSize
  {
    get { return fontSize; }
    set { fontSize = value; }
  } 
  int padding = 4;
  /// <summary>
  /// 边框补(默认4像素)
  /// </summary>
  public int Padding
  {
    get { return padding; }
    set { padding = value; }
  } 
  bool chaos = true;
  /// <summary>
  /// 是否输出燥点(默认输出)
  /// </summary>
  public bool Chaos
  {
    get { return chaos; }
    set { chaos = value; }
  } 
  Color chaosColor = Color.LightGray;
  /// <summary>
  /// 输出燥点的颜色(默认灰色)
  /// </summary>
  public Color ChaosColor
  {
    get { return chaosColor; }
    set { chaosColor = value; }
  }
  Color backgroundColor = Color.White;
  /// <summary>
  /// 自定义背景色(默认白色)
  /// </summary>
  public Color BackgroundColor
  {
    get { return backgroundColor; }
    set { backgroundColor = value; }
  } 
  Color[] colors = { Color.Black, Color.Red, Color.DarkBlue, Color.Green, Color.Orange, Color.Brown, Color.DarkCyan, Color.Purple };
  /// <summary>
  /// 自定义随机颜色数组
  /// </summary>
  public Color[] Colors
  {
    get { return colors; }
    set { colors = value; }
  }
  string[] fonts = { "Arial", "Georgia" };
  /// <summary>
  /// 自定义字体数组
  /// </summary>
  public string[] Fonts
  {
    get { return fonts; }
    set { fonts = value; }
  } 

  #region 产生波形滤镜效果

  private const double PI = 3.1415926535897932384626433832795;
  private const double PI2 = 6.283185307179586476925286766559;
  /// <summary>
  /// 正弦曲线Wave扭曲图片(Edit By 51aspx.com)
  /// </summary>
  /// <param name="srcBmp">图片路径</param>
  /// <param name="bXDir">如果扭曲则选择为True</param>
  /// <param name="nMultValue">波形的幅度倍数,越大扭曲的程度越高,一般为3</param>
  /// <param name="dPhase">波形的起始相位,取值区间[0-2*PI)</param>
  /// <returns></returns>
  public System.Drawing.Bitmap TwistImage(Bitmap srcBmp, bool bXDir, double dMultValue, double dPhase)
  {
    System.Drawing.Bitmap destBmp = new Bitmap(srcBmp.Width, srcBmp.Height);
    // 将位图背景填充为白色
    System.Drawing.Graphics graph = System.Drawing.Graphics.FromImage(destBmp);
    graph.FillRectangle(new SolidBrush(System.Drawing.Color.White), 0, 0, destBmp.Width, destBmp.Height);
    graph.Dispose();
    double dBaseAxisLen = bXDir ? (double)destBmp.Height : (double)destBmp.Width;
    for (int i = 0; i < destBmp.Width; i++)
    {
      for (int j = 0; j < destBmp.Height; j++)
      {
        double dx = 0;
        dx = bXDir ? (PI2 * (double)j) / dBaseAxisLen : (PI2 * (double)i) / dBaseAxisLen;
        dx += dPhase;
        double dy = Math.Sin(dx);
        // 取得当前点的颜色
        int nOldX = 0, nOldY = 0;
        nOldX = bXDir ? i + (int)(dy * dMultValue) : i;
        nOldY = bXDir ? j : j + (int)(dy * dMultValue);
        System.Drawing.Color color = srcBmp.GetPixel(i, j);
        if (nOldX >= 0 && nOldX < destBmp.Width
         && nOldY >= 0 && nOldY < destBmp.Height)
        {
          destBmp.SetPixel(nOldX, nOldY, color);
        }
      }
    }
    return destBmp;
  }

  #endregion

  /// <summary>
  /// 生成校验码图片
  /// </summary>
  /// <param name="code">验证码</param>
  /// <returns></returns>
  public Bitmap CreateImage(string code)
  {
    int fSize = FontSize;
    int fWidth = fSize + Padding;
    int imageWidth = (int)(code.Length * fWidth) + 4 + Padding * 2;
    int imageHeight = fSize * 2 + Padding * 2;
    System.Drawing.Bitmap image = new System.Drawing.Bitmap(imageWidth, imageHeight);
    Graphics g = Graphics.FromImage(image);
    g.Clear(BackgroundColor);
    //给背景添加随机生成的燥点
    if (this.Chaos)
    {
      Pen pen = new Pen(ChaosColor, 0);
      int c = Length * 10;
      for (int i = 0; i < c; i++)
      {
        int x = rnd.Next(image.Width);
        int y = rnd.Next(image.Height);
        g.DrawRectangle(pen, x, y, 1, 1);
      }
    }
    int left = 0, top = 0, top1 = 1, top2 = 1;
    int n1 = (imageHeight - FontSize - Padding * 2);
    int n2 = n1 / 4;
    top1 = n2;
    top2 = n2 * 2;
    Font f;
    Brush b;
    int cindex, findex;
    //随机字体和颜色的验证码字符
    for (int i = 0; i < code.Length; i++)
    {
      cindex = rnd.Next(Colors.Length - 1);
      findex = rnd.Next(Fonts.Length - 1);
      f = new System.Drawing.Font(Fonts[findex], fSize, System.Drawing.FontStyle.Bold);
      b = new System.Drawing.SolidBrush(Colors[cindex]);
      if (i % 2 == 1)
      {
        top = top2;
      }
      else
      {
        top = top1;
      }
      left = i * fWidth;
      g.DrawString(code.Substring(i, 1), f, b, left, top);
    }
    //画一个边框 边框颜色为Color.Gainsboro
    g.DrawRectangle(new Pen(Color.Gainsboro, 0), 0, 0, image.Width - 1, image.Height - 1);
    g.Dispose();
    //产生波形(Add By 51aspx.com)
    image = TwistImage(image, true, 8, 4);
    return image;
  }
  /// <summary>
  /// 生成随机字符码
  /// </summary>
  /// <param name="codeLen">字符串长度</param>
  /// <param name="zhCharsCount">中文字符数</param>
  /// <returns></returns>
  public string CreateVerifyCode(int codeLen, int zhCharsCount)
  {
    char[] chs = new char[codeLen];
    int index;
    for (int i = 0; i < zhCharsCount; i++)
    {
      index = rnd.Next(0, codeLen);
      if (chs[index] == '')
        chs[index] = CreateZhChar();
      else
        --i;
    }
    for (int i = 0; i < codeLen; i++)
    {
      if (chs[i] == '')
        chs[i] = CreateEnOrNumChar();
    }
    return new string(chs, 0, chs.Length);
  }
  /// <summary>
  /// 生成默认长度5的随机字符码
  /// </summary>
  /// <returns></returns>
  public string CreateVerifyCode()
  {
    return CreateVerifyCode(Length, 0);
  }
  /// <summary>
  /// 生成英文或数字字符
  /// </summary>
  /// <returns></returns>
  protected char CreateEnOrNumChar()
  {
    return EnglishOrNumChars[rnd.Next(0, EnglishOrNumChars.Length)];
  }
  /// <summary>
  /// 生成汉字字符
  /// </summary>
  /// <returns></returns>
  protected char CreateZhChar()
  {
    //若提供了汉字集,查询汉字集选取汉字
    if (ChineseChars.Length > 0)
    {
      return ChineseChars[rnd.Next(0, ChineseChars.Length)];
    }
    //若没有提供汉字集,则根据《GB2312简体中文编码表》编码规则构造汉字
    else
    {
      byte[] bytes = new byte[2];
      //第一个字节值在0xb0, 0xf7之间
      bytes[0] = (byte)rnd.Next(0xb0, 0xf8);
      //第二个字节值在0xa1, 0xfe之间
      bytes[1] = (byte)rnd.Next(0xa1, 0xff);
      //根据汉字编码的字节数组解码出中文汉字
      string str1 = Encoding.GetEncoding("gb2312").GetString(bytes);
      return str1[0];
    }
  }
}
  这里面大量使用了51aspx.com的代码,在此表示感谢。这里的主要改进在于支持生成中英文混合的验证码。中文的生成有两种方式,一是根据《GB2312简体中文编码表》编码规则构造汉字,二是从一个选定的中文字符集合中随即选取汉字。实现很简单,参考函数protected char CreateZhChar(),在此不赘述。

  最后说一下验证码的使用,下面是一个例子: 

  <img src="VerifyCode.aspx" id="valiCode" alt="验证码" />

  <a title="刷新验证码" href="http://j5c.ddvip.com/index.php#" onclick="javascript:document.getElementById('valiCode').src='VerifyCode.aspx?id='+Math.random();return false;">看不清,换张图片?</a>  这里有个小技巧,就是在刷新验证码的使用,验证码的URL后面用了随机参数以欺骗浏览器重新请求。

发表在 article | 红马版验证码实现(中文+变形+噪点)已关闭评论

table 冻结

 http://kb.cnblogs.com/a/2320795/

http://www.darkthread.net/MiniAjaxLab/ScrollTable/

 

 

 Table冻结行列需要的CSS

http://www.cnblogs.com/duanwg/archive/2006/07/12/448757.html

 

 table冻结第一行

http://blog.csdn.net/digyso888/article/details/5959228

发表在 article | table 冻结已关闭评论

mysql cursor 游标示例

create PROCEDURE ut()
begin

declare _done int default 0;
declare _id bigint;
declare _type tinyint;

declare cur cursor for select gn_number from groupnumber;
declare continue handler for not found set _done = 1;

open cur;

repeat
fetch cur into _id;
if not _done then
   select ai_type into _type from alcoholinfo where ai_number=_id limit 1;                   
   update groupnumber set gn_alcoholtype=_type where gn_number=_id;
end if;
until _done end repeat;
close cur;

end;

call ut();
drop PROCEDURE ut;

发表在 db | 标签为 , | mysql cursor 游标示例已关闭评论

php gzip

GZIP(GNU-ZIP)是一种压缩技术。经过GZIP压缩后页面大小可以变为原来的30%甚至更小。这样用户浏览的时候就会感觉很爽很愉快!
要实现GZIP压缩页面需要浏览器和服务器共同支持,实际上就是服务器压缩,传到浏览器后浏览器解压并解析。浏览器那边不需要我们担心,因为现在绝大多数浏览器都支持解析GZIP过的页面。我们只要把页面在服务器端压缩再输出到浏览器就行了。

有点罗嗦,下面说正事:

正如要制作压缩饼干,先要拿到原料,要压缩一个页面,首先要获得要输出的内容。PHP中的ob_start()(ob => output buffer)函数可以实现这个功能,它可以把程序里准备输出的内容先放到一个叫做“缓冲区”的地方,当然,你可以理解为制作压缩饼干的暂时放原料的工作台。
这个函数一定要在页面输出之前使用,所以一般把它放在代码的最顶端。因为它就像是一个工作台,所以你要在原料到来之前就要准备好它,否则原料来了没地方放,会出问题的。用ob_start()得到要压缩的页面之后,我们就可以制作压缩饼干了,不对,应该是可以压缩页面了!不过好像还缺少一台压缩机, EZ,我们用PHP带的zlib扩展做一台:

 // $content 就是要压缩的页面内容,或者说饼干原料
function ob_gzip($content){    
    if(    !headers_sent() && // 如果页面头部信息还没有输出
        extension_loaded("zlib") && // 而且zlib扩展已经加载到PHP中
        strstr($_SERVER["HTTP_ACCEPT_ENCODING"],"gzip")) //而且浏览器说它可以接受GZIP的页面
    {
        $content = gzencode($content." \n//此页已压缩",9); 为准备压缩的内容贴上“//此页已压缩”的注释标签,然后用zlib提供的gzencode()函数执行级别为9的压缩,这个参数值范围是0-9,0表示无压缩,9表示最大压缩,当然压缩程度越高越费CPU。
        
        //然后用header()函数给浏览器发送一些头部信息,告诉浏览器这个页面已经用GZIP压缩过了!
        header("Content-Encoding: gzip");
        header("Vary: Accept-Encoding");
        header("Content-Length: ".strlen($content));
    }
    return $content; //返回压缩的内容,或者说把压缩好的饼干送回工作台。
}

压缩机做好了之后,我们把压缩机放到工作台上,于是原来的ob_start()变成

ob_start('ob_gzip'); //没错,就是给ob_start()加一个参数,参数名就是我们刚才做的“压缩机”的函数名。这样当内容进入缓冲区后PHP就会调用ob_gzip函数把它压缩了。

好了,所有的工作已完成,最后交货:

ob_end_flush(); //结束缓冲区,输出内容。当然,不用这个函数也行,因为程序执行到最后会自动将缓冲区内容输出。

完整的示例如下:

<?php
//启用一个带有ob_gzip压缩机的工作台
ob_start('ob_gzip');
//准备一些待压缩的内容
for($i=0; $i<100; $i++)
{
    echo('这里是压缩饼干的原料,这里是压缩饼干的原料,原料');
}
//输出压缩成果
ob_end_flush();
//这是ob_gzip压缩机
function ob_gzip($content)
{   
    if(    !headers_sent() &&
        extension_loaded("zlib") &&
        strstr($_SERVER["HTTP_ACCEPT_ENCODING"],"gzip"))
    {
        $content = gzencode($content." \n//此页已压缩",9);
       
        header("Content-Encoding: gzip");
        header("Vary: Accept-Encoding");
        header("Content-Length: ".strlen($content));
    }
    return $content;
}
?>

经过实际测试,上面代码中如果不用GZIP,是4.69KB=4802.56B,启用GZIP后缩小为104B ,呃……我数学可能不好,自己算下压缩到了原来的百分之多少吧。。

另外,下面是用FlashGet获取的日志信息,可以看到我们程序里加的header信息:

Fri Jan 25 17:53:10 2008 HTTP/1.1 200 OK
Fri Jan 25 17:53:10 2008 Server: Microsoft-IIS/5.1
Fri Jan 25 17:53:10 2008 Date: Fri, 25 Jan 2008 09:53:10 GMT
Fri Jan 25 17:53:10 2008 Connection: close
Fri Jan 25 17:53:10 2008 X-Powered-By: PHP/5.2.5
Fri Jan 25 17:53:10 2008 Content-Encoding: gzip
Fri Jan 25 17:53:10 2008 Vary: Accept-Encoding
Fri Jan 25 17:53:10 2008 Content-Length: 104
Fri Jan 25 17:53:10 2008 Content-type: text/html

发表在 article | 标签为 , | php gzip已关闭评论

分词、全文索引、lucene、IKAnalyzer

索引的话,推荐使用lucene,去下一个lucene的API文档,把field,document,indexwriter,indexsearcher,这几个类看一下,还是比较好懂的。
分词器的建议使用,IKAnalyzer,在开源中文分词器里算是很好的,而且一直在稳定的更新版本。

 

.NET :

    HubbleDotNet

发表在 article | 标签为 , , | 分词、全文索引、lucene、IKAnalyzer已关闭评论