1 信息检索概述

1.1 传统检索方式的缺点

    • 文件检索
        操作系统常见的是硬盘文件检索
        文档检索:整个文档打开时已经加载到内存了;
        缺点:全盘遍历,慢,内存的海量数据
    • 数据库检索
        like "%三星%" 全表遍历;
        like "三星%" 最左特性 不会全表遍历;
        
        无法满足海量数据下准确迅速的定位
        mysql 单表数据量---千万级
        oracle 单表数据量---亿级
  

  总结:传统的方式无法满足检索的需求(迅速,准确,海量)

 

2 全文检索技术(大型互联网公司的搜索功能都是全文检索)

2.1 定义:

  • 在海量的信息中,通过固定的数据结构引入索引文件,利用对索引文件的处理实现对应数据的快速定位等功能的技术;
  • 信息检索系统(全文检索技术的应用,搜索引擎百度,google)
  • 信息采集:通过爬虫技术,将公网的海量非结构化数据爬去到本地的分布式存储系统进行存储
  • 信息整理:非结构化数据无法直接提供使用,需要整理,整理成索引文件
  • 信息查询:通过建立一个搜索的应用,提供用户的入口进行查询操作,利用查询条件搜索索引文件中的有效数据;

 2.2结构

问题:非结构化数据,海量数据如何整理成有结构的索引文件(索引文件到底什么结构)?

2.3 倒排索引

   索引文件,是全文检索技术的核心内容,创建索引,搜索索引也是核心,搜索在创建之后的;
      如何将海量数据计算输出成有结构的索引文件,需要严格规范的计算逻辑-----倒排索引的计算
    
      以网页为例:
      假设爬虫系统爬去公网海量网页(2条);利用倒排索引的计算逻辑,将这2个非结构化的网页信息数据整理成索引文件;
    
      源数据: 标题,时间,作者,留言,内容
      网页1(id=1): 王思聪的IG战队获得LOL世界冠军,结束长达8年的遗憾
      网页2(id=2): 王思聪又换女朋友了吗?嗯,天天换.
    
      倒排索引的第一步:计算分词(数据内容)
          分词:将数据字符串进行切分,形成最小意义的词语  (不同语言底层实现是不一样的)
          并且每个分词计算的词语都会携带计算过程中的一些参数
          词语(来源的网页id,当前网页中该词语出现的频率,出现的位置)
    
      网页1: 王思聪(1,1,1),IG(1,1,2),战队(1,1,3), LOL(1,1,4) 世界(1,1,5)
      网页2: 王思聪(2,1,1),女朋友(2,1,1),天天(2,1,1);
    
      倒排索引第二步:合并分词结果
        合并结果:王思聪([1,2],[1,1],[1,1]),IG(1,1,2),战队(1,1,3), LOL(1,1,4) 世界(1,1,5),女朋友(2,1,1),天天(2,1,1);
        合并逻辑:所有的网页的分词计算结果一定有重复的分词词汇,合并后所有参数也一起合并,结果形成了一批索引结构的数据;
      倒排索引第三步:源数据整理document对象

      document是索引文件中的文档对象,最小的数据单位(数据库中的一行数据)每个document对应一个网页

     倒排索引第四步:形成索引文件
        将网页的数据对象(document)和分词合并结果(index)一起存储到存储位置,形成整体的索引文件

  索引文件结构:

    数据对象

    合并分词结果

  

  对索引文件中的分词合并后的数据进行复杂的计算处理,获取我们想要的数据集合(document的集合)

3 Lucene

3.1介绍

  是一个全文检索引擎工具包,hadoop的创始人Doug Cutting开发,2000年开始,每周花费2天,完成了lucene的第一个版本;引起搜索界的巨大轰动; java开发的工具包;

3.2 特点

  •   稳定,索引性能高 (创建和搜索的性能)
  •   现代磁盘每小时能索引150G数据(读写中)
  •   对内存要求1MB栈内存
  •   增量索引和批量索引速度一样快
  •   索引的数据占整体索引文件20%
  •   支持多种主流搜索功能.

3.3分词代码测试

  准备依赖的jar包(lucene6.0)

  

    <!-- lucene查询扩展转化器 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>6.0.0</version>
</dependency>
<!-- lucene自带的智能中文分词器 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-smartcn</artifactId>
<version>6.0.0</version>
</dependency>
<!-- lucene核心功能包 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>6.0.0</version>
</dependency>

  lucene分词测试

  索引的查询都是基于分词的计算结果完成的,这种计算分词的过程叫做词条化,得到的每一个词汇称之为词项,lucene提供抽象类Analyzer表示分词器对象,不同的实现类来自不同的开发团队,实现这个Analyzer完成各自分词的计算;lucene也提供了多种分词器计算

  •       StandardAnalyzer 标准分词器,分词英文
  •   WhitespaceAnalyzer 空格分词器 
  • SimpleAnalyzer 简单分词器
  •    SmartChineseAnalyzer 智能中文分词器

  

 package com.jt.test.lucene;

 import java.io.StringReader;

 import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer;
import org.apache.lucene.analysis.core.SimpleAnalyzer;
import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.junit.Test; /**
*测试不同分词器对同一个字符串的分词结果
*/
public class AnalyzerTest { //编写一个静态方法, String str,Analayzer a
//实现传入的字符串进行不同分词器的计算词项结果
public static void printA(Analyzer analyzer,String str) throws Exception{
//org.apache.lucene
//获取str的刘对象
StringReader reader=new StringReader(str);
//通过字符串流获取分词词项流,每个不同的analyzer实现对象
//词项流的底层计算时不一样的;
//fieldName是当前字符串 代表的document的域名/属性名
TokenStream tokenStream = analyzer.tokenStream("name", reader);
//对流进行参数的重置reset,才能获取词项信息
tokenStream.reset();
//获取词项的打印结果
CharTermAttribute attribute
= tokenStream.getAttribute(CharTermAttribute.class);
while(tokenStream.incrementToken()){
System.out.println(attribute.toString());
}
}
@Test
public void test() throws Exception{
String str="近日,有网友偶遇诸葛亮王思聪和网红焦可然一起共进晚餐,"
+ "照片中,焦可然任由王思聪点菜,自己则专注玩手机,";
//创建不同的分词计算器
Analyzer a1=new StandardAnalyzer();
Analyzer a2=new SmartChineseAnalyzer();
Analyzer a3=new SimpleAnalyzer();
Analyzer a4=new WhitespaceAnalyzer();
//调用方法测试不同分词器的分词效果
System.out.println("*******标准分词器*******");
AnalyzerTest.printA(a1, str);
System.out.println("*******智能中文分词器*******");
AnalyzerTest.printA(a2, str);
System.out.println("*******简单分词器*******");
AnalyzerTest.printA(a3, str);
System.out.println("*******空格分词器*******");
AnalyzerTest.printA(a4, str);
}
}

3.4中文分词器常用IKAnalyzer

  可以实现中文的只能分词,并且支持扩展,随着语言的各种发展,可以利用ext.dic文档补充词项,也支持停用,stop.dic;

  • 实现类的编写(IKAnalyzer需要自定义实现一些类)

     package com.jt.lucene.IK;
    
     import java.io.IOException;
    
     import org.apache.lucene.analysis.Tokenizer;
    import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
    import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
    import org.apache.lucene.analysis.tokenattributes.TypeAttribute;
    import org.wltea.analyzer.core.IKSegmenter;
    import org.wltea.analyzer.core.Lexeme; public class IKTokenizer6x extends Tokenizer{
    //ik分词器实现
    private IKSegmenter _IKImplement;
    //词元文本属性
    private final CharTermAttribute termAtt;
    //词元位移属性
    private final OffsetAttribute offsetAtt;
    //词元分类属性
    private final TypeAttribute typeAtt;
    //记录最后一个词元的结束位置
    private int endPosition;
    //构造函数,实现最新的Tokenizer
    public IKTokenizer6x(boolean useSmart){
    super();
    offsetAtt=addAttribute(OffsetAttribute.class);
    termAtt=addAttribute(CharTermAttribute.class);
    typeAtt=addAttribute(TypeAttribute.class);
    _IKImplement=new IKSegmenter(input, useSmart);
    } @Override
    public final boolean incrementToken() throws IOException {
    //清除所有的词元属性
    clearAttributes();
    Lexeme nextLexeme=_IKImplement.next();
    if(nextLexeme!=null){
    //将lexeme转成attributes
    termAtt.append(nextLexeme.getLexemeText());
    termAtt.setLength(nextLexeme.getLength());
    offsetAtt.setOffset(nextLexeme.getBeginPosition(),
    nextLexeme.getEndPosition());
    //记录分词的最后位置
    endPosition=nextLexeme.getEndPosition();
    typeAtt.setType(nextLexeme.getLexemeText());
    return true;//告知还有下个词元
    }
    return false;//告知词元输出完毕
    } @Override
    public void reset() throws IOException {
    super.reset();
    _IKImplement.reset(input);
    } @Override
    public final void end(){
    int finalOffset = correctOffset(this.endPosition);
    offsetAtt.setOffset(finalOffset, finalOffset);
    } }
     package com.jt.lucene.IK;
    
     import org.apache.lucene.analysis.Analyzer;
    import org.apache.lucene.analysis.Tokenizer; public class IKAnalyzer6x extends Analyzer{
    private boolean useSmart;
    public boolean useSmart(){
    return useSmart;
    }
    public void setUseSmart(boolean useSmart){
    this.useSmart=useSmart;
    }
    public IKAnalyzer6x(){
    this(false);//IK分词器lucene analyzer接口实现类,默认细粒度切分算法
    }
    //重写最新版本createComponents;重载analyzer接口,构造分词组件
    @Override
    protected TokenStreamComponents createComponents(String filedName) {
    Tokenizer _IKTokenizer=new IKTokenizer6x(this.useSmart);
    return new TokenStreamComponents(_IKTokenizer);
    }
    public IKAnalyzer6x(boolean useSmart){
    super();
    this.useSmart=useSmart;
    } }
  • 手动导包
    build-path添加依赖的jar包到当前工程   IKAnalyzer2012_u6.jar
  • 扩展词典和停用词典的使用
        <entry key="ext_dict">ext.dic;</entry>
    <!--用户可以在这里配置自己的扩展停止词字典-->
    <entry key="ext_stopwords">stopword.dic;</entry>
    和配置文件同目录下准备2个词典;
    确定分词器使用的代码编码字符集与词典编码是同一个

4 Lucene创建索引

4.1概念

查询(Query):对于全文检索,最终都是使用词项指向一批document文档对象的集合,利用对词项的逻辑计算可以实现不同的查询功能;查询时构建的对象就是Query;

文档(document):是索引文件中的一个最小的数据单位,例如非结构化数据中的网页将会封装成一个document存储在索引文件中,而封装过程中写在对象里的所有数据都会根据逻辑进行分词计算,不同的结构数据源会对应创建具有不同属性的document对象

文档的域(Field):每个文档对象根据不同的数据来源封装Field的名称,个数和数据,导致document的结构可能各不相同

词条化(tokenization):计算分词过程

词项(Term):计算分词的结果每一个词语都是一个项

4.2 创建一个空的索引文件

  • 指向一个索引文件位置
  • 生成输出对象,进行输出
     @Test
    public void emptyIndex() throws Exception{
    //指向一个文件夹位置
    Path path = Paths.get("./index01");
    Directory dir=FSDirectory.open(path);
    //生成一个输出对象 writer 需要分词计算器,配置对象
    Analyzer analyzer=new IKAnalyzer6x();
    IndexWriterConfig config=new IndexWriterConfig(analyzer);
    IndexWriter writer=new IndexWriter(dir,config);
    //写出到磁盘,如果没有携带document,生成一个空的index文件
    writer.commit(); }

    在索引中创建数据

  • 将源数据读取封装成document对象,根据源数据的结构定义document的各种field;
         @Test
    public void createData() throws Exception{
    /*
    * 1 指向一个索引文件
    * 2 生成输出对象
    * 3 封装document对象(手动填写数据)
    * 4 将document添加到输出对象索引文件的输出
    */
    //指向一个文件夹位置
    Path path = Paths.get("./index02");
    Directory dir=FSDirectory.open(path);
    //生成一个输出对象 writer 需要分词计算器,配置对象
    Analyzer analyzer=new IKAnalyzer6x();
    IndexWriterConfig config=new IndexWriterConfig(analyzer);
    IndexWriter writer=new IndexWriter(dir,config);
    //构造document对象
    Document doc1=new Document();//新闻 作者,内容,网站链接地址
    Document doc2=new Document();//商品页面,title,price,详情,图片等
    doc1.add(new TextField("author", "韩寒", Store.YES));
    doc1.add(new TextField("content","我是上海大金子",Store.NO));
    doc1.add(new StringField("address", "http://www.news.com", Store.YES));
    doc2.add(new TextField("title", "三星(SAMSUNG) 1TB Type-c USB3.1 移动固态硬盘",Store.YES));
    doc2.add(new TextField("price","1699",Store.YES));
    doc2.add(new TextField("desc","不怕爆炸你就买",Store.YES));
    doc2.add(new StringField("image", "image.jt.com/1/1.jpg",
    Store.YES));
    //将2个document对象添加到writer中写出到索引文件;
    writer.addDocument(doc1);
    writer.addDocument(doc2);
    //写出到磁盘,如果没有携带document,生成一个空的index文件
    writer.commit();
    }
  • 问题一:Store.yes和no的区别是什么?????
    • Store,yes和no的区别在于,创建索引数据,非领导数据是否在输出到索引时存储到索引文件,按照类的类型进行计算分词,一些过大的数据,查询不需要的数据可以不存储在索引文件中(例如网页内容;计算不计算分词,和存储索引没有关系)
  • 问题二:StringField和TextField的区别是什么
    • 域的数据需要进行分词计算如果是字符串有两种对应的域类型
    • 其中StringField表示不对数据进行分词计算,以整体形势计算索引
    • TextField表示对数据进行分词计算,以词形势计算索引
  • 问题三:    问题3:显然document中的不同域field应该保存不同的数据类型
    • 数据中的类型不同,存储的数据计算逻辑也不同;
    • int long double的数字数据如果使用字符串类型保存域
    • 只能做到一件事--存储在索引文件上
    • 以上几个Point类型的域会对数据进行二进制数字的计算;
    • 范围查找,只要利用intPoint,longPoint对应域存储到document对象后
    • 这种类型的数据在分词计算中就具有了数字的特性 > <
    • intPoint只能存储数值,不存储数据
    • 如果既想记性数字特性的使用,又要存储数据;需要使用StringField类型

    

5 Lucene索引的搜索

5.1词项查询

   单域查询:查询条件封装指定的域,给定Term(词项),lucene调用搜索api可以根据指定的条件,将所有当前查询的这个域中的分词结果进行比对,如果比对成功指向document对象返回数据;

 @Test
public void search() throws Exception{
/*
* 1 指向索引文件
* 2 构造查询条件
* 3 执行搜索获取返回数据
* 4 从返回数据中获取document对象
*/
Path path = Paths.get("./index02");
Directory dir=FSDirectory.open(path);
//获取与输入流reader,从这里生产查询的对象
IndexReader reader=DirectoryReader.open(dir);
IndexSearcher search=new IndexSearcher(reader);
//由于使用的是term查询,无需包装analyzer;
//构造查询条件;
Term term=new Term("title","三星");
Query termQuery=new TermQuery(term);
//查询,获取数据
TopDocs docs = search.search(termQuery, 10);
//将docs转化成document的获取逻辑
ScoreDoc[] scoreDoc=docs.scoreDocs;
for (ScoreDoc sd : scoreDoc) {
//没循环一次,都可以获取document对象一个
Document doc=search.doc(sd.doc);
System.out.println("author:"+doc.get("author"));
System.out.println("content:"+doc.get("content"));
System.out.println("address:"+doc.get("address"));
System.out.println("title:"+doc.get("title"));
System.out.println("image:"+doc.get("image"));
System.out.println("price:"+doc.get("price"));
System.out.println("rate:"+doc.get("rate"));
System.out.println("desc:"+doc.get("desc"));}}

  多域查询:指定的查询多个field,传递参数的字符串会被先进行分词计算,利用分词计算的结果(多个词项),比对所有的域中的词项,只要满足一个与对应一个词项的最小要求就可以拿到当前的document范围.

     @Test
public void multiQuery() throws Exception{
//使用parser,转化查询条件,需要传递analyzer,查询的字符串需要计算分词
Path path=Paths.get("./index02");
Directory dir=FSDirectory.open(path);
IndexReader reader=DirectoryReader.open(dir);
IndexSearcher search=new IndexSearcher(reader);
//用到分词器计算查询的条件,必须和创建索引时用的分词一致;
Analyzer analyzer=new IKAnalyzer6x();
//准备查询的2个域desc title
String[] fields={"desc","title"};
//获取转化器,将查询的字符串进行分词计算,获取多于查询的对象query
MultiFieldQueryParser parser=
new MultiFieldQueryParser(fields,analyzer);
Query multiFieldQuery=parser.parse("爆");//三星,爆炸
TopDocs docs=search.search(multiFieldQuery, 10);
ScoreDoc[] scoreDocs=docs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
Document doc=search.doc(scoreDoc.doc);
System.out.println("author:"+doc.get("author"));
System.out.println("content:"+doc.get("content"));
System.out.println("address:"+doc.get("address"));
System.out.println("title:"+doc.get("title"));
System.out.println("image:"+doc.get("image"));
System.out.println("price:"+doc.get("price"));
System.out.println("rate:"+doc.get("rate"));
System.out.println("desc:"+doc.get("desc"));}}

  布尔查询:可以封装多个查询条件的对象query,由布尔查询条件实现多个其他查询的逻辑关系 MUST必须包含 MUST_NOT必须不包含.

  对应一个布尔查询条件,一个没有must条件的布尔查询可以有一个或者多个should,有must条件的布尔查询,should不起作用;

  MUST:匹配结果必须包含这个条件

  MUST_NOT:匹配结果必须不包含这个条件

  SHOULD:没有must的booleanClause中,可以有1个或者多个should,一旦有must条件,should就没有作用了

  FILTER:和must效果一样,必须包含,但是查询过程不参加评分计算.

     @Test
public void booleanQuery() throws Exception{
Path path=Paths.get("./index02");
Directory dir=FSDirectory.open(path);
IndexReader reader=DirectoryReader.open(dir);
IndexSearcher search=new IndexSearcher(reader);
//设置多个查询的query,可以使任何类型,TermQuery
//准备查询的2个域desc title
Analyzer analyzer=new IKAnalyzer6x();
String[] fields={"desc","title"};
//获取转化器,将查询的字符串进行分词计算,获取多于查询的对象query
MultiFieldQueryParser parser=
new MultiFieldQueryParser(fields,analyzer);
Query multiFieldQuery=parser.parse("三星爆炸");//三星,爆炸
Query query1=new TermQuery(new Term("title","三星"));
Query query2=new TermQuery(new Term("desc","爆炸"));
//构造一个布尔的查询条件 先构造查询的逻辑对象
BooleanClause bc1=new BooleanClause(query1,Occur.MUST);
//BooleanClause bc2=new BooleanClause(query2,Occur.MUST_NOT);
BooleanClause bc2=new BooleanClause(multiFieldQuery,Occur.FILTER);
BooleanQuery boolQuery=
new BooleanQuery.Builder().add(bc1).add(bc2).build();
TopDocs docs=search.search(boolQuery, 10);
ScoreDoc[] scoreDocs=docs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
Document doc=search.doc(scoreDoc.doc);
System.out.println("author:"+doc.get("author"));
System.out.println("content:"+doc.get("content"));
System.out.println("address:"+doc.get("address"));
System.out.println("title:"+doc.get("title"));
System.out.println("image:"+doc.get("image"));
System.out.println("price:"+doc.get("price"));
System.out.println("rate:"+doc.get("rate"));
System.out.println("desc:"+doc.get("desc"));
System.out.println("文档评分:"+scoreDoc.score);
}
}

  范围查询:对查询条件进行范围的定义,查询某个域的数据范围,只能对INTPOINT LONGPOINT DOUBLEPOINT FLOATPOINT类型的域做查询;

  Query rangeQuery=IntPoint.newRangeQuery("rate", 200, 1000);

  前缀查询:非常类似数据库中的like"三星%",查询条件表示前缀,只要当前的域满足前缀的内容,就能够查询document;

      //构造前缀的词项
    Term term=new Term("desc","爆");
    Query query=new PrefixQuery(term);
  前缀查询中,前缀本身必须是索引文件的词项,否则无法查到

  模糊查询
    Term term=new Term("name","tramp");
    FuzzyQuery query=new FuzzyQuery(term);
    使用term中的词项,但是不在进行精确的匹配,可以查到具有trump词项;
    曰和日, 晶和品

  通配符查询
    WildcardQuery query= new WildcardQuery(new Term("name","爆?")) //效率不高,小范围的遍历
  ?匹配所有内容,可以补充前缀查询中使用Term的问题;

Lucene-----信息检索技术的更多相关文章

  1. Lucene全文检索技术学习

    ---------------------------------------------------------------------------------------------------- ...

  2. Lucene全文检索技术

    Lucene全文检索技术 今日大纲 ●    搜索的概念.搜索引擎原理.倒排索引 ●    全文索引的概念 ●    使用Lucene对索引进行CRUD操作 ●    Lucene常用API详解 ●  ...

  3. Lucene:信息检索与全文检索

    目录 信息检索的概念 信息检索技术的分类 全文检索与数据库查询对比 全文检索工具一般由三部分构成 全文检索中建立索引和进行检索的流程 索引里面究竟存什么 如何创建索引 如何对索引进行检索 Lucene ...

  4. 后端技术杂谈3:Lucene基础原理与实践

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  5. (转)全文检索技术学习(二)——配置Lucene的开发环境

    http://blog.csdn.net/yerenyuan_pku/article/details/72589380 Lucene下载 Lucene是开发全文检索功能的工具包,可从官方网站http: ...

  6. Lucene学习总结:全文检索的基本原理

    一.总论 根据http://lucene.apache.org/java/docs/index.html定义: Lucene是一个高效的,基于Java的全文检索库. 所以在了解Lucene之前要费一番 ...

  7. Lucene学习笔记:一,全文检索的基本原理

    一.总论 根据http://lucene.apache.org/java/docs/index.html定义: Lucene是一个高效的,基于Java的全文检索库. 所以在了解Lucene之前要费一番 ...

  8. Lucene学习总结之一:全文检索的基本原理

    一.总论 根据http://lucene.apache.org/java/docs/index.html 定义: Lucene 是一个高效的,基于Java 的全文检索库. 所以在了解Lucene之前要 ...

  9. Lucene 4.X 全套教程

    http://www.cnblogs.com/forfuture1978/category/300665.html Lucene 4.X 倒排索引原理与实现: (3) Term Dictionary和 ...

  10. Lucene原理一

    Lucene 是一个高效的,基于Java 的全文检索库. 所以在了解Lucene之前要费一番工夫了解一下全文检索. 那么什么叫做全文检索呢?这要从我们生活中的数据说起. 我们生活中的数据总体分为两种: ...

随机推荐

  1. idapython 开发

    调试方法 使用 pydevd 然后在需要调试处加入调试代码 GetOperandValue 作用 参数1: ea 虚拟地址 参数2: 操作数号 返回指令的操作数的被解析过的值 文档 def GetOp ...

  2. BS网站架构演变

    BS网站架构演变 网站架构的整个演变过程主要是围绕大数据和高并发这两个问题展开的,解决的方案主要分为使用缓存和使用多资源两种类型.多资源主要指多存储(包括多内存).多CPU和多网络,对于多资源来说又可 ...

  3. 【Python】Java程序员学习Python(十)— 类、包和模块

    我觉得学习到现在应该得掌握Python的OOP编程了,但是现在还没有应用到,先留一个坑. 一.类和对象 说到类和对象其实就是在说面向对象编程,学完Java以后我觉得面向对象编程还是很不错的,首先封装了 ...

  4. PyQt4(简单界面)

    import sys; from PyQt4 import QtCore, QtGui; app=QtGui.QApplication(sys.argv); widget=QtGui.QWidget( ...

  5. 《SQL Server 2008从入门到精通》20180627

    数据库范式理论 范式理论是为了建立冗余较小结构合理的数据库所遵循的规则.关系数据库中的关系必须满足不同的范式.目前关系数据库有六种范式:第一范式(1NF).第二范式(2NF).第三范式(3NF).BC ...

  6. .net下log4net的使用

    这里以控制台应用程序为例 首先是要添加引用: 安装后可以看到项目中多了log4net的引用: 添加应用程序配置文件app.config,配置log4net <?xml version=" ...

  7. 去除Xcode6创建工程时自带的storyboard

    去除Xcode6创建工程时自带的storyboard 1. 删除storyboard文件,并在setting里面清空加载storyboard: 2. 导入ViewController到appDeleg ...

  8. 设置全局context变量 (显示用户登录名)

    比如在每个页面的最上面部分需要显示用户的登录名称,如果不登录则显示为Guest.这部分内容在每个页面都会出现,所以可将该部分内容作为一个公共模板(如userauth.html),如然后在其他模板中进行 ...

  9. windows 下安装nodejs 要怎么设置环境变量

    windows 下安装nodejs 了,也安装了npm, 但是有时候切不能直接用request(‘ws’)这一类的东西.我觉得是确实环境变量或其他设置有问题,能否给个完整的设置方案: 要设置两个东西, ...

  10. 铁乐学Python_day05-字典dict

    1.[字典dict] Python内置了字典:dict的支持,dict全称dictionary, 在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度. 字典和列表直观上 ...