忽然一想好久不写博客了,工作原因个人原因,这些天一直希望一天假如36个小时该多好,但是,假如不可能。

由于近期在项目中接触了lucene,这个已经没有人维护的全文搜索框架,确实踩了不少坑,为什么用lucene呢?其实我也不知道

关于lucene原理和全文搜索引擎的一些介绍,园子里有这几篇写的还是很好的

http://www.cnblogs.com/skybreak/archive/2013/05/06/3063520.html

http://kb.cnblogs.com/page/52642/

由于完全没有接触过lucene,一开始当然是从X度,bing上搜索关于lucene.net的教程,发现找不到好用的,lucene已经好久没有维护了,如果细心阅读源码会发现许多匪夷所思的设计。对于lucene可以理解为一个数据库,lucene提供了添加数据(创建索引),以及快速全文检索的api。

通过学习,我们发现,lucene.net给我们很多对象

Lucene.Net.Store.Directory:lucene文档的存放目录,我这里把它理解为一张表,实际上这一张表的字段可以不一样

Lucene.Net.Documents.Documents:lucene文档,相当于一条数据

Lucene.Net.Index.IndexWriter:用于向Lucene.Net.Store.Directory中添加Lucene.Net.Documents.Documents

Lucene.Net.Analysis.Standard.StandardAnalyzer:词法分析器

当然,还有一系列查询的类,这里就不列举了,说了这么多,我这篇随笔主要介绍什么呢?

既然把lucene理解为一个数据库了,从框架使用者的角度看,不需要拘泥于lucene究竟是如何存储和检索数据的,当然,第一次使用时还是要学习的,但是写完了就会发现代码写的好繁琐啊,每次都要创建Directory,Analyzer写查询语句创建对象,比如项目一开始对站内所有信息进行检索,一次加入所有数据,搜索所有数据,没有问题,后来需要对另一个模块的信息进行单表检索,会发现代码逐渐开始乱了,但是由于时间问题和开发经验一开始很难想到好的设计。刚开始时,项目经理指导将每个需要全文搜索的对象先独立出来,实现一个接口,程序运行时反射实现该接口的类(暂且成他为LuceneModel),即可实现搜索,但是后来需求要实现两种语言的搜索,根据当前前端提供的cookie来查询指定语言的数据,于是我就又给每一个LuceneModel添加一个Language字段,并且Language字段的搜索是一个且的关系(在满足搜索关键字后并且该条数据的语言必须 为从cookie中读取的)。

显然,需要对搜索这个模块进行单独的维护了,这样修改方便,并且也可以供其他项目使用,也是软件设计原则吧!

IndexerAttribute

  1. [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
  2. public class IndexerAttribute : Attribute
  3. {
  4. public bool Index;
  5. public bool Store;
  6. /// <summary>
  7. ///
  8. /// </summary>
  9. /// <param name="index">是否为其创建索引</param>
  10. /// <param name="store">是否存储原始数据</param>
  11. public IndexerAttribute(bool index,bool store)
  12. {
  13. Index = index;
  14. Store = store;
  15. }
  16. }

表示对模型具体字段的索引方式

LuceneDB

  1. public abstract class LuceneDB:IDisposable
  2. {
  3. private ILunceneDBPathProvider_dbPathProvider = LunceneDBPathProviders.Current;
  4. private string _dbPath;
  5. protected System.IO.DirectoryInfo _sysDirectory;
  6. protected Lucene.Net.Store.Directory _luDirectory;
  7. protected Lucene.Net.Analysis.Standard.StandardAnalyzer _analyzer;
  8. protected Lucene.Net.Index.IndexWriter _indexWriter;
  9. protected bool _isOpen = false;
  10. public LuceneDB(string path)
  11. {
  12. _dbPath = path;
  13. }
  14. public LuceneDB() { }
  15. public void SetDBPath(Type type)
  16. {
  17. if (null == _dbPath)
  18. {
  19. _dbPath = _dbPathProvider.Get(type);
  20. }
  21. }
  22.  
  23. public string DBPath
  24. {
  25. get
  26. {
  27. return _dbPath;
  28. }
  29. }
  30.  
  31. protected abstract void Open();
  32. public virtual void Dispose()
  33. {
  34. _analyzer.Close();
  35. _analyzer.Dispose();
  36. }
  37. }

首先LuceneDB,表示一个连接对象,有子类LuceneDBIndexer和LuceneDBSearcher,两者用到的资源不一样,所以提供了一个需要实现的Open方法。通常情况下一个Lucene文件夹下有多个索引文件夹,每个索引文件夹有着相似的数据结构(同一个数据类型),为了使这一个模块能够让别的项目使用,第一个要解决的问题就是Lucene文件夹位置的问题了,对于web项目,可以存在APPData也可以存在bin目录下,这里也照顾了下对于Console应用。

ILunceneDBPathProvider

  1. public interface ILunceneDBPathProvider
  2. {
  3. string Get(Type type);
  4. }

默认的是一个AppDataLunceneDBPathProvider

  1. public class AppDataLunceneDBPathProvider : ILunceneDBPathProvider
  2. {
  3. private string _prePath = AppDomain.CurrentDomain.BaseDirectory;
  4. public string Get(Type type)
  5. {
  6. return _prePath + @"\App_Data\Index\" + type.Name;
  7. }
  8. }

这样如果有需要检索的User类,他的lucene文件夹就为App_Data\Index\User

LuceneDBIndexer

  1. public class LuceneDBIndexer: LuceneDB
  2. {
  3. private Dictionary<Type,IEnumerable<PropertyInfo>> _tempProps;
  4. public LuceneDBIndexer(string path) : base(path)
  5. {
  6. }
  7. public LuceneDBIndexer() { }
  8. protected override void Open()
  9. {
  10. if (_isOpen)
  11. {
  12. Dispose();
  13. }
  14. if (!System.IO.Directory.Exists(DBPath))
  15. {
  16. System.IO.Directory.CreateDirectory(DBPath);
  17. }
  18. _analyzer = new Lucene.Net.Analysis.Standard.StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30);
  19. _luDirectory = Lucene.Net.Store.FSDirectory.Open(new System.IO.DirectoryInfo(DBPath));
  20. _indexWriter = new Lucene.Net.Index.IndexWriter(_luDirectory, _analyzer, true, Lucene.Net.Index.IndexWriter.MaxFieldLength.LIMITED);
  21. _isOpen = true;
  22. }
  23. public IEnumerable<PropertyInfo> GetProps(Type type)
  24. {
  25. if(null == _tempProps)
  26. {
  27. _tempProps = new Dictionary<Type, IEnumerable<PropertyInfo>>();
  28. }
  29. if (!_tempProps.ContainsKey(type))
  30. {
  31. _tempProps.Add(type, type.GetProperties().Where(prop => null != prop.GetCustomAttribute(typeof(IndexerAttribute), true)));
  32. }
  33. return _tempProps[type];
  34. }
  35.  
  36. public void Add<T>(T obj)
  37. {
  38. SetDBPath(typeof(T));
  39. Open();
  40.  
  41. Document document = new Document();
  42. foreach (var prop in GetProps(typeof(T)))
  43. {
  44. var value = prop.GetValue(obj)?.ToString();
  45. if(null != value)
  46. {
  47. var attr = prop.GetCustomAttribute(typeof(IndexerAttribute), true) as IndexerAttribute;
  48. var store = attr.Store ? Field.Store.YES : Field.Store.NO;
  49. var index = attr.Index ? Field.Index.ANALYZED : Field.Index.NOT_ANALYZED;
  50.  
  51. document.Add(new Field(prop.Name, value, store, index));
  52. }
  53. }
  54.  
  55. _indexWriter.AddDocument(document);
  56. }
  57.  
  58. public void AddRange<T>(IEnumerable<T> objs)
  59. {
  60. SetDBPath(typeof(T));
  61. Open();
  62.  
  63. foreach (var obj in objs)
  64. {
  65. Document document = new Document();
  66. foreach (var prop in GetProps(typeof(T)))
  67. {
  68. var value = prop.GetValue(obj)?.ToString();
  69. if (null != value)
  70. {
  71. var attr = prop.GetCustomAttribute<IndexerAttribute>();
  72. var store = attr.Store ? Field.Store.YES : Field.Store.NO;
  73. var index = attr.Index ? Field.Index.ANALYZED : Field.Index.NOT_ANALYZED;
  74.  
  75. document.Add(new Field(prop.Name, value, store, index));
  76. }
  77. }
  78. _indexWriter.AddDocument(document);
  79. }
  80. }
  81. public override void Dispose()
  82. {
  83. _indexWriter.Optimize();
  84. _indexWriter.Dispose();
  85. base.Dispose();
  86. }
  87. }

这个类用于创建索引,会读取用在对象上的Indexer标签

LuceneDBSearcher

  1. public class LuceneDBSearcher: LuceneDB
  2. {
  3. private Type _searchType;
  4. public LuceneDBSearcher(string path) : base(path)
  5. {
  6. }
  7. public LuceneDBSearcher(Type type)
  8. {
  9. SetDBPath(type);
  10. }
  11. public LuceneDBSearcher() { }
  12. public Type SearchType
  13. {
  14. set
  15. {
  16. //判断该类型是否实现 某 约定
  17. _searchType = value;
  18. }
  19. get { return _searchType; }
  20. }
  21. public IEnumerable<T> Search<T>(string searchText, IEnumerable<string> fields, int page, int pageSize, Dictionary<string,string> condition= null) where T : new()
  22. {
  23. return GetModels<T>(SearchText(searchText, fields, page, pageSize, condition));
  24. }
  25.  
  26. private IEnumerable<Document> SearchText(string searchText,IEnumerable<string> fields,int page,int pageSize, Dictionary<string,string> condition)
  27. {
  28. StringBuilder conditionWhere = new StringBuilder();
  29.  
  30. foreach (var item in condition)
  31. {
  32. conditionWhere.Append(" +" + item.Key + ":" + item.Value);
  33. }
  34.  
  35. Open();
  36.  
  37. var parser = new Lucene.Net.QueryParsers.MultiFieldQueryParser(Lucene.Net.Util.Version.LUCENE_30, fields.ToArray(), _analyzer);
  38. var search = new Lucene.Net.Search.IndexSearcher(_luDirectory, true);
  39.  
  40. var query = parser.Parse("+" + searchText + conditionWhere.ToString());
  41.  
  42. var searchDocs = search.Search(query, ).ScoreDocs;
  43.  
  44. return searchDocs.Select(t => search.Doc(t.Doc));
  45. }
  46.  
  47. protected override void Open()
  48. {
  49. _luDirectory = Lucene.Net.Store.FSDirectory.Open(new System.IO.DirectoryInfo(DBPath));
  50. if (Lucene.Net.Index.IndexWriter.IsLocked(_luDirectory))
  51. {
  52. Lucene.Net.Index.IndexWriter.Unlock(_luDirectory);
  53. }
  54. var lockFilePath = System.IO.Path.Combine(DBPath, "write.lock");
  55.  
  56. if (System.IO.File.Exists(lockFilePath))
  57. {
  58. System.IO.File.Delete(lockFilePath);
  59. }
  60.  
  61. _analyzer = new Lucene.Net.Analysis.Standard.StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30);
  62. }
  63. private IEnumerable<T> GetModels<T>(IEnumerable<Document> documents) where T:new()
  64. {
  65. var type = typeof(T);
  66. var props = type.GetProperties().Where(prop => null != prop.GetCustomAttribute(typeof(IndexerAttribute), true));
  67. var objs = new List<T>();
  68. foreach (var document in documents)
  69. {
  70. var obj = new T();
  71. foreach (var prop in props)
  72. {
  73. var attr = prop.GetCustomAttribute<IndexerAttribute>();
  74.  
  75. if (null != attr && attr.Store)
  76. {
  77. object v = Convert.ChangeType(document.Get(prop.Name), prop.PropertyType);
  78. prop.SetValue(obj, v);
  79. }
  80. }
  81. objs.Add(obj);
  82. }
  83. return objs;
  84. }
  85. private T GetModel<T>(Document document) where T : new()
  86. {
  87. var type = typeof(T);
  88. var props = type.GetProperties().Where(prop => null != prop.GetCustomAttribute(typeof(IndexerAttribute), true));
  89.  
  90. var obj = new T();
  91. foreach (var prop in props)
  92. {
  93. var attr = prop.GetCustomAttribute<IndexerAttribute>();
  94.  
  95. if (null != attr && attr.Store)
  96. {
  97. object v = Convert.ChangeType(document.Get(prop.Name), prop.PropertyType);
  98. prop.SetValue(obj, v);
  99. }
  100. }
  101. return obj;
  102. }
  103. public override void Dispose()
  104. {
  105. _analyzer.Dispose();
  106. base.Dispose();
  107. }
  108. }

用于检索Lucene文件夹并将数据转为对象

LuceneEntityBase

  1. public abstract class LuceneEntityBase:ILuceneStored
  2. {
  3. #region private
  4. private Dictionary<string, PropertyInfo> _propertiesCache;
  5. #endregion
  6.  
  7. #region IndexerFields
  8. #region ILuceneStored
  9. [Indexer(false, true)]
  10. public string ID { get; set; }
  11. [Indexer(true, false)]
  12. public string _Customer { get; set; }
  13. [Indexer(true, false)]
  14. public string _Category { get; set; }
  15. #endregion
  16.  
  17. /// <summary>
  18. /// 图片
  19. /// </summary>
  20. [Indexer(false, true)]
  21. public string Picture { get; set; }
  22. /// <summary>
  23. /// 标题
  24. /// </summary>
  25. [Indexer(true, true)]
  26. public string Title { get; set; }
  27. /// <summary>
  28. /// 简介
  29. /// </summary>
  30. [Indexer(true, true)]
  31. public string Synopsis { get; set; }
  32. /// <summary>
  33. /// 链接
  34. /// </summary>
  35. [Indexer(false, true)]
  36. public string Url { get; set; }
  37. #endregion
  38.  
  39. public LuceneEntityBase()
  40. {
  41.  
  42. }
  43.  
  44. protected IEnumerable<T> Search<T>(string searchText, int page, int pageSize, object condition = null) where T:new ()
  45. {
  46. var ConditionDictionary = null != condition ? InitConditionSearchFields(condition) : new Dictionary<string, string>();
  47.  
  48. var fullTextSearchFields = from propName in PropertiesCache.Select(t => t.Key)
  49. where !ConditionDictionary.ContainsKey(propName)
  50. select propName;
  51.  
  52. using (var luceneDB = new LuceneDBSearcher(GetType()))
  53. {
  54. return luceneDB.Search<T>(searchText, fullTextSearchFields, page, pageSize, ConditionDictionary);
  55. }
  56. }
  57. /// <summary>
  58. /// 属性缓存
  59. /// </summary>
  60. protected Dictionary<string, PropertyInfo> PropertiesCache
  61. {
  62. get
  63. {
  64. if(null == _propertiesCache)
  65. {
  66. _propertiesCache = new Dictionary<string, PropertyInfo>();
  67. foreach (var prop in GetType().GetProperties())
  68. {
  69. var attr = prop.GetCustomAttribute<IndexerAttribute>(true);
  70.  
  71. if (null != attr && attr.Index)
  72. {
  73. _propertiesCache.Add(prop.Name, prop);
  74. }
  75. }
  76. }
  77. return _propertiesCache;
  78. }
  79. }
  80.  
  81. /// <summary>
  82. /// 初始化 且 条件
  83. /// </summary>
  84. protected virtual Dictionary<string, string> InitConditionSearchFields(object andCondition)
  85. {
  86. var _conditionDictionary = new Dictionary<string, string>();
  87.  
  88. var type = GetType();
  89. var andConditionType = andCondition.GetType();
  90. var conditions = type.GetInterfaces().Where(t => typeof(ICondition).IsAssignableFrom(t) && t!= typeof(ICondition))
  91. .SelectMany(t => t.GetProperties() /*t.GetInterfaceMap(t).InterfaceMethods*/)
  92. .Select(t => t.Name);
  93. foreach (var condition in conditions)
  94. {
  95. if (!_conditionDictionary.ContainsKey(condition))
  96. {
  97. _conditionDictionary.Add(condition, andConditionType.GetProperty(condition).GetValue(andCondition)?.ToString() ?? string.Empty);
  98. }
  99. }
  100.  
  101. return _conditionDictionary;
  102. }
  103. }

需要索引的抽象基类

一般而言搜索结果有一个标题,介绍,图片等等,介绍和标题是数据库(这里是db)中实际存储的,然而对于搜索的关键字确实没有的,比如(在博客园中)有一个博客表,表里面存博客标题,博客主要内容,还有一个问道表,用户提的问题,介绍,等等,此时如果用户来到博客园中,键入博客(可能已进入首页都是博客,但是用户还是键入了博客),然而db中却不知道那个是博客??仔细想是不是这样子的,这里_Customer就是设定这些数据的,当然也可以是_Category。

结下来介绍一个比较重要的接口。

ICondition

  1. public interface ICondition { }

作为一个空接口,如果不能找到一个它不应该存在的理由,那它就真的不应该存在了。在上面说的LuceneEntityBase中,所有的字段都是全文搜索的,即任何一个字段数据匹配用户键入的值都可能称谓匹配的文档,这是假如需要匹配另一个条件该如何?写代码之初只考虑到一个语言条件,再添加一个参数,具体改一下搜索的方法就可以了,这样一样知识需要改三处,搜索入口(控制器),给该模型添加一个语言字段,具体的搜索方法也要改一下,甚至写索引的方法也都要改。然而有了这个接口,我们只需要实现一个ILanguage继承该接口,然后事具体的模型也继承ILanguage就行。

  1. public interface ILanguage: ICondition
  2. {
  3. string Language { get; set; }
  4. }

在看一下上面LuceneEntityBase中创建且条件的方法,会解析出ICondition中的属性并设置为必须条件

  1. var _conditionDictionary = new Dictionary<string, string>();
  2.  
  3. var type = GetType();
  4. var andConditionType = andCondition.GetType();
  5. var conditions = type.GetInterfaces().Where(t => typeof(ICondition).IsAssignableFrom(t) && t!= typeof(ICondition))
  6. .SelectMany(t => t.GetProperties() /*t.GetInterfaceMap(t).InterfaceMethods*/)
  7. .Select(t => t.Name);
  8. foreach (var condition in conditions)
  9. {
  10. if (!_conditionDictionary.ContainsKey(condition))
  11. {
  12. _conditionDictionary.Add(condition, andConditionType.GetProperty(condition).GetValue(andCondition)?.ToString() ?? string.Empty);
  13. }
  14. }

实例之三国人物

用上面封装后的类建立了一个简易的搜索示例

核心代码只有以下三部分,数据的话都爬虫字自百度百科

  1. public class Figure : Lyrewing.Search.LuceneEntityBase, ICountry
  2. {
  3. [Indexer(true,true)]
  4. public string Country { get; set; }
  5. [Indexer(true,true)]
  6. public string FigureName { get; set; }
  7. /// <summary>
  8. /// 称谓
  9. /// </summary>
  10. [Indexer(true,true)]
  11. public string Appellation { get; set; }
  12. /// <summary>
  13. /// 关键字
  14. /// </summary>
  15. [Indexer(true,true)]
  16. public string KeyWords { get; set; }
  17. public IEnumerable<Figure> Search(string searchText, int page, int pageSize, object condition = null)
  18. {
  19. return Search<Figure>(searchText, page, pageSize, condition);
  20. }
  21. }
  22. public interface ICountry : ICondition
  23. {
  24. string Country { get; set; }
  25. }
  1. using (var luceneDB = new LuceneDBIndexer())
  2. {
  3. luceneDB.AddRange(Figures);
  4. }
  1. var seacher = new Figure();
  2.  
  3. var result = seacher.Search(key, , , country != Country.默认 ? new { Country = country.ToString() } : null);

小结:

通过这次从lucene的踩坑,到lucene的重构,以及代码的一些思考,发现过程是艰辛的,代码现在也不是完美的,lucene的一些其他复杂的查询也没有加进去,但是从这个过程来说对自己来说是值得的,依稀记得实习的时候以为老师讲过的一句话,貌似高三语文老师也讲过哈,信达雅,但是这个过程是不易的,还需要更多的学习和挑战!

Lucene的使用与重构的更多相关文章

  1. lucene学习-3 - 代码重构

    内容就是标题了.是要重构下上一节的代码,大体上按如下的思路: 功能拆分: 创建必要的工具类: 两个工具类StringUtils和TxtUtils. StringUtils,主要是获取当前系统的换行符: ...

  2. Lucene核心--构建Lucene搜索(上篇,理论篇)

    2.1构建Lucene搜索 2.1.1 Lucene内容模型 一个文档(document)就是Lucene建立索引和搜索的原子单元,它由一个或者多个字段(field)组成,字段才是Lucene的真实内 ...

  3. Lucene 源码分析之倒排索引(二)

    本文以及后面几篇文章将讲解如何定位 Lucene 中的倒排索引.内容很多,唯有静下心才能跟着思路遨游. 我们可以思考一下,哪个步骤与倒排索引有关,很容易想到检索文档一定是要查询倒排列表的,那么就从此处 ...

  4. lucene学习教程

    1Lucene的介绍 ①Lucene是什么: 是一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎 ②Lu ...

  5. Lucene 4.0 正式版发布,亮点特性中文解读[转]

    http://blog.csdn.net/accesine960/article/details/8066877 2012年10月12日,Lucene 4.0正式发布了(点击这里下载最新版),这个版本 ...

  6. lucene实现初级搜索引擎

    一.系统设计 搜索引擎项目代码主要分为三个部分,第一部分是构建索引,全文检索:第二部分是输入问题,对问题进行分词.提取关键词.关键词扩展:第三部分是将搜索结果输出到GUI图形用户界面. 二.搜索引擎 ...

  7. 万亿级日志与行为数据存储查询技术剖析(续)——Tindex是改造的lucene和druid

    五.Tindex 数果智能根据开源的方案自研了一套数据存储的解决方案,该方案的索引层通过改造Lucene实现,数据查询和索引写入框架通过扩展Druid实现.既保证了数据的实时性和指标自由定义的问题,又 ...

  8. lucene LZ4 会将doc存储在一个chunk里进行Lz4压缩 ES的_source便如此

    默认情况下,Elasticsearch 用 JSON 字符串来表示文档主体保存在 _source 字段中.像其他保存的字段一样,_source 字段也会在写入硬盘前压缩.The _source is ...

  9. Lucene 查询原理 传统二级索引方案 倒排链合并 倒排索引 跳表 位图

    提问: 1.倒排索引与传统数据库的索引相比优势? 2.在lucene中如果想做范围查找,根据上面的FST模型可以看出来,需要遍历FST找到包含这个range的一个点然后进入对应的倒排链,然后进行求并集 ...

随机推荐

  1. 用border或者div制作三角形等图形

    一般情况下, 我们设置盒子的宽高度, 及上下左右边框, 具体代码如下: 通过上述代码,div的具体样式如下: 现在在上面基础上, 我们把div的宽高度都设为0时, 现在我们再次查看效果,如下图: 这时 ...

  2. 关于STM32空闲中断

    有一次做一个东西,为了尽量不占用CPU的处理数据时间,所以就使用DMA接收串口的数据,但是呢问题来了.,,,,,怎么样才能确定接收到了一条完整的数据了,,我们都知道只要打开DMA 那家伙就不停的把接收 ...

  3. 今天重装系统后,Wdows更新提示“windows update当前无法检查更新,因为未运行服务。您可能需要重新启动计算机”

    到百度搜了常用的解决方法,就是用命令提示符,但对我的情况不管用,提示“拒绝访问”.后来在08绿软站的一篇文章中找到了解决办法.原文如下(我本人也是用的第四种方法解决的): 试了下面几种解决方法,第四种 ...

  4. 可视化之AQICN

    上一篇和大家分享了<可视化之Berkeley Earth>,这次看一看下面这个网站---aqicn.org.先做一个提示:文末有惊喜~ 该网站在中国有一定的权威性,PM2.5数据有一点敏感 ...

  5. 主存与Cache的地址映射

    最近在复习计算机体系结构,选用的教材是名闻遐迩的<计算机体系结构 量化研究方法 第五版>(Computer Architecture A Quantitative Approach), 关 ...

  6. 《如莲春天》Java开发框架

    关于 如莲者,净洁如莲之意,希望打造一个简洁的系统框架.系统主要采用Spring相关技术,故取名:如莲春天.    如莲春天,包括一套系统界面.一个权限管理系统.一个CURD代码生成模块.一些基础模块 ...

  7. 关于AD9516芯片的硬件设计和FPGA程序编写心得

    最近在做一个项目,其中有涉及时钟芯片AD9516的硬件设计和软件编程,有些使用心得,供大家参考讨论. AD9516,这是一个由ADI公司设计的14路输出时钟发生器,具有亚皮秒级抖动性能,还配有片内集成 ...

  8. html加javascript和canvas类似超级玛丽游戏

    html加javascript和canvas制作 代码来源于网上 复制可用 <!doctype html><html lang="en"> <head ...

  9. jsp中怎么调用java类中的方法

    在jsp页面中先要,引入java类 例如: <%@page import="javabean.DbConn"%><!-- 引入包中的"类" - ...

  10. Windows定时关机

    用shutdown命令.开始菜单>运行,输入shutdown -s -t 7200 (两个小时之后关机)at 12:00 shutdown -s (12:00关机) 其他设置:shutdown ...