API文档搜索引擎

导航小助手

一、认识搜索引擎

二、项目目标

三、模块划分

四、创建项目

五、关于分词

六、实现索引模块

6.1 实现 Parser类

6.2 实现 Index类

6.2.1 创建 Index类

6.2.2 创建DocInfo类

6.2.3 创建 Weight类

6.2.4 实现 getDocInfo 和 getInverted方法

6.2.5 实现 addDoc方法

6.2.6 实现save方法

6.2.7 实现 load方法

6.3 在 Parser 中调用 Index

6.3.1 新增 Index实例

6.3.2 修改 parseHTML方法

6.3.3 新增save方法的调用

6.4 验证索引模块

6.5 优化制作索引速度

七、实现搜索模块

7.1 创建 DocSearcher类

7.1.1 创建 Result类

7.1.2 实现 search方法

7.1.3 验证 DocSearcher类

7.1.4 修改 Parser类

八、实现Web模块


一、认识搜索引擎

关于搜索引擎,平时的时候都是用的比较多,如:百度、搜狗、谷歌等搜索引擎,但是并没有仔细的观察过~

以搜狗搜索为例,我们打开搜狗搜索,就会发现,有一个搜索主页,并且有一个搜索框,我们可以在搜索框中搜索 想要知道的内容,点击 搜索,就可以查找搜索结果的页面~

API文档搜索引擎_第1张图片

搜索引擎 的核心功能,就是查找到 一组和用户输入的查询词(或者是一句话),所相关联的网页~

通过观察可以发现,对于每一条搜索结果,都会显示该搜索结果的 标题、描述、展示url等属性(虽然有的结果样式上可能会更复杂,但是还是会有 标题、描述、展示url属性),当我们点击一条搜索记录的时候,就跳转到相应的页面(称之为 落地页)~


搜索引擎的核心实现思路:

搜索引擎的核心目的是:在很多很多的网页中 找到和查询词相关联的内容~

那么,对于一个搜索引擎来说,首先需要获取到很多的网页,然后再根据用户输入的查询词,在这些网页中进行查找~

这就涉及到了几个问题:

  1. 搜索引擎的网页是怎么获取到的(此处主要是涉及到 "爬虫" 程序,此处不过多介绍)
  2. 用户输入查询词之后,如何让查询词和当前的这些网页进行匹配呢(这个是重点)

假设 当前已经爬取到了 1亿 个网页(html文件),用户输入了一个 "蛋糕"查询词,此时如果使用的是 "暴力搜索"的话,那么就需要把 "蛋糕"这个字符串 在这1亿个网页里面进行查找,那肯定是效率很低的(特别是对于搜索引擎来说,我们都希望 再进行搜索之后,会立刻出现我们所要查找的结果)~

因此为了更加高效的解决这个问题,大佬们专门设计了一种特殊的数据结构 —— 倒排索引~


需要知道的知识:

API文档搜索引擎_第2张图片举个例子:

API文档搜索引擎_第3张图片

在日常的生活中,我们也会有所体会(就比如说在 玩王者荣耀的时候):

API文档搜索引擎_第4张图片

二、项目目标

像搜索引擎这样的程序,是一个很复杂的程序,很难做出像百度、搜狗一样的规模庞大的搜索引擎,所以可以简化一下目标:可以做一个针对 Java API文档的搜索引擎~

API文档搜索引擎_第5张图片

通过观察可以发现,官方文档上面并没有一个搜索框,所以想要去查找某个类,但又不知道这个类在哪个包里,就会显得很麻烦~ 

因此就可以实现这样一个站内搜索引擎,让用户输入一些查询词,之后就可以找到相关的文档页面~


获取Java文档: 

要想实现这个项目,首先就需要把相关的网页文档给获取到,然后才可以继续接下来的过程~

相关网页在 oracle官方网站上面,想要把上面所有的页面获取到,并不是一件很容易的事情~

虽然说,可以通过 "爬虫"技术,把这些文档获取到,但是 "爬虫"技术 还没有学到(听说 实现"爬虫"程序 会存在法律风险的),所以就暂时放弃这个方法~

针对于 Java API文档 来说,还有更加简单的方法 —— 可以直接从官网上直接下载,因此就不必通过爬虫来实现~

Java API文档 线上版本:https://docs.oracle.com/javase/8/docs/api/index.html

Java API文档 线下版本 下载地址:https://www.oracle.com/java/technologies/downloads/

API文档搜索引擎_第6张图片

三、模块划分

1)索引模块

  • 扫描下载到的文档,分析文档内容,构建出 正排索引+倒排索引,并且把索引内容保存到文件中~
  • 加载制作好的索引,并提供一些API实现查正排和查倒排这样的功能~

2)搜索模块

  • 调用索引模块,实现一个搜索的完整过程~
  • 输入:用户的查询词;输出:完整的搜索结果(包含了很多条记录,每个记录有 标题、描述、展示url,并且点击可以跳转)~

3)Web模块

  • 需要实现一个简单的 web程序,能够通过网页的形式和用户进行交互~

四、创建项目

 API文档搜索引擎_第7张图片

API文档搜索引擎_第8张图片

五、关于分词

用户在搜索引擎中,输入的查询词,不一定真的只是一个词,也有可能是一句话:

API文档搜索引擎_第9张图片

由上面的搜索结果我们可以知道,先针对完整的句子进行分词,然后根据每个词的分词结果,分别找到相关的文档~

针对 "分词"操作,人是非常容易完成的;但是对于机器来说,这就困难很多;英文的话还好,有空格就可以了,但是中文博大精深,额,就非常的哇塞了~

比如说,"我一把把车把把住","小龙女也想过过过儿过过的生活","下雨天留客天留我不留" ~

好在,我们要想实现这个分词效果,就可以基于一些现成的第三方库,从而实现分词效果~


分词实现原理

虽然说,我们现在使用的是第三方库,并不是自己实现分词逻辑,但是还是需要了解一下 分词的原理~

第一种情况:基于词库!

        以汉语为例,虽然说,汉字很多,但是仍然是可以尝试着把所有的词都穷举在一本词典文件中,然后就可以依次的取句子中的内容:如:每隔一个字,就在词典里查一下;每隔两个字,查一下;......

        但是,这并不能把所有的词都穷举出来,而且 随着时间的推移,也会产生越来越多的新词,......,关是基于词库,并不能分出所有的词~

第二种情况:基于统计!

        利用这种方法,收集很多很多的 "语料库"(如:现有的一些文章、资料等等),接着就可以进一步进行人工标注/或者是直接统计,接着就进一步的知道了哪些词 成词的情况比较高(就类似于根据用户的习惯)~

分词的实现,就是属于 "人工智能" 典型应用的场景 ~


使用第三方库:

在Java中可以实现分词的第三方库 也是有很多的,在这里我们可以使用 ansj分词库~

        
        
        
            org.ansj
            ansj_seg
            5.1.6
        

示例:

   public static void main(String[] args) {
        //准备一个比较长的话,用来分词(中文分词)
        String str1 = "人终将被少年不可得之物困其一生,但也终会因一事一景解开一生困惑";
        List terms1 = ToAnalysis.parse(str1).getTerms();
        for (Term term1 : terms1) {
            System.out.print(term1.getName() + '/');
        }
        System.out.println();
        //英文分词(会把大写字母转换成小写字母)
        String str2 = "I have a dream!";
        List terms2 = ToAnalysis.parse(str2).getTerms();
        for (Term term2 : terms2) {
            System.out.print(term2.getName() + ' ');
        }
    }

结果:

六、实现索引模块

6.1 实现 Parser类

创建一个Parser类,通过这个类来完成制作索引的过程~

Parser:读取之前下载好的离线文档,去解析文档的内容,并完成索引的制作! 

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;

public class Parser {
    //指定加载文档的路径
    private static final String INPUT_PATH = "E:/doc_searcher_index/jdk-8u361-docs-all/docs/api/";

    private void run() {
        //整个Parser类的入口
        //1、根据上面指定的路径,枚举出该路径中所有的 html文件(包括子目录中的文件)
        //2、针对上面罗列出文件的路径,打开文件,读取文件内容,并进行解析,并构建索引
        //3、todo 把在内存中构造好的索引数据结构,保存到指定的文件中

        ArrayList fileList = new ArrayList<>();
        enumFile(INPUT_PATH,fileList);
        /**
         * System.out.println(fileList);
         * System.out.println(fileList.size());
         */

        for (File f:fileList) {
            //通过parseHTML()方法解析 .html文件
            System.out.println("开始解析:" + f.getAbsolutePath());
            parseHTML(f);
        }

    }

    /**
     * 解析 .html文件 的方法
     * @param f
     */
    private void parseHTML(File f) {
        //解析 标题
        String title = parseTitle(f);
        //解析 url
        String url = parseUrl(f);
        //解析 对应的正文(描述 是正文的一小部分,先解析出来正文,然后才可以获取到 描述)
        String content = parseContent(f);

        //todo 把解析出来的这些信息,加入到索引当中
    }

    /**
     * 通过观察,可以发现,在 .html文件中,标签 里面的就是标题
     * 进一步可以发现,.html文件名 就是该文件的标题
     */
    private String parseTitle(File f) {
        return f.getName().substring(0,f.getName().length() - ".html".length());
    }

    /**
     * url 是在线文档对应的链接
     */
    private String parseUrl(File f) {
        //先获取到固定的前缀
        String part1 = "https://docs.oracle.com/javase/8/docs/api/";
        //后获取 线下文档api后面的部分
        String part2 = f.getAbsolutePath().substring(INPUT_PATH.length());
        return part1 + part2;
    }

    /**
     * 一个完整的 .html文件,里面有的是 <html>标签 + 内容
     * 所以 解析正文的核心工作是:去掉 <html>标签,只剩下 内容
     */
    public String parseContent(File f) {
        //先按照一个字符一个字符的方式来读取( < 和 > 来控制拷贝数据的开关)
        try( FileReader fileReader = new FileReader(f)) {
            //加上一个开关:true拷贝,false不拷贝
            boolean isCopy = true;
            //加上一个保存结果的stringBuilder
            StringBuilder stringBuilder = new StringBuilder();

            while (true) {
                //read()返回值是 int,而不是 char
                //主要是为了表示一些非法情况,文件读完了,返回-1
                int ret = fileReader.read();
                if (ret == -1) {
                    break;
                }

                //如果结果不是-1,那就是合法的字符
                char c = (char) ret;
                if (isCopy) {
                    //开关打开,遇到普通字符就拷贝到 StringBuilder中
                    if (c == '<') {
                        //关闭开关
                        isCopy = false;
                        continue;
                    }
                    //去掉 .html文件当中的换行,不然实在是不好看
                    if (c == '\n' || c == '\r') {
                        //把换行替换成空格
                        c = ' ';
                    }
                    //其他字符,直接拷贝
                    stringBuilder.append(c);
                }else {
                    //开关不拷贝,直到遇到 >
                    if (c == '>') {
                        isCopy = true;
                    }
                }
            }
            return stringBuilder.toString();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }





    /**
     *
     * @param inputPath 表示从哪个目录开始进行递归遍历
     * @param fileList 表示递归所得到的结果
     */
    private void enumFile(String inputPath, ArrayList<File> fileList) {
        File rootPath = new File(inputPath);
        File[] files = rootPath.listFiles(); //listFiles()方法,获取到rootPath目录下的路径名(仅仅只能看到一层内容)

        //使用递归,看见所有子目录下面的内容
        for (File f:files) {
            //根据f类型,来决定是否需要递归
            if (f.isDirectory()) {
                //f 是一个目录,递归调用enumFile()方法,进一步获取子目录的内容
                enumFile(f.getAbsolutePath(),fileList);
            }else {
                //f 是一个普通文件,加入到 fileList中
                //在此处排除非.html文件(以.html的文件才添加上去)
                if (f.getAbsolutePath().endsWith(".html")) {
                    fileList.add(f);
                }
            }
        }
    }


    public static void main(String[] args) {
        //通过 main方法,实现整个制作索引的过程
        Parser parser = new Parser();
        parser.run();
    }
}
</code></pre> 
  <p>在Parser类当中,主要做的是,枚举出当前指定目录中的所有的 .html文档,通过递归函数 enumFile(INPUT_PATH,fileList) ,把所获取到的文档存储在 fileList里面;接下来就可以循环遍历这里的每一个文件,再针对文件进行解析,每一个文件都是一个 .html,解析 .html文件的 标题、描述、展示url ~</p> 
  <p>在解析好了这些信息,就可以把它们加入到索引数据结构当中;于是,就可以专门创建一个 Index类,来负责构建索引数据结构~</p> 
  <hr> 
  <h3 id="6.2%20%E5%AE%9E%E7%8E%B0%20Index%E7%B1%BB">6.2 实现 Index类</h3> 
  <h4 id="6.2.1%20%E5%88%9B%E5%BB%BA%20Index%E7%B1%BB">6.2.1 创建 Index类</h4> 
  <p>Index类 主要实现的功能有:</p> 
  <ul> 
   <li>正排索引</li> 
   <li>倒排索引</li> 
   <li>新增一个文档</li> 
   <li>保存索引文件</li> 
   <li>加载索引文件</li> 
  </ul> 
  <pre><code>import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * 通过这个类在内存中构造出索引结构
 */
public class Index {

    //使用数组下标表示 docId(正排索引)
    private ArrayList<DocInfo> forwardIndex = new ArrayList<>();

    //使用 哈希表 来表示倒排索引(key是词,value是一组和这个词相关联的文章)
    private HashMap<String,ArrayList<Weight>> invertedIndex = new HashMap<>();

    //这个类所提供的方法
    //1.正排索引:给定一个 docId,在正排索引中,可以查询到文档的详细信息
    //2.倒排索引:给定一个词,在倒排索引中,查找那些文档和这个词有关联
    //3.将前面所解析出来的信息添加进去,向索引当中新增一个文档
    //4.把内存中的索引结构保存到磁盘中
    //5.把磁盘中的索引数据加载到内存

    //1.
    public DocInfo getDocInfo(int docId) {
        //todo
        return null;
    }

    //2.
    public List<Weight> getInverted(String term) {
        //todo
        return null;
    }

    //3.
    public void addDoc(String title,String url,String content) {
        //todo
    }

    //4.
    public void save() {
        //todo
    }

    //5.
    public void load() {
        //todo
    }
}
</code></pre> 
  <hr> 
  <h4 id="6.2.2%20%E5%88%9B%E5%BB%BADocInfo%E7%B1%BB">6.2.2 创建DocInfo类</h4> 
  <p>DocInfo类主要表示的是:在正排索引中,根据 docId 所查询的文档的相关细节~</p> 
  <pre><code>/**
 * 该类当中可以表示一个文档的相关细节
 */
public class DocInfo {
    private int docId;
    private String title;
    private String url;
    private String content;

    public int getDocId() {
        return docId;
    }

    public void setDocId(int docId) {
        this.docId = docId;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}
</code></pre> 
  <hr> 
  <h4 id="6.2.3%20%E5%88%9B%E5%BB%BA%20Weight%E7%B1%BB">6.2.3 创建 Weight类</h4> 
  <p>Weight类主要表示的是:在倒排索引中,文档id 和 文档与词的相关性 的权重~</p> 
  <pre><code>/**
 * 这个类是把 文档id 和 文档与词的相关性 的权重 进行一个包裹
 */
public class Weight {
    private int docId;
    //weight表示 文档和词之间的"相关性",值越大,就说明 这个词和这个文档越有关的
    private int weight;

    public int getDocId() {
        return docId;
    }

    public void setDocId(int docId) {
        this.docId = docId;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }
}
</code></pre> 
  <hr> 
  <h4 id="6.2.4%20%E5%AE%9E%E7%8E%B0%20getDocInfo%20%E5%92%8C%20getInverted%E6%96%B9%E6%B3%95">6.2.4 实现 getDocInfo 和 getInverted方法</h4> 
  <pre><code>    //1.
    public DocInfo getDocInfo(int docId) {
        return forwardIndex.get(docId);
    }

    //2.
    public List<Weight> getInverted(String term) {
        return invertedIndex.get(term);
    }</code></pre> 
  <hr> 
  <p></p> 
  <h4 id="6.2.5%20%E5%AE%9E%E7%8E%B0%20addDoc%E6%96%B9%E6%B3%95">6.2.5 实现 addDoc方法</h4> 
  <pre><code>//3.
    public void addDoc(String title,String url,String content) {
        //新增文档操作,需要给正排索引和倒排索引都新增
        //构建正排索引
        DocInfo docInfo = buildForward(title,url,content);
        //构建倒排索引
        buildInverted(docInfo);

    }

    private void buildInverted(DocInfo docInfo) {
        //倒排索引,是词与文档id的映射关系
        //就需要知道当前的文档,里面存在有哪些词,就需要实现"分词"
        //就需要针对 标题、正文 进行分词,之后就可以根据分词的结果,知道当前的文档id 应该要加入到哪一个倒排索引的key里

        //1.针对标题进行分词,遍历分词结果,统计每个词出现的次数
        //2.针对正文进行分词,遍历分词结果,统计每个词出现的个数
        //3.汇总到HashMap中,简单设置权重,权重=标题出现的次数*10 + 正文出现的次数
        //4.遍历HashMap,依次更新倒排索引中的结构

        class WordCount {
            public int titleCount; //标题次数
            public int contentCount;//正文次数
        }
        //用来统计词频的数据结构
        HashMap<String,WordCount> wordCountHashMap = new HashMap<>();
        List<Term> terms = ToAnalysis.parse(docInfo.getTitle()).getTerms();
        for (Term term:terms) {
            //先判定term 是否存在
            String word = term.getName();
            WordCount wordCount = wordCountHashMap.get(word);
            if (wordCount == null) {
                //不存在,创建一个新的键值对,插入进去,titleCount设为1
                WordCount newWordCount = new WordCount();
                newWordCount.titleCount = 1;
                newWordCount.contentCount = 0;
                wordCountHashMap.put(word,newWordCount);
            }else {
                //存在,找到之前的值,对应的 titleCount+1
                wordCount.titleCount += 1;
            }
        }

        terms = ToAnalysis.parse(docInfo.getContent()).getTerms();
        for (Term term:terms) {
            String word = term.getName();
            WordCount wordCount = wordCountHashMap.get(word);
            if (wordCount == null) {
                WordCount newWordCount = new WordCount();
                newWordCount.titleCount = 0;
                newWordCount.contentCount = 1;
                wordCountHashMap.put(word,newWordCount);
            }else {
                wordCount.contentCount += 1;
            }
        }

        for (Map.Entry<String,WordCount> entry : wordCountHashMap.entrySet()) {
            //先根据这里的词去倒排索引中查一查
            List<Weight> invertedList = invertedIndex.get(entry.getKey()); //倒排拉链
            if (invertedList == null) {
                ArrayList<Weight> newInvertedList = new ArrayList<>();
                //把新的文档(当前 DocInfo),构造成 Weight对象,插入进来
                Weight weight = new Weight();
                weight.setDocId(docInfo.getDocId());
                //权重计算公式:标题中出现的次数*10+正文中出现的次数
                weight.setWeight(entry.getValue().titleCount * 10 + entry.getValue().contentCount);
                newInvertedList.add(weight);
                //如果为空,就插入一个新的键值对
                invertedIndex.put(entry.getKey(),newInvertedList);
            }else {
                //如果非空,就把这个文档,构造一个 Weight对象,插入到倒排拉链的后面
                Weight weight = new Weight();
                weight.setDocId(docInfo.getDocId());
                //权重计算公式:标题中出现的次数*10+正文中出现的次数
                weight.setWeight(entry.getValue().titleCount * 10 + entry.getValue().contentCount);
                invertedList.add(weight);
            }
        }

    }</code></pre> 
  <h4 id="6.2.6%20%E5%AE%9E%E7%8E%B0save%E6%96%B9%E6%B3%95">6.2.6 实现save方法</h4> 
  <p>至于为什么要实现save方法呢其实原因也是很简单的:当前索引是存储在 内存 中,而构建索引的过程(即上面实现的addDoc方法),实际上是非常耗时间的,仅仅一个API就有上万份文档了,更别说其他的了~</p> 
  <p>因此我们不应该服务器启动的时候才构建索引(启动服务器就可能会被拖慢很多了),万一服务器在运行过程中又崩溃了,那又要重启服务器,那又要浪费更长的时间了~</p> 
  <p>通常的操作可以是:先把这些耗时的操作,单独的去执行;执行好了之后,再让线上服务器直接加载这个构造好的索引;此时,加载的速度就仅在读磁盘上面花费时间了~</p> 
  <p>至于具体该如何去保存到文件中呢?我们都知道,文件里保存的不是 二进制数据,就是文本数据;这就需要把内存中的索引结构 变成一个字符串(这个过程叫做 序列化),然后就可以直接写文件就可以了~</p> 
  <blockquote> 
   <p>类似的,把特定结构的字符串 按照一定的结构解析回来(类/对象/基础数据结构),我们就称之为 反序列化~</p> 
  </blockquote> 
  <p>其实,序列化和反序列化 ,都有许多现成的方法,此处可以使用JSON的格式,来完成 序列化和反序列化,就可以很轻松的完成保存和加载索引的操作了~</p> 
  <blockquote> 
   <p>此处使用JSON的格式,可以从中央仓库中引入 相关依赖:</p> 
   <pre><code><!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.2</version>
</dependency>
</code></pre> 
  </blockquote> 
  <pre><code>//引入 jackson 里面的核心对象
private ObjectMapper objectMapper = new ObjectMapper();

//保存的地址
private static final String INDEX_PATH = "E:\\doc_searcher_index\\";</code></pre> 
  <p>由于涉及到两种索引,就可以分别使用两个文件 来保存正排索引和倒排索引~</p> 
  <pre><code>    //4.
    public void save() {
        //序列化
        //使用两个文件,分别保存正排和倒排
        System.out.println("保存所以开始!");
        //1.先判断索引对应的目录是否存在,不存在就创建
        File indexPathFile = new File(INDEX_PATH);
        if (!indexPathFile.exists()) {
            indexPathFile.mkdirs();
        }
        File forwardIndexFile = new File(INDEX_PATH + "forward.txt");
        File invertedIndexFile = new File(INDEX_PATH + "inverted.txt");
        try {
            objectMapper.writeValue(forwardIndexFile,forwardIndex);
            objectMapper.writeValue(invertedIndexFile,invertedIndex);
        }catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("保存索引完成!");
    }
</code></pre> 
  <p></p> 
  <h4 id="6.2.7%20%E5%AE%9E%E7%8E%B0%20load%E6%96%B9%E6%B3%95">6.2.7 实现 load方法</h4> 
  <pre><code>    //5.
    public void load() {
        //反序列化
        System.out.println("加载索引开始!");
        File forwardIndexFile = new File(INDEX_PATH + "forward.txt");
        File invertedIndexFile = new File(INDEX_PATH + "inverted.txt");
        try {
            forwardIndex = objectMapper.readValue(forwardIndexFile, new TypeReference<ArrayList<DocInfo>>() {});
            invertedIndex = objectMapper.readValue(invertedIndexFile, new TypeReference<HashMap<String, ArrayList<Weight>>>() {});
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("加载索引结束!");
    }</code></pre> 
  <blockquote> 
   <p>当然,由于索引中的文件可能会有很多,构造出来的索引数据也是比较大的,那么保存和加载到底会消耗多长时间呢?<strong>我们就可以修改一下 load和save方法</strong>,来衡量出它们的运行时间:</p> 
   <pre><code>//4.
    public void save() {
        //序列化
        long beg = System.currentTimeMillis();
        //使用两个文件,分别保存正排和倒排
        System.out.println("保存索引开始!");
        //1.先判断索引对应的目录是否存在,不存在就创建
        File indexPathFile = new File(INDEX_PATH);
        if (!indexPathFile.exists()) {
            indexPathFile.mkdirs();
        }
        File forwardIndexFile = new File(INDEX_PATH + "forward.txt");
        File invertedIndexFile = new File(INDEX_PATH + "inverted.txt");
        try {
            objectMapper.writeValue(forwardIndexFile,forwardIndex);
            objectMapper.writeValue(invertedIndexFile,invertedIndex);
        }catch (IOException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();

        System.out.println("保存索引完成!所消耗的时间是:" + ( end - beg ) + "ms");
    }

    //5.
    public void load() {
        //反序列化
        long beg = System.currentTimeMillis();
        System.out.println("加载索引开始!");
        File forwardIndexFile = new File(INDEX_PATH + "forward.txt");
        File invertedIndexFile = new File(INDEX_PATH + "inverted.txt");
        try {
            forwardIndex = objectMapper.readValue(forwardIndexFile, new TypeReference<ArrayList<DocInfo>>() {});
            invertedIndex = objectMapper.readValue(invertedIndexFile, new TypeReference<HashMap<String, ArrayList<Weight>>>() {});
        } catch (IOException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("加载索引结束!消耗时间:" + ( end - beg ) + "ms");
    }</code></pre> 
  </blockquote> 
  <p></p> 
  <h3 id="6.3%20%E5%9C%A8%20Parser%20%E4%B8%AD%E8%B0%83%E7%94%A8%20Index">6.3 在 Parser 中调用 Index</h3> 
  <p>当然,关于 Parser类 和 Index类 之间的关系 我们需要清楚:Parser类 就相当于是 制作索引的入口,通过 Parser类 中的 main方法,就可以把整个流程给跑起来;Index类 就相当于是实现了 索引的数据结构,并且提供了一些API供他人调用~</p> 
  <p>因此,Index类 需要给 Parser类 调用,才可以完成整个制作索引的过程~</p> 
  <p>这也是上面的曾经留下了 两个todo 需要做的事情:</p> 
  <p><a href="http://img.e-com-net.com/image/info8/1c8eb4ada603472999f648bddd9e5190.jpg" target="_blank"><img alt="API文档搜索引擎_第10张图片" height="123" src="http://img.e-com-net.com/image/info8/1c8eb4ada603472999f648bddd9e5190.jpg" width="650" style="border:1px solid black;"></a></p> 
  <h4 id="6.3.1%20%E6%96%B0%E5%A2%9E%20Index%E5%AE%9E%E4%BE%8B">6.3.1 新增 Index实例</h4> 
  <pre><code>public class Parser {
    ......
    
    //创建一个 Index实例
    private Index index = new Index();
    
    ......
}</code></pre> 
  <p></p> 
  <h4 id="6.3.2%20%E4%BF%AE%E6%94%B9%20parseHTML%E6%96%B9%E6%B3%95">6.3.2 修改 parseHTML方法</h4> 
  <p>新增 addDoc方法的调用</p> 
  <pre><code>private void parseHTML(File f) {
        //解析 标题
        String title = parseTitle(f);
        //解析 url
        String url = parseUrl(f);
        //解析 对应的正文(描述 是正文的一小部分,先解析出来正文,然后才可以获取到 描述)
        String content = parseContent(f);

        //todo 把解析出来的这些信息,加入到索引当中[新增]
        index.addDoc(title,url,content);
    }</code></pre> 
  <p></p> 
  <h4 id="6.3.3%20%E6%96%B0%E5%A2%9Esave%E6%96%B9%E6%B3%95%E7%9A%84%E8%B0%83%E7%94%A8">6.3.3 新增save方法的调用</h4> 
  <pre><code> private void run() {
        long beg = System.currentTimeMillis();
        System.out.println("索引制作开始!");
        //整个Parser类的入口
        //1、根据上面指定的路径,枚举出该路径中所有的 html文件(包括子目录中的文件)
        //2、针对上面罗列出文件的路径,打开文件,读取文件内容,并进行解析,并构建索引
        //3、todo 把在内存中构造好的索引数据结构,保存到指定的文件中

        long begEnumFile = System.currentTimeMillis();
        ArrayList<File> fileList = new ArrayList<>();
        enumFile(INPUT_PATH,fileList);
        long endEnumFile = System.currentTimeMillis();
        System.out.println("枚举文件完毕!消耗时间:" + ( endEnumFile - begEnumFile ) + "ms");
        /**
         * System.out.println(fileList);
         * System.out.println(fileList.size());
         */

        long begFor = System.currentTimeMillis();
        for (File f:fileList) {
            //通过parseHTML()方法解析 .html文件
            System.out.println("开始解析:" + f.getAbsolutePath());
            parseHTML(f);
        }
        long endFor = System.currentTimeMillis();
        System.out.println("循环遍历文件完毕!消耗时间:" + ( endFor - begFor ) + "ms");

        //新增save方法的调用(好吧,其实也新增了一些时间戳)
        index.save();
        long end = System.currentTimeMillis();
        System.out.println("索引制作完毕!消耗时间:" + ( end - beg ) + "ms");
    }</code></pre> 
  <p></p> 
  <h3 id="6.4%20%E9%AA%8C%E8%AF%81%E7%B4%A2%E5%BC%95%E6%A8%A1%E5%9D%97">6.4 验证索引模块</h3> 
  <p> 运行Parser类,观察运行结果:</p> 
  <p></p> 
  <p><a href="http://img.e-com-net.com/image/info8/664043d1197047c4abc21097edd4af54.jpg" target="_blank"><img alt="" height="74" src="http://img.e-com-net.com/image/info8/664043d1197047c4abc21097edd4af54.jpg" width="650"></a></p> 
  <p><a href="http://img.e-com-net.com/image/info8/9acdce438ee94f48802c7b5a7ddc658e.jpg" target="_blank"><img alt="" height="68" src="http://img.e-com-net.com/image/info8/9acdce438ee94f48802c7b5a7ddc658e.jpg" width="650"></a></p> 
  <p><a href="http://img.e-com-net.com/image/info8/bee2bda5cda643a7a2591d77c24c30f6.jpg" target="_blank"><img alt="" height="68" src="http://img.e-com-net.com/image/info8/bee2bda5cda643a7a2591d77c24c30f6.jpg" width="650"></a></p> 
  <p></p> 
  <h3 id="6.5%20%E4%BC%98%E5%8C%96%E5%88%B6%E4%BD%9C%E7%B4%A2%E5%BC%95%E9%80%9F%E5%BA%A6">6.5 优化制作索引速度</h3> 
  <p>关于性能优化,首先就需要通过 "测试的手段"(咳咳,通过一些时间戳 知道某一段程序运行所花费的时间),找到其中的 "性能瓶颈"~</p> 
  <p>通过结果发现,主要花费的时间(性能瓶颈) 就是在循环遍历文件 上面,每次遍历一个 .html文件,都需要进行 读文件+分词+解析内容 等操作(主要就是花费在CPU运算上面)~</p> 
  <p>在单个线程的情况下,这些任务都是串行执行的;因此,可以引进多线程,并发执行,优化速度(当然,需要通过加锁来完成线程安全)~</p> 
  <pre><code>//通过这个方法实现多线程制作索引
    public void runByThread() throws InterruptedException {
        long beg = System.currentTimeMillis();
        System.out.println("索引制作开始!");

        //1.枚举所有文件
        ArrayList<File> files = new ArrayList<>();
        enumFile(INPUT_PATH,files);

        //2.循环遍历文件(引入线程池)
        CountDownLatch latch = new CountDownLatch(files.size());
        ExecutorService executorService = Executors.newFixedThreadPool(4);
        for (File f:files) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("解析:" + f.getAbsolutePath());
                    parseHTML(f);
                    latch.countDown();
                }
            });
        }
        //await方法会阻塞,直到所有选手都调用countDown撞线,才会阻塞结束
        latch.await();
        executorService.shutdown(); //手动杀死线程池里面的线程

        //3.保存索引
        index.save();

        long end = System.currentTimeMillis();
        System.out.println("索引制作完毕(多线程)!消耗时间:" + ( end - beg ) + "ms");
    }</code></pre> 
  <p><strong>解决线程安全问题:</strong></p> 
  <pre><code>    //创建两个锁对象
    private Object locker1 = new Object();
    private Object locker2 = new Object();</code></pre> 
  <p><strong>修改 buildForward方法:</strong></p> 
  <pre><code>private DocInfo buildForward(String title, String url, String content) {
        DocInfo docInfo = new DocInfo();
        docInfo.setTitle(title);
        docInfo.setUrl(url);
        docInfo.setContent(content);
        synchronized (locker1) {
            docInfo.setDocId(forwardIndex.size());
            forwardIndex.add(docInfo);
        }
        return docInfo;
    }</code></pre> 
  <p><strong>修改 buildInverted方法:</strong></p> 
  <pre><code>private void buildInverted(DocInfo docInfo) {

        ......

        for (Map.Entry<String,WordCount> entry : wordCountHashMap.entrySet()) {
            //先根据这里的词去倒排索引中查一查
            synchronized (locker2) {

                   ......

            }
        }
    }
</code></pre> 
  <p>在调用多线程的方法之后,我们就可以发现 制作索引的速度就快了很多:</p> 
  <p></p> 
  <hr> 
  <p><strong>一些其他的小问题:</strong></p> 
  <p>比如说,第一次制作索引的时候,它的制作速度会特别慢,在之后制作索引的时候,速度又会快很多;但把电脑关掉,再一次重启电脑,第一次制作索引的时候又变得慢很多,后面的时候速度又会快很多,这是为什么呢?</p> 
  <blockquote> 
   <p>分析:在代码中 核心操作还是在 循环遍历上面,循环遍历上面 又调用了 parseHTML方法,parseHTML方法中又调用了 parseContent方法,进行了读取文件的操作~</p> 
   <p>在计算机读取文件的时候,是一个开销比较大的操作~</p> 
   <p>我们就可以猜想:是不是开机之后,首次读取文件的时候 速度特别慢呢~</p> 
   <hr> 
   <p>实际上,是因为缓存的问题~</p> 
   <p>首次运行的时候,由于当前的这些 Java API 文档,没有在内存中进行缓存,因此读取的时候就只能从硬盘上进行读取(这是及其耗时的);后面再运行的时候,由于前面已经读取过文档了,即 在操作系统中其实已经有了一个缓存(在内存中),所以这次读取的就不是直接读硬盘,而是直接读内存的缓存(速度就会快很多)~</p> 
   <hr> 
   <p>当然,这也是可以进行优化的~</p> 
   <p>BufferedReader 和 FileReader 搭配着来使用,BufferedReader 内部会内置一个缓冲区,就可以自动的把 FileReader 中的一些内容预读到内存中,从而减少直接访问磁盘的次数~</p> 
   <p>虽然说,后面的运行程序 的时候操作系统已经做了缓存,此时运行速度也提高不了太多,但也好歹是优化了一点点~</p> 
  </blockquote> 
  <p><img alt="" src="http://img.e-com-net.com/image/info8/a575c4c38f6b41248878c29d078cd7c3.jpg" width="650" height="29"></p> 
  <h2 id="%E4%B8%83%E3%80%81%E5%AE%9E%E7%8E%B0%E6%90%9C%E7%B4%A2%E6%A8%A1%E5%9D%97">七、实现搜索模块</h2> 
  <p>搜索模块 主要是调用 索引模块,来完成搜索的核心过程!!!!!!</p> 
  <p>其中是可以包括:</p> 
  <ul> 
   <li>分词:针对用户输入的查询词,进行分词</li> 
   <li>触发:拿着每一个分词结果,去倒排索引当中找出具有相关性的文章(调用 Index类 里面查倒排的方法即可)</li> 
   <li> 排序:根据上面所触发出来的结果,根据相关性降序进行排序</li> 
   <li>包装结果:根据排序后的结果,依次去查正排,获取到每个文档的详细信息,包装成一定结构的数据返回出去</li> 
  </ul> 
  <hr> 
  <h3 id="7.1%20%E5%88%9B%E5%BB%BA%20DocSearcher%E7%B1%BB">7.1 创建 DocSearcher类</h3> 
  <pre><code>import java.util.List;

//通过这个类 来完成整个搜索模块的过程
public class DocSearcher {

    //此处需要加上索引对象的实例,同时需要完成索引加载的工作
    private Index index = new Index();
    public DocSearcher() {
        index.load();
    }

    /**
     * 完成整个搜索过程的方法
     * @param query 查询词
     * @return 搜索结果的集合
     */
    public List<Result> searcher(String query) {
        //1.分词
        //2.触发
        //3.排序
        //4.包装结果
        return null;
    }
}
</code></pre> 
  <hr> 
  <h4 id="7.1.1%20%E5%88%9B%E5%BB%BA%20Result%E7%B1%BB">7.1.1 创建 Result类</h4> 
  <pre><code>//这个类来表示一个搜索结果
public class Result {
    private String title;
    private String url;
    private String desc; //描述(正文的一段摘要)

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    @Override
    public String toString() {
        return "Result{" +
                "title='" + title + '\'' +
                ", url='" + url + '\'' +
                ", desc='" + desc + '\'' +
                '}';
    }
}
</code></pre> 
  <hr> 
  <h4 id="7.1.2%20%E5%AE%9E%E7%8E%B0%20search%E6%96%B9%E6%B3%95">7.1.2 实现 search方法</h4> 
  <pre><code>//通过这个类 来完成整个搜索模块的过程
public class DocSearcher {

    //此处需要加上索引对象的实例,同时需要完成索引加载的工作
    private Index index = new Index();
    public DocSearcher() {
        index.load();
    }

    /**
     * 完成整个搜索过程的方法
     * @param query 查询词
     * @return 搜索结果的集合
     */
    public List<Result> searcher(String query) {
        //1.分词
        List<Term> terms = ToAnalysis.parse(query).getTerms();

        //2.触发(/针对分词结果进行倒排查找)
        List<Weight> allTermResult = new ArrayList<>();
        for (Term term:terms) {
            String word = term.getName();
            List<Weight> invertedList = index.getInverted(word);
            if (invertedList == null) {
                //说明这个词在所有文档中不存在
                continue;
            }
            allTermResult.addAll(invertedList);
        }

        //3.排序
        allTermResult.sort(new Comparator<Weight>() {
            @Override
            public int compare(Weight o1, Weight o2) {
                //如果是升序排序:return o1.getWeight() - o2.getWeight()
                //如果是降序排序:return o2.getWeight() - o1.getWeight()
                return o2.getWeight() - o1.getWeight();
            }
        });

        //4.包装结果
        List<Result> results = new ArrayList<>();
        for (Weight weight:allTermResult) {
            DocInfo docInfo = index.getDocInfo(weight.getDocId());
            Result result = new Result();
            result.setTitle(docInfo.getTitle());
            result.setUrl(docInfo.getUrl());
            result.setDesc(GenDesc(docInfo.getContent(),terms)); //描述
            results.add(result);
        }
        return results;
    }

    //生成描述信息
    private String GenDesc(String content, List<Term> terms) {
        //先遍历分词结果,看看结果是否在 content 中存在
        //正文需要先转成小写(重要)
        int firstPos = -1;
        for (Term term:terms) {
            String word = term.getName();
            firstPos = content.toLowerCase().indexOf( " " + word + " ");
            if (firstPos >= 0) {
                //找到了位置
                break;
            }
        }

        if (firstPos == -1) {
            //所有的分词结果不在正文中存在
            //取正文的前 160 个字符作为描述
            return content.substring(0,160) + "...";
        }

        //从 firstPos 作为基准位置,往前找60个字符,往后找100个字符
        String desc = " ";
        int descBeg = firstPos < 60 ? 0 : firstPos-60;
        if (descBeg + 160 > content.length()) {
            //descBeg 位置很靠后面了
            desc = content.substring(descBeg);
        } else {
            desc = content.substring(descBeg,descBeg+160) + "...";
        }
        return desc;
    }
}
</code></pre> 
  <blockquote> 
   <p>在包装结果生成描述的时候,需要注意的是:</p> 
   <ul> 
    <li>前面使用第三方库分词的结果 都是小写形式的,所以不可以直接对着内容进行比较,需要先转换成小写才可以</li> 
    <li>其实需要根据实际的情况,类似于 买了一份"老婆饼",并不可以送一个"老婆",我们搜索的时候,不能把两个单词由于差不多,就归结于相关性较强,所以在查找单词的时候,可以在前面和后面加上 " ",虽然可能会查询不到在最前面或者最后面,但这毕竟还是少数情况(当然,也可以使用正则表达式去解决这个问题)</li> 
   </ul> 
  </blockquote> 
  <p></p> 
  <hr> 
  <h4 id="7.1.3%20%E9%AA%8C%E8%AF%81%20DocSearcher%E7%B1%BB">7.1.3 验证 DocSearcher类</h4> 
  <p>验证 DocSearcher类</p> 
  <pre><code>    public static void main(String[] args) {
        DocSearcher docSearcher = new DocSearcher();
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.print("-> ");
            String query = scanner.next();
            List<Result> results = docSearcher.searcher(query);
            for (Result result : results) {
                System.out.println("====================================");
                System.out.println(result);
            }
        }
    }</code></pre> 
  <p><a href="http://img.e-com-net.com/image/info8/d37905c2ee944502a04fa9f487f7341f.jpg" target="_blank"><img alt="API文档搜索引擎_第11张图片" height="194" src="http://img.e-com-net.com/image/info8/d37905c2ee944502a04fa9f487f7341f.jpg" width="650" style="border:1px solid black;"></a></p> 
  <p>我们观察结果可以发现,在有的查询结果中出现了下面的内容:</p> 
  <p><img alt="" height="19" src="http://img.e-com-net.com/image/info8/27438167c6c14ff8a54ed0201c349ff0.jpg" width="650"></p> 
  <p>其实,这个内容就是 JavaScript 的代码,由于在处理文档的时候,只是针对正文 进行了 "去标签",而有的 HTML 里面包含了 script标签,就导致了在去标签过后,JS的代码也被整理到索引里面了,所以查的时候就出现了这个情况~</p> 
  <p>而这种情况并不科学,因此还需要在后面去掉(可以使用正则表达式)~</p> 
  <p></p> 
  <hr> 
  <h4 id="7.1.4%20%E4%BF%AE%E6%94%B9%20Parser%E7%B1%BB">7.1.4 修改 Parser类</h4> 
  <p> 修改 Parser类(换成使用正则表达式来解决):</p> 
  <pre><code>    /**
     * 通过这个方法内部基于正则表达式,实现去除标签,以及 script的效果
     */
    public String parseContentByRegex(File f) {
        //1.先把这个文件都读到 String 里面
        String content = readFile(f);
        //2.替换script标签
        content = content.replaceAll("<script.*?>(.*?)</script>"," ");
        //3.替换普通的 .html标签
        content = content.replaceAll("<.*?>"," ");
        return content;
    }

    private String readFile(File f) {
        try(BufferedReader bufferedReader = new BufferedReader(new FileReader(f))) {
            StringBuilder content = new StringBuilder();
            while (true) {
                int ret = bufferedReader.read();
                if (ret == -1) {
                    //读完了
                    break;
                }
                char c = (char) ret;
                if (c == '\n' || c == '\r') {
                    c = ' ';
                }
                content.append(c);
            }
            return content.toString();
        }catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }</code></pre> 
  <p>此时,就可以验证一下效果如何:</p> 
  <p></p> 
  <p><a href="http://img.e-com-net.com/image/info8/364b0729842f4bc2a4f8ba3a57c166fc.jpg" target="_blank"><img alt="" height="100" src="http://img.e-com-net.com/image/info8/364b0729842f4bc2a4f8ba3a57c166fc.jpg" width="650"></a></p> 
  <p>可是,新的看起来也不是很好看(原因是因为空格太多了),我们就可以把多个空格合并成一个即可(同样是使用正则表达式):</p> 
  <pre><code>        //4.使用正则表达式,把多个空格合并成一个空格
        content = content.replaceAll("\\s+"," ");</code></pre> 
  <p></p> 
  <p>所以在解析正文的时候,就可以使用正则的方式了~</p> 
  <p><img alt="" src="http://img.e-com-net.com/image/info8/a575c4c38f6b41248878c29d078cd7c3.jpg" width="650" height="29"></p> 
  <h2 id="%E5%85%AB%E3%80%81%E5%AE%9E%E7%8E%B0Web%E6%A8%A1%E5%9D%97">八、实现Web模块</h2> 
  <p>此时,搜索的核心功能都已经实现出来了,但是此时仍然需要提供一个Web接口,以网页的形式,把程序呈现给用户~</p> 
  <p>因为 此时的效果只能是在控制台中进行操作,此时对于普通用户来说 并不友好,此时如果想真正的搜索引擎一样,有一个搜索框,有一个搜索按钮......想必这样的界面对于用户来说更加的友好~</p> 
  <blockquote> 
   <p>要想实现这样的Web模块,需要约定一下前后端通信的接口,需要明确的描述出,服务器都能接受什么样的请求 ,都能返回什么样的响应的~</p> 
   <p>前端(HTML+CSS+JS)+ 后端(Java + Servlet/Spring相关)~</p> 
   <p>针对于这个项目,我们只需要实现一个 搜索接口 即可~</p> 
   <pre><code>//约定
//请求:
GET/searcher?query=[查询词] HTTP/1.1

//响应:
HTTP/1.1

[
    {
        title:"这是标题1",
        url:"这是描述1",
        desc:"这是描述1"
    }
]
</code></pre> 
  </blockquote> 
  <hr> 
  <p>如果是使用 Servlet 来实现后端的接口的话,第一步需要引入相关的依赖的jar包~</p> 
  <pre><code>        <!-- 引入 Servlet依赖      -->
        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency></code></pre> 
  <p>创建 DocSearchServlet类,基于Servlet来实现后端</p> 
  <pre><code>//指定当前的路径 和哪个Servlet类对应
@WebServlet("/searcher")
public class DocSearchServlet extends HttpServlet {

    //引入 DocSearcher实例,调用 searcher方法
    private static DocSearcher docSearcher = new DocSearcher();
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.先解析请求,拿到用户所提交的查询词
        String query = req.getParameter("query");
        if (query == null || query.equals(" ")) {
            //用户的参数是不科学的,一个没有key,一个有key没有value
            String msg = "您的参数非法!没有获得query的值";
            System.out.println(msg);
            System.out.println();
            resp.sendError(404,msg);
            return;
        }

        //2.打印 记录 query的值
        System.out.println("query=" + query);

        //3.调用搜索模块,来进行一下搜索
        List<Result> results = docSearcher.searcher(query);

        //4.把当前的搜索结果进行打包
        resp.setContentType("application/json;charset=utf-8");
        objectMapper.writeValue(resp.getWriter(),results);
    }
}
</code></pre> 
  <hr> 
  <p>嗯,最后剩下的就是实现前端页面的部分了,关于这一部分内容,就不细细展开了~</p> 
  <p><a href="http://img.e-com-net.com/image/info8/3078fcc825114a5ba19b052753731045.jpg" target="_blank"><img alt="API文档搜索引擎_第12张图片" height="227" src="http://img.e-com-net.com/image/info8/3078fcc825114a5ba19b052753731045.jpg" width="650" style="border:1px solid black;"></a></p> 
  <hr> 
  <p>而如果是使用 Spring Boot 来实现后端接口的话,那么可以这样做:</p> 
  <p>在 controller包下面,创建 DocSearcherController 类:</p> 
  <pre><code>@RestController
public class DocSearcherController {
    private static DocSearcher = new DocSearcher();
    private ObjectMapper objectMapper = new ObjectMapper();
    
    @RequestMapping("/searcher")
    public String search(@RequestParam("query") String query) throws JsonProcessingException {
        //参数是查询词,返回值是响应内容
        //参数来自于请求query string中的query这个key的值
        List<Result> results = searcher.search(query);
        return objectMapper.writeValueAsString(results);
    }
}
</code></pre> 
  <p></p> 
  <hr> 
  <p>对了,由于在运行的时候,路径会发生错误,因为有两条路径:一条是云服务器上的路径,一条是本地路径~</p> 
  <p>那么想要在哪里运行的话,就需要去运行哪条路径~</p> 
  <p>所以,想要更加方便的运行,可以制作一个开关来切换路径~</p> 
  <p>虽然说在实际开发中不是这样子的,但是这个却是一个更加简单粗暴的方法:</p> 
  <pre><code>//创建一个Config类
public class Config {
    // 这个变量为 true,表示在云服务器上运行,为 false,表示在本地运行
    public static boolean isOnline = false;
}</code></pre> 
  <pre><code>//Index类底下

public class Index {
    private static  String INDEX_PATH = null;
    static {
        if (Config.isOnline) {
            INDEX_PATH = "/root/java/doc_searcher_index/";
        }else {
            INDEX_PATH = "E:\\doc_searcher_index\\";
        }
    }
    
    ......
}</code></pre> 
  <pre><code>//DocSearcher类底下

public class DocSearcher {

    //停用词表的路径
    private static  String STOP_WORD_PATH = null;

    static {
        if (Config.isOnline) {
            STOP_WORD_PATH = "/root/java/doc_searcher_index/stop_word.txt";
        }else {
            STOP_WORD_PATH = "E:\\doc_searcher_index\\stop_word.txt";
        }
    }
    
    ......
}
</code></pre> 
  <p><img alt="" src="http://img.e-com-net.com/image/info8/a575c4c38f6b41248878c29d078cd7c3.jpg" width="650" height="29"></p> 
 </div> 
</div>
                            </div>
                        </div>
                    </div>
                    <!--PC和WAP自适应版-->
                    <div id="SOHUCS" sid="1705335782614577152"></div>
                    <script type="text/javascript" src="/views/front/js/chanyan.js"></script>
                    <!-- 文章页-底部 动态广告位 -->
                    <div class="youdao-fixed-ad" id="detail_ad_bottom"></div>
                </div>
                <div class="col-md-3">
                    <div class="row" id="ad">
                        <!-- 文章页-右侧1 动态广告位 -->
                        <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_1"> </div>
                        </div>
                        <!-- 文章页-右侧2 动态广告位 -->
                        <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_2"></div>
                        </div>
                        <!-- 文章页-右侧3 动态广告位 -->
                        <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_3"></div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="container">
        <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(项目,搜索引擎)</h4>
        <div id="paradigm-article-related">
            <div class="recommend-post mb30">
                <ul class="widget-links">
                    <li><a href="/article/1901455681932816384.htm"
                           title="HarmonyOS Next ohpm-repo实战案例——搭建企业级私有仓库" target="_blank">HarmonyOS Next ohpm-repo实战案例——搭建企业级私有仓库</a>
                        <span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/harmonyos/1.htm">harmonyos</a>
                        <div>在企业级开发环境中,搭建一个稳定、高效且安全的HarmonyOSNextohpm-repo私有仓库至关重要。它不仅能集中管理项目依赖的三方库,还能提升开发效率、保障代码安全。接下来,我们将从部署架构设计、包管理优化以及访问控制等方面,详细介绍企业级ohpm-repo私有仓库的搭建指南。企业级ohpm-repo部署架构设计(私有仓库+反向代理+多实例高可用架构)私有仓库ohpm-repo作为私有仓库</div>
                    </li>
                    <li><a href="/article/1901454165452845056.htm"
                           title="Spring Cloud Eureka快速搭建:微服务注册中心的配置步骤" target="_blank">Spring Cloud Eureka快速搭建:微服务注册中心的配置步骤</a>
                        <span class="text-muted">勤劳兔码农</span>
<a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/cloud/1.htm">cloud</a><a class="tag" taget="_blank" href="/search/eureka/1.htm">eureka</a><a class="tag" taget="_blank" href="/search/%E5%BE%AE%E6%9C%8D%E5%8A%A1/1.htm">微服务</a>
                        <div>SpringCloudEureka快速搭建:微服务注册中心的配置步骤目录引言SpringCloud微服务架构概述什么是Eureka?EurekaServer的搭建步骤4.1创建EurekaServer项目4.2配置EurekaServer4.3启动EurekaServer4.4多实例EurekaServer的搭建EurekaClient的配置步骤5.1创建EurekaClient项目5.2配置Eu</div>
                    </li>
                    <li><a href="/article/1901450258769047552.htm"
                           title="健康养生:滋养身心的生活智慧" target="_blank">健康养生:滋养身心的生活智慧</a>
                        <span class="text-muted">yy0821yy</span>
<a class="tag" taget="_blank" href="/search/%E7%94%9F%E6%B4%BB/1.htm">生活</a>
                        <div>健康养生,是一场与身体和心灵的对话,是对生命细致入微的呵护,它贯穿于生活的点滴之中,为我们开启优质生活的大门。情志养生在健康养生中占据重要地位。人的情绪如同四季的天气,或晴或雨,但长期的负面情绪如狂风暴雨,会冲击身体的健康防线。我们要学会保持平和、乐观的心态。当遇到挫折时,把它视为成长的机遇,以积极的思维去化解困难。比如,工作上的项目失败了,可以从中总结经验,看到自己提升的空间,而不是一味自责。平</div>
                    </li>
                    <li><a href="/article/1901446852155338752.htm"
                           title="计算机毕业设计springboot教务管理系统 0k1c1源码+系统+程序+lw文档+部署" target="_blank">计算机毕业设计springboot教务管理系统 0k1c1源码+系统+程序+lw文档+部署</a>
                        <span class="text-muted">呦呦网络</span>
<a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a>
                        <div>计算机毕业设计springboot教务管理系统0k1c1源码+系统+程序+lw文档+部署计算机毕业设计springboot教务管理系统0k1c1源码+系统+程序+lw文档+部署本源码技术栈:项目架构:B/S架构开发语言:Java语言开发软件:ideaeclipse前端技术:Layui、HTML、CSS、JS、JQuery等技术后端技术:JAVA运行环境:Win10、JDK1.8数据库:MySQL5</div>
                    </li>
                    <li><a href="/article/1901444330506219520.htm"
                           title="大模型和数据要素赋能实体零售行业数字化转型建设和实施方案" target="_blank">大模型和数据要素赋能实体零售行业数字化转型建设和实施方案</a>
                        <span class="text-muted">优享智库</span>
<a class="tag" taget="_blank" href="/search/%E5%A4%A7%E6%A8%A1%E5%9E%8B/1.htm">大模型</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E8%A6%81%E7%B4%A0/1.htm">数据要素</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E6%B2%BB%E7%90%86/1.htm">数据治理</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E4%BB%93%E5%BA%93/1.htm">数据仓库</a><a class="tag" taget="_blank" href="/search/%E4%B8%BB%E6%95%B0%E6%8D%AE/1.htm">主数据</a><a class="tag" taget="_blank" href="/search/%E9%9B%B6%E5%94%AE/1.htm">零售</a>
                        <div>大模型和数据要素赋能实体零售行业数字化转型建设和实施方案更多参考公众号:优享智库引言项目背景与意义数字化转型目标与期望实施方案概述零售行业现状及挑战实体零售行业现状数字化转型面临的挑战市场需求与趋势分析大模型与数据要素赋能策略大模型技术及应用场景数据要素采集、整合与治理赋能策略制定与实施路径数字化转型关键技术与解决方案人工智能技术及应用大数据分析与挖掘技术云计算、物联网等技术支持定制化解决方案设计</div>
                    </li>
                    <li><a href="/article/1901426678324850688.htm"
                           title="Spring Boot 脚手架搭建:新姿势" target="_blank">Spring Boot 脚手架搭建:新姿势</a>
                        <span class="text-muted">墨瑾轩</span>
<a class="tag" taget="_blank" href="/search/%E4%B8%80%E8%B5%B7%E5%AD%A6%E5%AD%A6Java%E3%80%90%E4%B8%80%E3%80%91/1.htm">一起学学Java【一】</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                        <div>关注墨瑾轩,带你探索编程的奥秘!超萌技术攻略,轻松晋级编程高手技术宝库已备好,就等你来挖掘订阅墨瑾轩,智趣学习不孤单即刻启航,编程之旅更有趣‍刨根问底:脚手架是什么?‍嘿,小伙伴们!今天咱们要聊的是如何搭建一个既漂亮又实用的SpringBoot脚手架。脚手架就像是盖房子时搭起的架子,它能帮助我们快速构建出项目的骨架,让我们可以更专注于业务逻辑的实现。那么,如何搭建这样一个脚手架呢?别急,咱们一步一</div>
                    </li>
                    <li><a href="/article/1901422772219867136.htm"
                           title="使用Unity引擎开发的Windows 11系统3D打地鼠游戏的方案" target="_blank">使用Unity引擎开发的Windows 11系统3D打地鼠游戏的方案</a>
                        <span class="text-muted">1079986725</span>
<a class="tag" taget="_blank" href="/search/%E6%89%8B%E6%9C%BA%E6%B8%B8%E6%88%8F/1.htm">手机游戏</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%80%85/1.htm">开发者</a><a class="tag" taget="_blank" href="/search/Windows/1.htm">Windows</a><a class="tag" taget="_blank" href="/search/%E6%B8%B8%E6%88%8F/1.htm">游戏</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E7%8E%A9%E6%B8%B8%E6%88%8F/1.htm">玩游戏</a>
                        <div>创建Unity项目:使用UnityHub新建3D项目设置目标平台为Windows场景搭建:csharp//地鼠控制器WhackAMole.csusingUnityEngine;usingSystem.Collections;publicclassWhackAMole:MonoBehaviour{publicfloatpopupDuration=1.5f;publicfloatminHideTime</div>
                    </li>
                    <li><a href="/article/1901420630335614976.htm"
                           title="Feign性能调优" target="_blank">Feign性能调优</a>
                        <span class="text-muted">௸྄ིོུ倾心ღ᭄ᝰꫛꫀꪝ</span>

                        <div>Feign性能调优测试demo地址:https://gitee.com/bjrts/spring-cloud-study-demo/tree/feign/父项目pom4.0.0com.baojiarenfeignpom1.0-SNAPSHOTeureka-servereureka-server02service-providerservice-provider02service-consumers</div>
                    </li>
                    <li><a href="/article/1901420629505142784.htm"
                           title="如何减少跨团队交付摩擦?——基于 DevOps 与敏捷的最佳实践" target="_blank">如何减少跨团队交付摩擦?——基于 DevOps 与敏捷的最佳实践</a>
                        <span class="text-muted">网罗开发</span>
<a class="tag" taget="_blank" href="/search/%E5%AE%9E%E6%88%98/1.htm">实战</a><a class="tag" taget="_blank" href="/search/%E5%AE%9E%E6%88%98%E6%BA%90%E7%A0%81/1.htm">实战源码</a><a class="tag" taget="_blank" href="/search/devops/1.htm">devops</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</a>
                        <div>网罗开发(小红书、快手、视频号同名)  大家好,我是展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、HarmonyOS、Java、Python等方向。在移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源等领域有深厚造诣。图书作者:《ESP32-C3物联网工程开发实战》图书作者:《SwiftUI入门,进阶与实战》超级个体:CO</div>
                    </li>
                    <li><a href="/article/1901420122359263232.htm"
                           title="FireRedASR:精准识别普通话、方言和歌曲歌词!小红书开源工业级自动语音识别模型" target="_blank">FireRedASR:精准识别普通话、方言和歌曲歌词!小红书开源工业级自动语音识别模型</a>
                        <span class="text-muted">蚝油菜花</span>
<a class="tag" taget="_blank" href="/search/%E6%AF%8F%E6%97%A5/1.htm">每日</a><a class="tag" taget="_blank" href="/search/AI/1.htm">AI</a><a class="tag" taget="_blank" href="/search/%E9%A1%B9%E7%9B%AE%E4%B8%8E%E5%BA%94%E7%94%A8%E5%AE%9E%E4%BE%8B/1.htm">项目与应用实例</a><a class="tag" taget="_blank" href="/search/%E8%AF%AD%E9%9F%B3%E8%AF%86%E5%88%AB/1.htm">语音识别</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%E5%BC%80%E6%BA%90/1.htm">人工智能开源</a>
                        <div>❤️如果你也关注AI的发展现状,且对AI应用开发感兴趣,我会每日分享大模型与AI领域的开源项目和应用,提供运行实例和实用教程,帮助你快速上手AI技术!微信公众号|搜一搜:蚝油菜花大家好,我是蚝油菜花,今天跟大家分享一下FireRedASR这个小红书开源的工业级自动语音识别模型。快速阅读FireRedASR是小红书开源的工业级自动语音识别模型,支持普通话、中文方言和英语。该模型在普通话ASR基准测试</div>
                    </li>
                    <li><a href="/article/1901419996110712832.htm"
                           title="Feign性能优化以及最佳实践" target="_blank">Feign性能优化以及最佳实践</a>
                        <span class="text-muted">南川北渔</span>
<a class="tag" taget="_blank" href="/search/SpringCloud/1.htm">SpringCloud</a><a class="tag" taget="_blank" href="/search/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/1.htm">性能优化</a><a class="tag" taget="_blank" href="/search/okhttp/1.htm">okhttp</a>
                        <div>1.Feign性能优化Feign底层发起http请求,依赖于其它的框架。其底层客户端实现包括:•URLConnection:默认实现,不支持连接池•ApacheHttpClient:支持连接池•OKHttp:支持连接池优化1:因此提高Feign的性能主要手段就是使用**连接池**代替默认的URLConnection。优化2:日志的级别,根据项目测试,确定最大连接数和单个路径的最大连接数,日志尽量用</div>
                    </li>
                    <li><a href="/article/1901419869430149120.htm"
                           title="java实现卷积神经网络CNN(附带源码)" target="_blank">java实现卷积神经网络CNN(附带源码)</a>
                        <span class="text-muted">Katie。</span>
<a class="tag" taget="_blank" href="/search/Java/1.htm">Java</a><a class="tag" taget="_blank" href="/search/%E5%AE%9E%E6%88%98%E9%A1%B9%E7%9B%AE/1.htm">实战项目</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                        <div>Java实现卷积神经网络(CNN)项目详解目录项目概述1.1项目背景与意义1.2什么是卷积神经网络(CNN)1.3卷积神经网络的应用场景相关知识与理论基础2.1神经网络与深度学习概述2.2卷积操作与卷积层原理2.3激活函数与池化层2.4全连接层与损失函数2.5前向传播、反向传播与梯度下降项目需求与分析3.1项目目标3.2功能需求分析3.3性能与扩展性要求3.4异常处理与鲁棒性考虑系统设计与实现思路</div>
                    </li>
                    <li><a href="/article/1901418232418463744.htm"
                           title="Agora-Uniapp-SDK 使用指南" target="_blank">Agora-Uniapp-SDK 使用指南</a>
                        <span class="text-muted">章瑗笛</span>

                        <div>Agora-Uniapp-SDK使用指南Agora-Uniapp-SDK项目地址:https://gitcode.com/gh_mirrors/ag/Agora-Uniapp-SDK1.项目目录结构及介绍Agora-Uniapp-SDK是一个基于Uni-app与AgoraAndroid和iOS视频SDK实现的集成库,它专门设计用于简化在uni-app项目中集成Agora音视频功能的过程。以下是其主</div>
                    </li>
                    <li><a href="/article/1901417728183431168.htm"
                           title="推荐开源项目:Free Templates for AWS CloudFormation" target="_blank">推荐开源项目:Free Templates for AWS CloudFormation</a>
                        <span class="text-muted">褚知茉Jade</span>

                        <div>推荐开源项目:FreeTemplatesforAWSCloudFormationaws-cf-templateswiddix/aws-cf-templates:是一个包含各种AWSCloudFormation模板的存储库。适合查找和学习AWSCloudFormation模板的示例,以及用于构建自己的基础设施。特点是包含了许多AWS服务和功能的模板示例,可以快速地了解如何使用CloudFormati</div>
                    </li>
                    <li><a href="/article/1901417728665776128.htm"
                           title="探索未来架构:基于AWS的响应式微服务框架" target="_blank">探索未来架构:基于AWS的响应式微服务框架</a>
                        <span class="text-muted">柏赢安Simona</span>

                        <div>探索未来架构:基于AWS的响应式微服务框架reactive-refarch-cloudformationReactiveMicroservicesArchitectureswithAmazonECS,AWSLambda,AmazonKinesisStreams,AmazonElastiCache,andAmazonDynamoDB项目地址:https://gitcode.com/gh_mirror</div>
                    </li>
                    <li><a href="/article/1901416467090436096.htm"
                           title="Python 简单后台项目的脚手架" target="_blank">Python 简单后台项目的脚手架</a>
                        <span class="text-muted">程序媛了了</span>
<a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>说明近期写了一个简单的项目,在后台运行获取网上的期货数据并保存到相应的数据库里。由于之前工作很多这种简单的类似调用接口或攫取数据的项目都是用Python来写,因此这次也继续用Python写。但是这次更换了几个包,此份文档简单来说明一下。依赖的包toml:用户解析配置文件,配置文件用的是toml格式。arrow:用于处理日期相关。loguru:用于日志处理。requests:用于http请求响应。p</div>
                    </li>
                    <li><a href="/article/1901415332245991424.htm"
                           title="Next.js博客项目-快速起步" target="_blank">Next.js博客项目-快速起步</a>
                        <span class="text-muted">Ktovoz</span>
<a class="tag" taget="_blank" href="/search/nextjs/1.htm">nextjs</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/react/1.htm">react</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
                        <div>作者:KTO原文:Next.js博客项目-快速起步简介:从nextjs博客模板开始,快速配置搭建自己的博客项目。部署出来的网站样式可以参考原文的网站。Next.js博客项目-快速起步使用的模板我们使用的模板是:tailwind-nextjs-starter-blog该模板有1.0版本和2.0版本。本文以1.0版本为例进行介绍。1.环境配置安装Node.js首先,确保你的机器上已安装Node.js。</div>
                    </li>
                    <li><a href="/article/1901406125400584192.htm"
                           title="Python入门实战:Python的代码重构" target="_blank">Python入门实战:Python的代码重构</a>
                        <span class="text-muted">AI智能涌现深度研究</span>
<a class="tag" taget="_blank" href="/search/DeepSeek/1.htm">DeepSeek</a><a class="tag" taget="_blank" href="/search/R1/1.htm">R1</a><a class="tag" taget="_blank" href="/search/%26amp%3B/1.htm">&</a><a class="tag" taget="_blank" href="/search/%E5%A4%A7%E6%95%B0%E6%8D%AEAI%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">大数据AI人工智能</a><a class="tag" taget="_blank" href="/search/%E5%A4%A7%E6%95%B0%E6%8D%AE/1.htm">大数据</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/%E8%AF%AD%E8%A8%80%E6%A8%A1%E5%9E%8B/1.htm">语言模型</a><a class="tag" taget="_blank" href="/search/AI/1.htm">AI</a><a class="tag" taget="_blank" href="/search/LLM/1.htm">LLM</a><a class="tag" taget="_blank" href="/search/Java/1.htm">Java</a><a class="tag" taget="_blank" href="/search/Python/1.htm">Python</a><a class="tag" taget="_blank" href="/search/%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/1.htm">架构设计</a>
                        <div>1.背景介绍Python是一种基于社区发展、易用性、生态系统完善、可扩展性强、性能卓越等特点的高级编程语言。作为一门解释型语言,它具有高效率、简洁语法、丰富的库函数、跨平台能力和多种开发范式等优点。但随着项目不断迭代更新,代码量逐渐增加,导致代码结构混乱、缺乏模块化设计、重复逻辑过多、命名不规范等问题。如何有效地组织、管理和维护代码、提升代码质量、更好地实现功能,是一个技术人的日常工作。如何进行代</div>
                    </li>
                    <li><a href="/article/1901404613538541568.htm"
                           title="python提取excel数据批量生成固定格式的word文件的问题" target="_blank">python提取excel数据批量生成固定格式的word文件的问题</a>
                        <span class="text-muted">鱼弦</span>
<a class="tag" taget="_blank" href="/search/%E3%80%90HOT%E3%80%91%E6%8A%80%E6%9C%AF%E7%83%AD%E8%B0%88/1.htm">【HOT】技术热谈</a><a class="tag" taget="_blank" href="/search/excel/1.htm">excel</a><a class="tag" taget="_blank" href="/search/word/1.htm">word</a>
                        <div>鱼弦:公众号【红尘灯塔】,CSDN博客专家、内容合伙人、新星导师、全栈领域优质创作者、51CTO(Top红人+专家博主)、github开源爱好者(go-zero源码二次开发、游戏后端架构https://github.com/Peakchen)使用Python从Excel中提取数据并生成固定格式的Word文档1.介绍本项目旨在介绍如何使用Python从Excel中提取数据并生成固定格式的Word文档</div>
                    </li>
                    <li><a href="/article/1901402595017158656.htm"
                           title="django自动添加接口文档" target="_blank">django自动添加接口文档</a>
                        <span class="text-muted">LCY133</span>
<a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/django%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%982023/1.htm">django项目实战2023</a><a class="tag" taget="_blank" href="/search/django/1.htm">django</a><a class="tag" taget="_blank" href="/search/sqlite/1.htm">sqlite</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a>
                        <div>以下是使用Django和django-rest-swagger(或替代方案drf-yasg)生成API接口文档的详细指南。由于django-rest-swagger已停止维护,推荐使用drf-yasg(支持Swagger2.0和OpenAPI3.0),但两种方法均会说明:一、方案选择与安装1.方案对比库名维护状态支持规范功能特点django-rest-swagger已弃用Swagger2.0旧项目</div>
                    </li>
                    <li><a href="/article/1901398682712993792.htm"
                           title=".NET/C# 生成二维码" target="_blank">.NET/C# 生成二维码</a>
                        <span class="text-muted">~请叫我小祸害~</span>
<a class="tag" taget="_blank" href="/search/.NET%2FC%23/1.htm">.NET/C#</a><a class="tag" taget="_blank" href="/search/.net/1.htm">.net</a><a class="tag" taget="_blank" href="/search/c%23/1.htm">c#</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a>
                        <div>大家好,在本章是如何通过.net/C#来生成二维码首先大家还是需要仔细阅读这篇文档有小细节就需要注意大家需要生成一些类,把我的方法复制进去,如果不想添加类的话,大家需要再主代码上更改引用信息找到我们所添加的方法不适用于零基础的朋友,适用于有点经验的懂一点代码的就行1、首先我们新建一个自己的项目.netcore或其他项目新建好后新建一个控制器我们需要在里面写自己的代码,在控制器里面生成一个index</div>
                    </li>
                    <li><a href="/article/1901395400154214400.htm"
                           title="金融时间序列分析(Yahoo Finance API实战)" target="_blank">金融时间序列分析(Yahoo Finance API实战)</a>
                        <span class="text-muted">闲人编程</span>
<a class="tag" taget="_blank" href="/search/Python%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E5%AE%9E%E6%88%98%E7%B2%BE%E8%A6%81/1.htm">Python数据分析实战精要</a><a class="tag" taget="_blank" href="/search/%E9%87%91%E8%9E%8D/1.htm">金融</a><a class="tag" taget="_blank" href="/search/yfinance/1.htm">yfinance</a><a class="tag" taget="_blank" href="/search/%E6%97%B6%E9%97%B4%E5%BA%8F%E5%88%97/1.htm">时间序列</a><a class="tag" taget="_blank" href="/search/%E6%B3%A2%E5%8A%A8%E7%8E%87/1.htm">波动率</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BD%92%E4%B8%80%E5%8C%96/1.htm">数据归一化</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/1.htm">数据分析</a><a class="tag" taget="_blank" href="/search/Dash/1.htm">Dash</a>
                        <div>这里写目录标题金融时间序列分析(YahooFinanceAPI实战)1.引言2.项目背景与意义3.数据集介绍4.GPU加速在数据处理中的应用5.交互式GUI设计与加速处理6.系统整体架构7.数学公式与指标计算8.完整代码实现9.代码自查与BUG排查10.总结与展望金融时间序列分析(YahooFinanceAPI实战)1.引言在当今金融市场中,时间序列数据分析是理解股票、指数以及其他金融产品走势的重</div>
                    </li>
                    <li><a href="/article/1901395021689581568.htm"
                           title="Python第二十三课:自监督学习 | 无标注数据的觉醒" target="_blank">Python第二十三课:自监督学习 | 无标注数据的觉醒</a>
                        <span class="text-muted">程之编</span>
<a class="tag" taget="_blank" href="/search/Python%E5%85%A8%E6%A0%88%E9%80%9A%E5%85%B3%E7%A7%98%E7%B1%8D/1.htm">Python全栈通关秘籍</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/1.htm">机器学习</a>
                        <div>本节目标理解自监督学习的核心范式与优势掌握对比学习(ContrastiveLearning)框架实现图像掩码自编码器(MaskedAutoencoder)开发实战项目:亿级参数模型轻量化探索数据增强的创造性艺术一、自监督学习基础(AI的拼图游戏)1.核心思想解析学习范式数据需求生活比喻监督学习海量标注数据老师逐题批改作业无监督学习纯无标签数据自学杂乱笔记自监督学习自动生成伪标签玩拼图游戏(根据碎片</div>
                    </li>
                    <li><a href="/article/1901393123104321536.htm"
                           title="【从零开始学习计算机科学】硬件设计与FPGA原理" target="_blank">【从零开始学习计算机科学】硬件设计与FPGA原理</a>
                        <span class="text-muted">贫苦游商</span>
<a class="tag" taget="_blank" href="/search/%E3%80%90%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E5%AD%A6%E4%B9%A0%E8%AE%A1%E7%AE%97%E6%9C%BA%E3%80%91%E7%A1%AC%E4%BB%B6%E8%AE%BE%E8%AE%A1/1.htm">【从零开始学习计算机】硬件设计</a><a class="tag" taget="_blank" href="/search/fpga%E5%BC%80%E5%8F%91/1.htm">fpga开发</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E5%AD%97%E9%80%BB%E8%BE%91/1.htm">数字逻辑</a><a class="tag" taget="_blank" href="/search/verilog/1.htm">verilog</a><a class="tag" taget="_blank" href="/search/HDL/1.htm">HDL</a><a class="tag" taget="_blank" href="/search/%E7%A1%AC%E4%BB%B6%E8%AE%BE%E8%AE%A1/1.htm">硬件设计</a><a class="tag" taget="_blank" href="/search/%E7%A1%AC%E4%BB%B6%E5%B7%A5%E7%A8%8B/1.htm">硬件工程</a>
                        <div>硬件设计硬件设计流程在设计硬件电路之前,首先要把大的框架和架构要搞清楚,这要求我们搞清楚要实现什么功能,然后找找有否能实现同样或相似功能的参考电路板(要懂得尽量利用他人的成果,越是有经验的工程师越会懂得借鉴他人的成果)。如果你找到了的参考设计,最好还是先看懂并理解,这一方面能提高我们的电路理解能力,而且能避免设计中的错误。在开始做硬件设计前,根据自己的项目需求,可以去找能够满足硬件功能设计的,有很</div>
                    </li>
                    <li><a href="/article/1901385937934413824.htm"
                           title="Web3.0 从入门到实战:一站式开发指南" target="_blank">Web3.0 从入门到实战:一站式开发指南</a>
                        <span class="text-muted">七七知享</span>
<a class="tag" taget="_blank" href="/search/Web/1.htm">Web</a><a class="tag" taget="_blank" href="/search/web3/1.htm">web3</a><a class="tag" taget="_blank" href="/search/html5/1.htm">html5</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%8C%BA%E5%9D%97%E9%93%BE/1.htm">区块链</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/1.htm">网络安全</a><a class="tag" taget="_blank" href="/search/%E5%AE%89%E5%85%A8/1.htm">安全</a><a class="tag" taget="_blank" href="/search/web%E5%AE%89%E5%85%A8/1.htm">web安全</a>
                        <div>在科技浪潮持续翻涌的当下,Web3.0作为互联网发展的全新篇章,正以前所未有的姿态重塑数字世界格局。从去中心化应用(DApps)蓬勃兴起,到区块链技术成为底层支撑架构,Web3.0开启了一个用户真正掌控数据、价值自由流通的崭新时代。对于怀揣探索精神的开发者而言,投身Web3.0领域,不仅意味着解锁全新技术栈,更能参与塑造互联网的未来形态。本文精心打造了一套从理论基石铺陈,到实战项目落地的Web3.</div>
                    </li>
                    <li><a href="/article/1901382407387410432.htm"
                           title="[rust] rust学习" target="_blank">[rust] rust学习</a>
                        <span class="text-muted">1nv1s1ble</span>
<a class="tag" taget="_blank" href="/search/rust/1.htm">rust</a><a class="tag" taget="_blank" href="/search/rust/1.htm">rust</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>rust学习1.项目组织结构工程#创建一个工程cargonewmy-project工作空间在Rust中,工作空间(Workspace)是一个包含多个Rust项目的共享环境,用于管理多个crate(库或可执行文件)。它允许多个Rust项目共享Cargo.lock、依赖项和target/目录,提高编译效率,并且适用于多包管理、模块化开发。为什么使用Rust工作空间?Rust工作空间适用于以下场景:管理</div>
                    </li>
                    <li><a href="/article/1901382153720098816.htm"
                           title="京准电钟分享:水利系统NTP网络时间服务器应用" target="_blank">京准电钟分享:水利系统NTP网络时间服务器应用</a>
                        <span class="text-muted">北京华人开创公司</span>
<a class="tag" taget="_blank" href="/search/%E6%97%B6%E9%92%9F%E7%B3%BB%E7%BB%9F/1.htm">时钟系统</a><a class="tag" taget="_blank" href="/search/%E5%8C%97%E6%96%97%E5%8D%AB%E6%98%9F%E6%8E%88%E6%97%B6/1.htm">北斗卫星授时</a><a class="tag" taget="_blank" href="/search/NTP%E6%97%B6%E9%97%B4%E5%90%8C%E6%AD%A5/1.htm">NTP时间同步</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C/1.htm">网络</a><a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</a><a class="tag" taget="_blank" href="/search/%E6%97%B6%E9%97%B4%E5%90%8C%E6%AD%A5/1.htm">时间同步</a><a class="tag" taget="_blank" href="/search/%E6%97%B6%E9%92%9F%E5%90%8C%E6%AD%A5/1.htm">时钟同步</a><a class="tag" taget="_blank" href="/search/NTP%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">NTP服务器</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C%E6%97%B6%E9%97%B4%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">网络时间服务器</a>
                        <div>京准电钟分享:水利系统NTP网络时间服务器应用京准电钟分享:水利系统NTP网络时间服务器应用1.项目背景水利控制系统涵盖水文监测、闸门控制、泵站调度、数据采集与传输等多个子系统,设备分布广泛且需协同工作。系统内各设备(如PLC、RTU、SCADA服务器、传感器等)的时间一致性直接影响数据记录的准确性、事件报警的时序性以及故障分析的可靠性。为实现全系统高精度时间同步,需部署NTP(NetworkTi</div>
                    </li>
                    <li><a href="/article/1901371311112908800.htm"
                           title="腾讯 IEG 游戏前沿技术 一面复盘" target="_blank">腾讯 IEG 游戏前沿技术 一面复盘</a>
                        <span class="text-muted">andrew_1219</span>
<a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95%E7%BB%8F%E9%AA%8C/1.htm">面试经验</a><a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/sql/1.htm">sql</a><a class="tag" taget="_blank" href="/search/redis/1.htm">redis</a>
                        <div>前言投了个实习内推后台开发,本来要电话先交流的那天直接走流程下午面试了,对面两人,面了有一个小时,游戏本的构思续航忘记插电了最后还掉线了一下,趁着还记得面试内容复盘一下自我介绍一下答:您好,我是深大26届xxx的xxx,对贵公司后台开发的岗位很感兴趣。现在在xxx做后台开发实习生,负责项目的开发和运维相关,还有一部分系统设计相关的的工作,之前在学校中也做过web开发相关的项目。了解到贵公司正在招聘</div>
                    </li>
                    <li><a href="/article/1901370429235326976.htm"
                           title="MongoDB集合(表)自动创建机制" target="_blank">MongoDB集合(表)自动创建机制</a>
                        <span class="text-muted">zpjing~.~</span>
<a class="tag" taget="_blank" href="/search/mongodb/1.htm">mongodb</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a>
                        <div>开发项目时,要整理上线涉及的表,MongoDB里新创建的表是不用整理发给运维的,因为代码中插入数据时,MongoDB会自动创建哦MongoDB中的集合(表)是在插入数据时自动创建的。当你第一次向一个不存在的集合插入文档时,MongoDB会自动创建该集合。你不需要像在关系型数据库中那样预先定义表结构。例如,执行以下操作时:db.myCollection.insertOne({name:"Alice"</div>
                    </li>
                    <li><a href="/article/1901368663571755008.htm"
                           title="Flutter设计模式全面解析:单例模式" target="_blank">Flutter设计模式全面解析:单例模式</a>
                        <span class="text-muted">那年星空</span>
<a class="tag" taget="_blank" href="/search/flutter/1.htm">flutter</a><a class="tag" taget="_blank" href="/search/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/1.htm">设计模式</a><a class="tag" taget="_blank" href="/search/%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/1.htm">单例模式</a>
                        <div>谈到设计模式这个“古老”的话题,大家先别急着划走哈,虽然对它再熟悉不过,几乎是最初开始学习编程到现在伴随着我们整个编程生涯,最早Java、C++语言实现的各种设计模式到现在还会经常有所接触,面试中也是必问的环节,在开发Flutter项目的时候,也会多少借鉴了其它语言设计模式的实现,但始终觉得dart语言实现的设计模式理解不够系统,有的实现还缺点儿dart语言本身的语法特性。加上最近在看一些Flut</div>
                    </li>
                                <li><a href="/article/60.htm"
                                       title="Dom" target="_blank">Dom</a>
                                    <span class="text-muted">周华华</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a>
                                    <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&q</div>
                                </li>
                                <li><a href="/article/187.htm"
                                       title="【Spark九十六】RDD API之combineByKey" target="_blank">【Spark九十六】RDD API之combineByKey</a>
                                    <span class="text-muted">bit1129</span>
<a class="tag" taget="_blank" href="/search/spark/1.htm">spark</a>
                                    <div>1. combineByKey函数的运行机制 
  
RDD提供了很多针对元素类型为(K,V)的API,这些API封装在PairRDDFunctions类中,通过Scala隐式转换使用。这些API实现上是借助于combineByKey实现的。combineByKey函数本身也是RDD开放给Spark开发人员使用的API之一 
  
首先看一下combineByKey的方法说明:</div>
                                </li>
                                <li><a href="/article/314.htm"
                                       title="msyql设置密码报错:ERROR 1372 (HY000): 解决方法详解" target="_blank">msyql设置密码报错:ERROR 1372 (HY000): 解决方法详解</a>
                                    <span class="text-muted">daizj</span>
<a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a><a class="tag" taget="_blank" href="/search/%E8%AE%BE%E7%BD%AE%E5%AF%86%E7%A0%81/1.htm">设置密码</a>
                                    <div>MySql给用户设置权限同时指定访问密码时,会提示如下错误: 
ERROR 1372 (HY000): Password hash should be a 41-digit hexadecimal number; 
  
问题原因:你输入的密码是明文。不允许这么输入。 
  
解决办法:用select password('你想输入的密码');查询出你的密码对应的字符串, 
然后</div>
                                </li>
                                <li><a href="/article/441.htm"
                                       title="路漫漫其修远兮 吾将上下而求索" target="_blank">路漫漫其修远兮 吾将上下而求索</a>
                                    <span class="text-muted">周凡杨</span>
<a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0+%E6%80%9D%E7%B4%A2/1.htm">学习 思索</a>
                                    <div>王国维在他的《人间词话》中曾经概括了为学的三种境界古今之成大事业、大学问者,罔不经过三种之境界。“昨夜西风凋碧树。独上高楼,望尽天涯路。”此第一境界也。“衣带渐宽终不悔,为伊消得人憔悴。”此第二境界也。“众里寻他千百度,蓦然回首,那人却在灯火阑珊处。”此第三境界也。学习技术,这也是你必须经历的三种境界。第一层境界是说,学习的路是漫漫的,你必须做好充分的思想准备,如果半途而废还不如不要开始。这里,注</div>
                                </li>
                                <li><a href="/article/568.htm"
                                       title="Hadoop(二)对话单的操作" target="_blank">Hadoop(二)对话单的操作</a>
                                    <span class="text-muted">朱辉辉33</span>
<a class="tag" taget="_blank" href="/search/hadoop/1.htm">hadoop</a>
                                    <div>Debug: 
 
1、 
 
A = LOAD '/user/hue/task.txt' USING PigStorage(' ') 
AS (col1,col2,col3); 
DUMP A; 
 
//输出结果前几行示例: 
(>ggsnPDPRecord(21),,) 
(-->recordType(0),,) 
(-->networkInitiation(1),,) 
</div>
                                </li>
                                <li><a href="/article/695.htm"
                                       title="web报表工具FineReport常用函数的用法总结(日期和时间函数)" target="_blank">web报表工具FineReport常用函数的用法总结(日期和时间函数)</a>
                                    <span class="text-muted">老A不折腾</span>
<a class="tag" taget="_blank" href="/search/finereport/1.htm">finereport</a><a class="tag" taget="_blank" href="/search/%E6%8A%A5%E8%A1%A8%E5%B7%A5%E5%85%B7/1.htm">报表工具</a><a class="tag" taget="_blank" href="/search/web%E5%BC%80%E5%8F%91/1.htm">web开发</a>
                                    <div>web报表工具FineReport常用函数的用法总结(日期和时间函数) 
  
说明:凡函数中以日期作为参数因子的,其中日期的形式都必须是yy/mm/dd。而且必须用英文环境下双引号(" ")引用。 
  
DATE 
DATE(year,month,day):返回一个表示某一特定日期的系列数。 
Year:代表年,可为一到四位数。 
Month:代表月份。</div>
                                </li>
                                <li><a href="/article/822.htm"
                                       title="c++ 宏定义中的##操作符" target="_blank">c++ 宏定义中的##操作符</a>
                                    <span class="text-muted">墙头上一根草</span>
<a class="tag" taget="_blank" href="/search/C%2B%2B/1.htm">C++</a>
                                    <div>#与##在宏定义中的--宏展开 #include <stdio.h> #define f(a,b) a##b #define g(a)   #a #define h(a) g(a) int main() {       &nbs</div>
                                </li>
                                <li><a href="/article/949.htm"
                                       title="分析Spring源代码之,DI的实现" target="_blank">分析Spring源代码之,DI的实现</a>
                                    <span class="text-muted">aijuans</span>
<a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/DI/1.htm">DI</a><a class="tag" taget="_blank" href="/search/%E7%8E%B0/1.htm">现</a><a class="tag" taget="_blank" href="/search/%E6%BA%90%E4%BB%A3%E7%A0%81/1.htm">源代码</a>
                                    <div>(转) 
   
分析Spring源代码之,DI的实现  
2012/1/3 by tony 
                接着上次的讲,以下这个sample    
[java]  
view plain 
copy 
print 
</div>
                                </li>
                                <li><a href="/article/1076.htm"
                                       title="for循环的进化" target="_blank">for循环的进化</a>
                                    <span class="text-muted">alxw4616</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a>
                                    <div>// for循环的进化
// 菜鸟
for (var i = 0; i < Things.length ; i++) {
	// Things[i]
}

// 老鸟
for (var i = 0, len = Things.length; i < len; i++) {
	// Things[i]
}

// 大师
for (var i = Things.le</div>
                                </li>
                                <li><a href="/article/1203.htm"
                                       title="网络编程Socket和ServerSocket简单的使用" target="_blank">网络编程Socket和ServerSocket简单的使用</a>
                                    <span class="text-muted">百合不是茶</span>
<a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80/1.htm">网络编程基础</a><a class="tag" taget="_blank" href="/search/IP%E5%9C%B0%E5%9D%80%E7%AB%AF%E5%8F%A3/1.htm">IP地址端口</a>
                                    <div>  
网络编程;TCP/IP协议 
  
网络:实现计算机之间的信息共享,数据资源的交换 
  
协议:数据交换需要遵守的一种协议,按照约定的数据格式等写出去 
  
端口:用于计算机之间的通信 
     每运行一个程序,系统会分配一个编号给该程序,作为和外界交换数据的唯一标识 
0~65535 
  
查看被使用的</div>
                                </li>
                                <li><a href="/article/1330.htm"
                                       title="JDK1.5 生产消费者" target="_blank">JDK1.5 生产消费者</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/thread/1.htm">thread</a><a class="tag" taget="_blank" href="/search/%E7%94%9F%E4%BA%A7%E6%B6%88%E8%B4%B9%E8%80%85/1.htm">生产消费者</a><a class="tag" taget="_blank" href="/search/java%E5%A4%9A%E7%BA%BF%E7%A8%8B/1.htm">java多线程</a>
                                    <div>ArrayBlockingQueue: 
       一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。队列的头部 是在队列中存在时间最长的元素。队列的尾部 是在队列中存在时间最短的元素。新元素插入到队列的尾部,队列检索操作则是从队列头部开始获得元素。 
ArrayBlockingQueue的常用方法: 
</div>
                                </li>
                                <li><a href="/article/1457.htm"
                                       title="JAVA版身份证获取性别、出生日期及年龄" target="_blank">JAVA版身份证获取性别、出生日期及年龄</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E6%80%A7%E5%88%AB/1.htm">性别</a><a class="tag" taget="_blank" href="/search/%E5%87%BA%E7%94%9F%E6%97%A5%E6%9C%9F/1.htm">出生日期</a><a class="tag" taget="_blank" href="/search/%E5%B9%B4%E9%BE%84/1.htm">年龄</a>
                                    <div>        工作中需要根据身份证获取性别、出生日期及年龄,且要还要支持15位长度的身份证号码,网上搜索了一下,经过测试好像多少存在点问题,干脆自已写一个。 
CertificateNo.java 
package com.bijian.study;

import java.util.Calendar;
import </div>
                                </li>
                                <li><a href="/article/1584.htm"
                                       title="【Java范型六】范型与枚举" target="_blank">【Java范型六】范型与枚举</a>
                                    <span class="text-muted">bit1129</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                                    <div>首先,枚举类型的定义不能带有类型参数,所以,不能把枚举类型定义为范型枚举类,例如下面的枚举类定义是有编译错的 
  
public enum EnumGenerics<T> { //编译错,提示枚举不能带有范型参数
    OK, ERROR;
    public <T> T get(T type) {
        return null;
    </div>
                                </li>
                                <li><a href="/article/1711.htm"
                                       title="【Nginx五】Nginx常用日志格式含义" target="_blank">【Nginx五】Nginx常用日志格式含义</a>
                                    <span class="text-muted">bit1129</span>
<a class="tag" taget="_blank" href="/search/nginx/1.htm">nginx</a>
                                    <div>1. log_format 
1.1 log_format指令用于指定日志的格式,格式: 
  
log_format name(格式名称) type(格式样式) 
  
1.2 如下是一个常用的Nginx日志格式: 
  
log_format      main    '[$time_local]|$request_time|$status|$body_bytes</div>
                                </li>
                                <li><a href="/article/1838.htm"
                                       title="Lua 语言 15 分钟快速入门" target="_blank">Lua 语言 15 分钟快速入门</a>
                                    <span class="text-muted">ronin47</span>
<a class="tag" taget="_blank" href="/search/lua+%E5%9F%BA%E7%A1%80/1.htm">lua 基础</a>
                                    <div>-
- 
单行注释   
-
-
[[   
    
[多行注释]   
-
-
]]       
-
-
-
-
-
-
-
-
-
-   
- 
1. 
变量 & 控制流   
-
-
-
-
-
-
-
-
-
-   
num 
= 
23 
-
- 
数字都是双精度   
str 
= 
'aspythonstring' 
</div>
                                </li>
                                <li><a href="/article/1965.htm"
                                       title="java-35.求一个矩阵中最大的二维矩阵 ( 元素和最大 )" target="_blank">java-35.求一个矩阵中最大的二维矩阵 ( 元素和最大 )</a>
                                    <span class="text-muted">bylijinnan</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                                    <div>the idea is from: 
http://blog.csdn.net/zhanxinhang/article/details/6731134 
 


public class MaxSubMatrix {

	/**see http://blog.csdn.net/zhanxinhang/article/details/6731134
	 * Q35
求一个矩阵中最大的二维</div>
                                </li>
                                <li><a href="/article/2092.htm"
                                       title="mongoDB文档型数据库特点" target="_blank">mongoDB文档型数据库特点</a>
                                    <span class="text-muted">开窍的石头</span>
<a class="tag" taget="_blank" href="/search/mongoDB%E6%96%87%E6%A1%A3%E5%9E%8B%E6%95%B0%E6%8D%AE%E5%BA%93%E7%89%B9%E7%82%B9/1.htm">mongoDB文档型数据库特点</a>
                                    <div>MongoDD: 文档型数据库存储的是Bson文档-->json的二进制 
 
特点:内部是执行引擎是js解释器,把文档转成Bson结构,在查询时转换成js对象。 
 
mongoDB传统型数据库对比 
   传统类型数据库:结构化数据,定好了表结构后每一个内容符合表结构的。也就是说每一行每一列的数据都是一样的 
   文档型数据库:不用定好数据结构,</div>
                                </li>
                                <li><a href="/article/2219.htm"
                                       title="[毕业季节]欢迎广大毕业生加入JAVA程序员的行列" target="_blank">[毕业季节]欢迎广大毕业生加入JAVA程序员的行列</a>
                                    <span class="text-muted">comsci</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                                    <div> 
    一年一度的毕业季来临了。。。。。。。。 
 
     正在投简历的学弟学妹们。。。如果觉得学校推荐的单位和公司不适合自己的兴趣和专业,可以考虑来我们软件行业,做一名职业程序员。。。 
 
     软件行业的开发工具中,对初学者最友好的就是JAVA语言了,网络上不仅仅有大量的</div>
                                </li>
                                <li><a href="/article/2346.htm"
                                       title="PHP操作Excel – PHPExcel 基本用法详解" target="_blank">PHP操作Excel – PHPExcel 基本用法详解</a>
                                    <span class="text-muted">cuiyadll</span>
<a class="tag" taget="_blank" href="/search/PHP/1.htm">PHP</a><a class="tag" taget="_blank" href="/search/Excel/1.htm">Excel</a>
                                    <div>导出excel属性设置//Include classrequire_once('Classes/PHPExcel.php');require_once('Classes/PHPExcel/Writer/Excel2007.php');$objPHPExcel = new PHPExcel();//Set properties 设置文件属性$objPHPExcel->getProperties</div>
                                </li>
                                <li><a href="/article/2473.htm"
                                       title="IBM Webshpere MQ Client User Issue (MCAUSER)" target="_blank">IBM Webshpere MQ Client User Issue (MCAUSER)</a>
                                    <span class="text-muted">darrenzhu</span>
<a class="tag" taget="_blank" href="/search/IBM/1.htm">IBM</a><a class="tag" taget="_blank" href="/search/jms/1.htm">jms</a><a class="tag" taget="_blank" href="/search/user/1.htm">user</a><a class="tag" taget="_blank" href="/search/MQ/1.htm">MQ</a><a class="tag" taget="_blank" href="/search/MCAUSER/1.htm">MCAUSER</a>
                                    <div>IBM MQ JMS Client去连接远端MQ Server的时候,需要提供User和Password吗? 
答案是根据情况而定,取决于所定义的Channel里面的属性Message channel agent user identifier (MCAUSER)的设置。 
 
 
http://stackoverflow.com/questions/20209429/how-mca-user-i</div>
                                </li>
                                <li><a href="/article/2600.htm"
                                       title="网线的接法" target="_blank">网线的接法</a>
                                    <span class="text-muted">dcj3sjt126com</span>

                                    <div>一、PC连HUB (直连线)A端:(标准568B):白橙,橙,白绿,蓝,白蓝,绿,白棕,棕。 B端:(标准568B):白橙,橙,白绿,蓝,白蓝,绿,白棕,棕。 二、PC连PC (交叉线)A端:(568A): 白绿,绿,白橙,蓝,白蓝,橙,白棕,棕; B端:(标准568B):白橙,橙,白绿,蓝,白蓝,绿,白棕,棕。 三、HUB连HUB&nb</div>
                                </li>
                                <li><a href="/article/2727.htm"
                                       title="Vimium插件让键盘党像操作Vim一样操作Chrome" target="_blank">Vimium插件让键盘党像操作Vim一样操作Chrome</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/chrome/1.htm">chrome</a><a class="tag" taget="_blank" href="/search/vim/1.htm">vim</a>
                                    <div>什么是键盘党? 
 
 键盘党是指尽可能将所有电脑操作用键盘来完成,而不去动鼠标的人。鼠标应该说是新手们的最爱,很直观,指哪点哪,很听话!不过常常使用电脑的人,如果一直使用鼠标的话,手会发酸,因为操作鼠标的时候,手臂不是在一个自然的状态,臂肌会处于绷紧状态。而使用键盘则双手是放松状态,只有手指在动。而且尽量少的从鼠标移动到键盘来回操作,也省不少事。 
 在chrome里安装 vimium 插件 
</div>
                                </li>
                                <li><a href="/article/2854.htm"
                                       title="MongoDB查询(2)——数组查询[六]" target="_blank">MongoDB查询(2)——数组查询[六]</a>
                                    <span class="text-muted">eksliang</span>
<a class="tag" taget="_blank" href="/search/mongodb/1.htm">mongodb</a><a class="tag" taget="_blank" href="/search/MongoDB%E6%9F%A5%E8%AF%A2%E6%95%B0%E7%BB%84/1.htm">MongoDB查询数组</a>
                                    <div>MongoDB查询数组 
转载请出自出处:http://eksliang.iteye.com/blog/2177292 一、概述 
 MongoDB查询数组与查询标量值是一样的,例如,有一个水果列表,如下所示: 
> db.food.find()
{ "_id" : "001", "fruits" : [ "苹</div>
                                </li>
                                <li><a href="/article/2981.htm"
                                       title="cordova读写文件(1)" target="_blank">cordova读写文件(1)</a>
                                    <span class="text-muted">gundumw100</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/Cordova/1.htm">Cordova</a>
                                    <div>使用cordova可以很方便的在手机sdcard中读写文件。 
 
首先需要安装cordova插件:file 
命令为: 
 
cordova plugin add org.apache.cordova.file 
 
然后就可以读写文件了,这里我先是写入一个文件,具体的JS代码为: 
 


var datas=null;//datas need write
var directory=&</div>
                                </li>
                                <li><a href="/article/3108.htm"
                                       title="HTML5 FormData 进行文件jquery ajax 上传 到又拍云" target="_blank">HTML5 FormData 进行文件jquery ajax 上传 到又拍云</a>
                                    <span class="text-muted">ileson</span>
<a class="tag" taget="_blank" href="/search/jquery/1.htm">jquery</a><a class="tag" taget="_blank" href="/search/Ajax/1.htm">Ajax</a><a class="tag" taget="_blank" href="/search/html5/1.htm">html5</a><a class="tag" taget="_blank" href="/search/FormData/1.htm">FormData</a>
                                    <div>html5 新东西:FormData  可以提交二进制数据。 
 
 
页面test.html 
 

<!DOCTYPE>
<html>
<head>
<title> formdata file jquery ajax upload</title>
</head>

<body>
<</div>
                                </li>
                                <li><a href="/article/3235.htm"
                                       title="swift appearanceWhenContainedIn:(version1.2 xcode6.4)" target="_blank">swift appearanceWhenContainedIn:(version1.2 xcode6.4)</a>
                                    <span class="text-muted">啸笑天</span>
<a class="tag" taget="_blank" href="/search/version/1.htm">version</a>
                                    <div>  
swift1.2中没有oc中对应的方法: 
+ (instancetype)appearanceWhenContainedIn:(Class <UIAppearanceContainer>)ContainerClass, ... NS_REQUIRES_NIL_TERMINATION; 
 解决方法: 
在swift项目中新建oc类如下: 
#import &</div>
                                </li>
                                <li><a href="/article/3362.htm"
                                       title="java实现SMTP邮件服务器" target="_blank">java实现SMTP邮件服务器</a>
                                    <span class="text-muted">macroli</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B/1.htm">编程</a>
                                    <div>电子邮件传递可以由多种协议来实现。目前,在Internet 网上最流行的三种电子邮件协议是SMTP、POP3 和 IMAP,下面分别简单介绍。 
  ◆ SMTP 协议 
  简单邮件传输协议(Simple Mail Transfer Protocol,SMTP)是一个运行在TCP/IP之上的协议,用它发送和接收电子邮件。SMTP 服务器在默认端口25上监听。SMTP客户使用一组简单的、基于文本的</div>
                                </li>
                                <li><a href="/article/3489.htm"
                                       title="mongodb group by having where 查询sql" target="_blank">mongodb group by having where 查询sql</a>
                                    <span class="text-muted">qiaolevip</span>
<a class="tag" taget="_blank" href="/search/%E6%AF%8F%E5%A4%A9%E8%BF%9B%E6%AD%A5%E4%B8%80%E7%82%B9%E7%82%B9/1.htm">每天进步一点点</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0%E6%B0%B8%E6%97%A0%E6%AD%A2%E5%A2%83/1.htm">学习永无止境</a><a class="tag" taget="_blank" href="/search/mongo/1.htm">mongo</a><a class="tag" taget="_blank" href="/search/%E7%BA%B5%E8%A7%82%E5%8D%83%E8%B1%A1/1.htm">纵观千象</a>
                                    <div>SELECT cust_id,
       SUM(price) as total
FROM orders
WHERE status = 'A'
GROUP BY cust_id
HAVING total > 250

db.orders.aggregate( [
   { $match: { status: 'A' } },
   {
     $group: {
</div>
                                </li>
                                <li><a href="/article/3616.htm"
                                       title="Struts2 Pojo(六)" target="_blank">Struts2 Pojo(六)</a>
                                    <span class="text-muted">Luob.</span>
<a class="tag" taget="_blank" href="/search/POJO/1.htm">POJO</a><a class="tag" taget="_blank" href="/search/strust2/1.htm">strust2</a>
                                    <div>注意:附件中有完整案例 
1.采用POJO对象的方法进行赋值和传值 
2.web配置 
 

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
	xmlns="http://java.sun.com/xml/ns/javaee&q</div>
                                </li>
                                <li><a href="/article/3743.htm"
                                       title="struts2步骤" target="_blank">struts2步骤</a>
                                    <span class="text-muted">wuai</span>
<a class="tag" taget="_blank" href="/search/struts/1.htm">struts</a>
                                    <div>1、添加jar包 
2、在web.xml中配置过滤器 
 <filter> 
       <filter-name>struts2</filter-name> 
       <filter-class>org.apache.st</div>
                                </li>
                </ul>
            </div>
        </div>
    </div>

<div>
    <div class="container">
        <div class="indexes">
            <strong>按字母分类:</strong>
            <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a
                href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a
                href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a
                href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a
                href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a
                href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a
                href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a
                href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a
                href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a>
        </div>
    </div>
</div>
<footer id="footer" class="mb30 mt30">
    <div class="container">
        <div class="footBglm">
            <a target="_blank" href="/">首页</a> -
            <a target="_blank" href="/custom/about.htm">关于我们</a> -
            <a target="_blank" href="/search/Java/1.htm">站内搜索</a> -
            <a target="_blank" href="/sitemap.txt">Sitemap</a> -
            <a target="_blank" href="/custom/delete.htm">侵权投诉</a>
        </div>
        <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved.
<!--            <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>-->
        </div>
    </div>
</footer>
<!-- 代码高亮 -->
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script>
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script>
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script>
<link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/>
<script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script>





</body>

</html>