[转载]firebug1.7调试实用技巧指南

mikel阅读(970)

.

武器档案

  • 名称:firebug
  • 最新版本:1.7
  • 用途:前端调试器
  • 必备指数:
  • 使用难度:

firebug是前端最具盛名的调试器,功能非常强悍。firebug1.7正式版现在已经发布了,完美兼容firefox4.0(无法兼容 firefox4.0以下的版本),由于firebug非常易用,这里明河不一一介绍firebug的功能,只给大家介绍一些firebug的实用技巧, 有些技巧是firebug1.7才有的,有些是通用的。

1.使用“Debugger”关键字快速断点调试

正常如果我们想Debugger下js代码,先进入“脚本”面板,然后找到要调试的js文件,最后在你需要调试的行,打上断点,最后刷新页面,如下图:

有没有更快速的断点方式呢?那就是使用“debugger”关键字快速断点调试,接着往下看。
在你要调试的代码行前面,加一行”debugger;”,比如下面的

  1. debugger;
  2. if(steps.length == 0) returnfalse;
  3. self.isRender = true;

然后刷新下页面,如下图:

断到点了!是不是很方便,你无须再切换到脚本,再花心思找对应的调试脚本,这里我们只需要加个debugger;即可,放心,脚本不会报错,当然你正式发布脚本的时候,务必把debugger;关键字去掉哦。

2.快速定位指定行的代码

比如我要快速定位到第122行的代码,你可以如下操作。
在搜索框上输入“#122”,就可以找到指定行的代码了!

3.只显示起作用的css属性

firebug1.7是默认开启的,如下图:
当勾选第一项“Only Show Applied Style”时,只显示起作用的样式,如果我们把这项去掉,看下效果。

被覆盖,不起作用的css属性就会出现了。

4.如何查看hover的css属性

加了hover属性的容器,鼠标滑过,在HTML面板右侧会出现hover属性,但你移开就没有了,如果你希望在面板中保留hover属性,看下面:

勾选最后一项hover即可。

5.如何理解脚本面板中的“堆栈”

堆栈,可以理解为执行顺序总揽,“监控”起到微观作用,让我们知道变量的赋值情况,而监控起到宏观作用,让我们知道,脚本的运行流程。
是不是用的很少?其实堆栈信息已经第一时间出现在面板了,只是很少人留意,截个图给大家瞧瞧:

高亮部分就是堆栈信息!

6.循环体内的特例调试

调试循环体,有个很杯具的地方,在循环内部打个断点,循环20次,会触发20次断点……这很愁人,影响调试效率,其实我们只需要调试循环第2次的情况。

右击断点处,会出现一个蓝色的弹出层,让你输入监控表达式,这时候输入i == 2,再按“F8”,你就会发现直接跳到第二次循环了。
(图截不下来,无法奉上图片见谅。)

7.以独立窗口的形式打开firebug

1.7firebug的面板可以变成独立窗口了,快捷键:“ctrl+F12”,也可以点击如下图的位置。

8.向控制台输出日记消息

在你的代码上输出个“36ria.com”,如下:

  1. console.log(36ria.com);
  2. console.error(36ria.com);

使用console.log的效率,个人觉得还是比原始的alert()来的高的,关键是console.log()不会中断程序执行。

9.利用命名行工具,快速定位对象


step1这个对象,是KISSY.Step()的实例,明河想要知道这个对象内部的赋值情况,只要在命令行上键入”step1″,就会在控制台上出现相关信息,然后我再点击该信息,就进入了DOM详细信息界面。

明河结语

先写到这里,firebug还有其他技巧,日后补上,欢迎前端朋友补充,感激不尽。

[转载]jQuery选择器的性能测试

mikel阅读(1135)

[转载]到处都是jQuery选择器的年代,不了解它们的性能,行吗? – 爱莲学堂 – 博客园.

如今JQuery网站上普及的程度越来越高,所以,对于JQuery的使用,即使再小的一点认识、一点优势、一点不足,也显得越来越值得研究和探讨。最 近,我就对jQuery的选择器使用做了一些个小小的实验,用来说明jQuery的不同选择器在不同的情况下,哪个效率更高,更值得使用。

先在每个测试页面的head中引入google提供的jquery文件和用于测试的小插件firejspt。


<!-- 引入FireJSPT的库文件 -->
<script type="text/javascript" src="firejspt.js"></script><!-- 引入google提供的1.44版的jQuery的库文件,其实哪个版本都无所谓了,呵呵 --><script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>

1. 最常用的id选择器和class选择器
将以下代码复制200次,置于body标签内。

<div id="ilian">比较id选择器和class选择器</div>
<div class="ilian">比较id选择器和class选择器</div>

用于本次测试的JS代码如下:

function ilianTest01(){
$('#ilian').click(function() { alert('Hello World'); });
}

function ilianTest02(){
$('.ilian').click(function() { alert('Hello World'); });
}

/*调用2个函数进行测试*/
$(function(){
jspt.test(function(){ ilianTest01(); });
jspt.test(function(){ ilianTest02(); });
});

测试结果如下:


由图可以id选择器相比于class选择器的效率优势是非常地。。。。。
附本次测试地址:http://www.threesnow.com/code/090/ilian_01.html
2. 在选择标签时,层级选择器用得也非常频繁,这次对比测试直接子标签符号“>”和children。

将以下代码放入body标签内,并将其中的li标签复制500次。

<ul>
<ul>
	<li>比较直接子标签符号“&gt;”和children</li>
	<li>比较直接子标签符号“&gt;”和children</li>
	<li>比较直接子标签符号“&gt;”和children</li>
</ul>
</ul>
&nbsp;

用于本次测试的JS代码如下:

function ilianTest01(){
$('#ilian &gt; li').click(function() { alert('Hello World'); });
}

function ilianTest02(){
$('#ilian').children('li').click(function() { alert('Hello World'); });
}

/*调用2个函数进行测试*/
$(function(){
jspt.test(function(){ ilianTest01(); });
jspt.test(function(){ ilianTest02(); });
});

测试结果:


由此可见children选择器要优于直接子标签符号选择器。
附本次测试地址:http://www.threesnow.com/code/090/ilian_02.html

限于文章长度,本文只展示了最基本的测试,且以上测试,均是在简单环境中测试的,测试效果并不代表绝对的结论。

如果觉得文章有用,别忘了推荐撒~~

[转载]Flash开发Android应用

mikel阅读(1913)

[转载]Flash开发Android应用 – 天威茫然 – 博客园.

1.

 

开发工具:

 

下载Adobe Flash Professional CS5.5:http://www.xiazaiba.com/html/4577.html

下载Adobe AIR 3.3 SDK:http://www.adobe.com/devnet/air/air-sdk-download.html

 

2.

也许是Adobe Flash Professional CS5.5这个是绿化版的,或者本身Adobe Flash Professional CS5.5安装完就没有Adobe AIR 3.3 SDK。

因此我们需要安装Adobe AIR 3.3 SDK。所以下载完Adobe AIR 3.3 SDK后解压到E:\Program Files\Adobe\Flash_CS5.5_XiaZaiBa\Adobe Flash Professional CS5.5\Adobe Flash CS5.5这个地方。并将解压后的文件夹名称修改为AIR2.6(这个为了和Adobe Flash Professional CS5.5搭配)。

3.

运行Adobe Flash Professional CS5.5,选择AIR for Android

 

 

然后做一个简单的补间动画:

 

4:

配置AIR For Android.

这里注意一下图标问题。一定是大小和配置说明的一样。不然总是会有个错误。。。。

 

 

5

发布:

一切都OK的话就是这样了。

 

 例子源码:http://files.cnblogs.com/hzl512/Falsh.rar

[转载]图像识别练习(字符验证码、车牌号、身份证号)

mikel阅读(1321)

[转载]图像识别练习(字符验证码、车牌号、身份证号) – 野比 – 博客园.

欢迎大家和我讨论相关问题。联系方式:1429013154

(注意此版并非最终版)

光学图像识别(OCR)是非常有用的技术。在验证码识别、车牌号识别、文字识别方面,基于字符的识别技术算是比较容易上手的了(相比图文识别)。

闲来看到有朋友研究验证码识别,一时手痒,野比自己动手来做做验证码识别。当然,肯定只是简单的验证码。

名为验证码,实际上并不限于,还可以识别车牌号、身份证号、门牌号等各种乱七八糟的内容。

识别的流程很明确:

1、预处理图像

2、做y轴的投影

3、分析直方图分区

4、根据分区拆分图像为多个字符(很关键,拆得越好,后续识别率越高)

5、丢弃空白或无效字符

6、自动旋转字符(如果有倾斜),识别字符

如果样本中个图像有粘连,则可能造成分区不准确。这种情况下,需要进行旋转,但是怎样自动旋转,是个难题。

目前已可拆分出字符,下一步准备研究如何识别。(如果单个字符比较规范,可以利用现成的OCR控件)

这里有一些例子。

普通的验证码(毫无难度)

带干扰的验证码

较高强度干扰(目前使用的分区算法不能解决,需要更好的算法,比如动态阈值)

CSDN的验证码(毫无压力)

身份证号码

车牌号

补充个QQ验证码,用单一阈值方法,识别很困难,需要结合字符宽度进行判断

这是单一阈值分区的结果(没有限制宽度),可以看到效果很差。

继续研究如何优化分区算法,如何识别单个文字(可以考虑多重识别+样本训练)。

 

附上太平洋网站验证码。

有些粘连,但是可以通过固定字符宽度解决(宽度基本一致)

参考这张图(获得整个宽度,然后除以字符数得到每个宽度,分别提取)

二值化我用的Otsu算法,参考文献:”A threshold selection method from gray-level histograms”, IEEE Trans. Systems, Man and Cybernetics 9(1), pp. 62–66, 1979

关于验证码,这篇论文很不错,建议参考:”Text-based CAPTCHA Strengths and Weaknesses”, ACM Computer and Communication security 2011 (CSS’2011)

© 野比 2012

[转载]cocos2d-x安卓开发环境搭建(windows+eclipse+ndk 不用cygwin)

mikel阅读(1011)

[转载]让人死去活来的cocos2d-x安卓开发环境搭建(windows+eclipse+ndk 不用cygwin) – 夜阑卧听风吹雨 – 博客园.

安卓环境搭建
【一 写在前面】
环境搭建的顺序是这样
1.JDK&JRE         安装JDK会顺带安装JRE的,这一步是为了可以编写和允许普通的JAVA程序
2.Eclipse            JAVA 的 IDE,这一步是为了方便地开发JAVA程序
3.CDT                是Eclipse的一个插件,这一步是为了可以引入C++
4.ADT                是Eclipse的一个插件,这一步是为了管理安卓开发库
5.ADK                上一步是工具,这一步就是开发库了,到了这一步你可以编写普通的安卓程序
6.NDK                单有CDT还不行,它只能编辑,不能编译,这一步是为了可以编译C++程序,然后在安卓中使用
这里不需要使用cygwin这个麻烦的东西,我们使用新版本的ndk来编译C++
【二 JDK的安装】
JDK的最新版本是1.7,【重要】这里需要下载使用 1.6的JDK,JDK自动包含JRE,使用1.7的JDKcocos的代码会有”暂时性编译错误”问题
下载安装都是一路Next,下面的内容是安装1.7之后降1.6的方法,直接安装1.6的可以飘过
下载地址:http://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-javase6-419409.html
根据自己的操作系统选择,这里我用的是Java SE Development Kit 6u30
如果你像我一样不幸,装上了1.7,可以用下面的方法来恢复降到1.6
1.打开windows的Preferences,把Compiler compliance level 设置到1.6

然后会显示让你配置JRE

在配置jre中添加jre的目录,然后选定
这里,我们在Eclipse里面设置好了JRE6,以及编译使用的JDK,但还不可以
接下来要设置一下你的项目,打开Project Properties,Add Library 然后Next,接下来会要你选择JRE的路径
这里我们直接选择刚刚在windows Preferences设置好的JRE6,然后Finish即可
此时你会发现更多的红叉叉,所有文件都出现红叉叉了,让我们把他们消除掉,这次很简单,刚刚重置JRE的时候,吧安卓也重置掉了
把安卓ADK勾上就可以了
【三 安装Eclipse和CDT】
这里我直接用这一个,带CDT插件,已经有Eclipse的同学可以去下载一个CDT
Eclipse解压就可以直接使用了,这里不墨迹
Eclipse C++版本下载地址 http://www.eclipse.org/downloads/packages/eclipse-ide-cc-developers-includes-incubating-components/indigosr2
【四 安装ADT】
两种安装方法,一种在线安装,一种下载安装,网速问题,我倾向于下载安装。
注意,请下载1.7及以上版本的ADT,这里使用1.8的版本
在Help里面,选择Install New Software…
然后选择Add,如果是在线安装,在Location里面填https://dl-ssl.google.com/Android/eclipse/
(不行的话,把https改为http试试)
如果是下载安装,先把ADT下载到本地,然后按Archive选择ADT的路径,点 ok,勾上Developer Tools开始安装
ADT 1.8的地址:http://dl.google.com/Android/ADT-18.0.0.zip
PS。这个安装需要一段时间…..不要以为它死机了,挂了,如果你的机器或者网络不给力,让你感觉到不耐烦
请不要强制关闭它….否则,你可能要看接下来的错误排除
安装完毕之后重启Eclipse,然后会提示你安装ADK
如果你非常不幸安装了1.6以及以前版本的ADT,或者强制中断了它的安装….那么你需要把它卸掉
它将无法创建安卓项目…,而卸载,也是一个麻烦的事情
首先,需要把这个插件删除,在Help的About找到对应的ADT,然后删除
接下来从软件更新列表中,删除我们的ADT
【五 安装ADK】
安装完ADT之后,重启Eclipse会提示你安装ADK,你可以指定路径,或者下载安装
可以在这个地址下载ADK http://developer.android.com/sdk/index.html
如果是下载的话,建议下载2.1,他的兼容性比较好,本地的话,指定ADK解压后的路径即可
【六 安装NDK】
到这里为止,我们已经可以使用Eclipse来编写安卓程序了,但我们的目的是用C++编写cocos,要达到这个目的,还需要最后一步——NDK
使用NDK有两个目的,第一个是将我们的C++代码编译成链接库,这里的链接库是Linux下的so或者a文件,不同于windows的dll
第二个目的是Jni,Java需要通过Jni来调用我们的C++代码
下载地址为:http://dl.google.com/android/ndk/android-ndk-r7-windows.zip
下载完毕之后解压,这里给出最简单的步骤,在编译的过程中会碰到各种问题,这里也将一一解答
首先我们要创建一个Android项目
使用最新版本的Cocos2d-x,我们用他的HelloWorld来做试验
项目打开之后,,,,这里发现一个错误,没有setEGLContextClientVersion这个方法,这里,暂时把它注释掉吧
看意思应该是设置OpenGL ES的版本….反正默认的应该可以
然后看到控制台输出一句话
这里在Application.mk加上APP_PLATFORM :=android-4就可以了
接下来我们要把NDK加进来,让NDK来编译我们的C++
指定ndk-build.cmd来编译我们的项目,下面的工作路径将作为参数传入给ndk-build.cmd,
我们选择项目的路径,它会自动编译jni子目录下的Android.mk
这时候如果是cocos2d-1.0.1-x-0.13.0-beta以及之前的版本,是可以进行编译的,但如果是之后的版本,有可能出现如下错误
Android.mk引用了一个模块,cocos2dx模块,而NDK没有找到它,所以报了这个错,
之前的版本是直接包含cocos2dx模块的Android.mk的,所以不会报错
而这里使用了import,我们也可以使用之前版本的方法来包含它,但貌似import比之前的方法要更好一些
错误信息提示我们,可以通过设置NDK_MODULE_PATH环境变量,这里很有可能设置错误
一般,一个Android.mk包含一个或多个模块,当你要import cocos2dx模块时,
他会在这个路径下% NDK_MODULE_PATH\cocos2dx寻找Android.mk文件
所以我们编辑我们的Builder
添加NDK_MODULE_PATH变量,注意要写两个路径,用分号分开
D:\SDK\cocos2d-2.0-rc0a-x-2.0;D:\SDK\cocos2d-2.0-rc0a-x-2.0\cocos2dx\platform\third_party\android\prebuilt
因为cocos2dx还import了其他几个module,第二个路径制定了第三方的module,设置完就可以进行编译了
我用的是最新的2.0,有些头文件没找到,一些类型也有问题….密密麻麻地近百个错误。。。
我们试着用低一点的版本吧
接下来又出现了一个找不到静态库的问题
把<NDK>/sources/cxx-stl/gnu-libstdc++/libs/armeabi/目录下的libgnustl_static.a
拷贝到obj/local/armeabi/libgnustl_static.a目录下,解决
最后生成的apk放在bin目录下
PS.资源文件需要放在assets下,ADT会将它打包到apk中

[转载]orchard之lucene.net索引生成

mikel阅读(1584)

[转载]orchard之lucene.net索引生成 – 叶鹏 – 博客园.

orchard是微软自己团队搞的cms,园子里有很多园友已经对系统结构详细分析了,但是对里面的某些模块没有一一分析,因为需要使用 lucene.net做站内搜索,所以参考学习一下,ps一下lucene.net已经是2.9.4版本了,虽然还在孵化器,但是还是更新了,不容易啊。

点开Modules开启lucene相关应用,如下图。

先自己在后台发几篇文章,其实在发文章的同时,orchard的消息监听机制就已经自动从消息队列中取出消息然后自动生成索引了。这里分析一下索引过程。

点开Settings里的Search Index菜单

现在索引里已经包含4条文档了,点击Update的时候会重新生成索引,流程如下。

 

在Modules里的Orchard.Indexing里的Controllers下的AdminController

[HttpPost]
        public ActionResult Update() {
            if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not allowed to manage the search index.")))
                return new HttpUnauthorizedResult();
          //更新索引,DefaultIndexName为索引文件夹名称
            _indexingService.UpdateIndex(DefaultIndexName);

            return RedirectToAction("Index");
        }

Orchard.Indexing.Services.IndexingService并不少直接生成索引,而是从消息通知里获取通知后才生成索引的,如下。

public void UpdateIndex(string indexName) {
            //获取消息通知里索引生成通知才生成索引
            foreach(var handler in _indexNotifierHandlers) {
                handler.UpdateIndex(indexName);
            }
            //生成后将信息通过消息通知传给前台显示
            Services.Notifier.Information(T("The search index has been updated."));
        }

将索引生成的消息通知给索引生成程序后还是不能生成索引,而是将这个消息传给生成索引的计划任务程序Orchard.Indexing.Services.UpdateIndexScheduler,在这里继续生成索引之旅。

//将生成索引这件事添加到计划任务
public void Schedule(string indexName) {
            var shellDescriptor = _shellDescriptorManager.GetShellDescriptor();
            _processingEngine.AddTask(
                _shellSettings,
                shellDescriptor,
                "IIndexNotifierHandler.UpdateIndex",
                new Dictionary<string, object> { { "indexName", indexName } }
            );
        }

        public void UpdateIndex(string indexName) {
            if(_indexingTaskExecutor.Value.UpdateIndexBatch(indexName)) {           //将生成索引的任务交给它去处理,上面的方法
                Schedule(indexName);
            }
        }

添加到计划任务后,他们之间的传递关系就只能通过读取消息队列来继续了。核心在这里。

Orchard.Indexing.Services.IndexingTaskExecutor,真正处理索引任务的类,这个类会加载到内存,通过心跳方式读取消息队列,如果有新的生成索引任务就执行如下代码。

View Code 

        /// <summary>
        /// Indexes a batch of content items
        /// </summary>
        /// <returns>
        /// <c>true</c> if there are more items to process; otherwise, <c>false</c>.
        /// </returns>
        private bool BatchIndex(string indexName, string settingsFilename, IndexSettings indexSettings) {
            var addToIndex = new List<IDocumentIndex>();
            var deleteFromIndex = new List<int>();

            // Rebuilding the index ?
            if (indexSettings.Mode == IndexingMode.Rebuild) {
                Logger.Information("Rebuilding index");
                _indexingStatus = IndexingStatus.Rebuilding;

                // load all content items
                var contentItems = _contentRepository
                    .Fetch(
                        versionRecord => versionRecord.Published && versionRecord.Id > indexSettings.LastContentId,
                        order => order.Asc(versionRecord => versionRecord.Id))
                    .Take(ContentItemsPerLoop)
                    .Select(versionRecord => _contentManager.Get(versionRecord.ContentItemRecord.Id, VersionOptions.VersionRecord(versionRecord.Id)))
                    .Distinct()
                    .ToList();

                // if no more elements to index, switch to update mode
                if (contentItems.Count == 0) {
                    indexSettings.Mode = IndexingMode.Update;
                }

                foreach (var item in contentItems) {
                    try {
                        IDocumentIndex documentIndex = ExtractDocumentIndex(item);

                        if (documentIndex != null && documentIndex.IsDirty) {
                            addToIndex.Add(documentIndex);
                        }

                        indexSettings.LastContentId = item.VersionRecord.Id;
                    }
                    catch (Exception ex) {
                        Logger.Warning(ex, "Unable to index content item #{0} during rebuild", item.Id);
                    }
                }
            }

            if (indexSettings.Mode == IndexingMode.Update) {
                Logger.Information("Updating index");
                _indexingStatus = IndexingStatus.Updating;

                var indexingTasks = _taskRepository
                    .Fetch(x => x.Id > indexSettings.LastIndexedId)
                    .OrderBy(x => x.Id)
                    .Take(ContentItemsPerLoop)
                    .GroupBy(x => x.ContentItemRecord.Id)
                    .Select(group => new {TaskId = group.Max(task => task.Id), Delete = group.Last().Action == IndexingTaskRecord.Delete, Id = group.Key, ContentItem = _contentManager.Get(group.Key, VersionOptions.Published)})
                    .OrderBy(x => x.TaskId)
                    .ToArray();

                foreach (var item in indexingTasks) {
                    try {
                        // item.ContentItem can be null if the content item has been deleted
                        IDocumentIndex documentIndex = ExtractDocumentIndex(item.ContentItem);

                        if (documentIndex == null || item.Delete) {
                            deleteFromIndex.Add(item.Id);
                        }
                        else if (documentIndex.IsDirty) {
                            addToIndex.Add(documentIndex);
                        }

                        indexSettings.LastIndexedId = item.TaskId;
                    }
                    catch (Exception ex) {
                        Logger.Warning(ex, "Unable to index content item #{0} during update", item.Id);
                    }
                }
            }

            // save current state of the index
            indexSettings.LastIndexedUtc = _clock.UtcNow;
            _appDataFolder.CreateFile(settingsFilename, indexSettings.ToXml());

            if (deleteFromIndex.Count == 0 && addToIndex.Count == 0) {
                // nothing more to do
                _indexingStatus = IndexingStatus.Idle;
                return false;
            }

            // save new and updated documents to the index
            try {
                if (addToIndex.Count > 0) {
                    _indexProvider.Store(indexName, addToIndex);
                    Logger.Information("Added content items to index: {0}", addToIndex.Count);
                }
            }
            catch (Exception ex) {
                Logger.Warning(ex, "An error occured while adding a document to the index");
            }

            // removing documents from the index
            try {
                if (deleteFromIndex.Count > 0) {
                    _indexProvider.Delete(indexName, deleteFromIndex);
                    Logger.Information("Added content items to index: {0}", addToIndex.Count);
                }
            }
            catch (Exception ex) {
                Logger.Warning(ex, "An error occured while removing a document from the index");
            }

            return true;
        }

其中重要的一点是从Task中取出索引任务然后添加到lucene文档

var indexingTasks = _taskRepository
                    .Fetch(x => x.Id > indexSettings.LastIndexedId)
                    .OrderBy(x => x.Id)
                    .Take(ContentItemsPerLoop)
                    .GroupBy(x => x.ContentItemRecord.Id)
                    .Select(group => new {TaskId = group.Max(task => task.Id), Delete = group.Last().Action == IndexingTaskRecord.Delete, Id = group.Key, ContentItem = _contentManager.Get(group.Key, VersionOptions.Published)})
                    .OrderBy(x => x.TaskId)
                    .ToArray();

                foreach (var item in indexingTasks) {
                    try {
                        // item.ContentItem can be null if the content item has been deleted
                        IDocumentIndex documentIndex = ExtractDocumentIndex(item.ContentItem);

                        if (documentIndex == null || item.Delete) {
                            deleteFromIndex.Add(item.Id);
                        }

                        else if (documentIndex.IsDirty) {
                            addToIndex.Add(documentIndex);
                        }

                        indexSettings.LastIndexedId = item.TaskId;
                    }
                    catch (Exception ex) {
                        Logger.Warning(ex, "Unable to index content item #{0} during update", item.Id);
                    }
                }

处理完文档过后存储文档到索引的代码如下:

// save new and updated documents to the index
            try {
                if (addToIndex.Count > 0) {
                 //将文档存储到索引
                    _indexProvider.Store(indexName, addToIndex);
                    Logger.Information("Added content items to index: {0}", addToIndex.Count);
                }
            }
            catch (Exception ex) {
                Logger.Warning(ex, "An error occured while adding a document to the index");
            }

最终的索引存储处理在Lucene.Services.LuceneIndexProvider

public void Store(string indexName, IEnumerable<LuceneDocumentIndex> indexDocuments) {
            if (indexDocuments.AsQueryable().Count() == 0) {
                return;
            }

            // Remove any previous document for these content items
            Delete(indexName, indexDocuments.Select(i => i.ContentItemId));

            var writer = new IndexWriter(GetDirectory(indexName), _analyzer, false, IndexWriter.MaxFieldLength.UNLIMITED);
            LuceneDocumentIndex current = null;

            try {

                foreach (var indexDocument in indexDocuments) {
                    current = indexDocument;
                     //将自定义的indexDocument处理成lucene的文档
                    var doc = CreateDocument(indexDocument);

                    writer.AddDocument(doc);
                    Logger.Debug("Document [{0}] indexed", indexDocument.ContentItemId);
                }
            }
            catch (Exception ex) {
                Logger.Error(ex, "An unexpected error occured while add the document [{0}] from the index [{1}].", current.ContentItemId, indexName);
            }
            finally {
                writer.Optimize();
                writer.Close();
            }
        }

至此lucene的索引算是创建完毕,但是中间的一系列消息和任务之间的传递细节还需要进一步深入学习,错误之处希望园友们能够给予指正。

[转载]肥兔读书笔记之Effective C#(第2版) 第一章

mikel阅读(1270)

[转载]肥兔读书笔记之Effective C#(第2版) 第一章 – 懒惰的肥兔 – 博客园.

Effective C#(第2版)中文名称为: C#高效编程 改进C#代码的50个行之有效的办法(第2版)

这本书的中文名字起的很蛋疼,其它Effective系列的书名都是Effective  XXX,在网上商城输入Effective就能全找到,唯独这本死活找不到,后来偶然机会才知到原来中文名称叫做C#高效编程 改进C#代码的50个行之有效的办法,真是蛋疼至极。

第一章 C#语言习惯

条目1 使用属性而不是可访问的数据成员
条目2 用运行时常量(readonly)而不是编译期常量(const)
条目3 推荐使用 is 或 as 操作符而不是强制类型转换
条目4 使用 Conditional 特性而不是#if 条件编译
条目5 为类型提供 ToString() 方法
条目6 理解几个等同性判断之间的关系
条目7 理解 GetHashCode() 的陷阱
条目8 推荐使用查询语法而不是循环
条目9 避免在API中使用转换操作符
条目10 使用可选参数减少方法重载的数量
条目11 理解短小方法的优势
小结

条目1 使用属性而不是可访问的数据成员

关于这个概念是程序员都知道,知道的原因更多是因为编程用属性已经成为了一种习惯,你写代码若不用属性会遭人鄙视,属性能更好的进行封装、更好 的进行权限访问控制等,然鲜为人知的是虽然属性和数据成员在源代码层次上完全兼容,但在二进制层面上却大相径庭。这也就意味着,若将某个公有的数据成员改 成了与之等同的公有属性,那么就必须重新编译所有用到了该公有数据成员的代码,否则运行时会抛出异常。

如下所示,若Customer类被其它程序或类进行了引用,当把Name从变量改为属性的时候必须重新编译所有引用了Customer类的代码,否则运行时会发生错误,虽然我们看上去没任何区别,但将公有变量改为属性破坏了二进制兼容性,从而造成更新单一程序集变得很困难,认识这点没啥太大的实际意义,但是可以让我们更加坚定使用属性的信念。

public class Customer
{
    ///
<summary> /// 这里声明为公有的变量
 /// </summary>
    public string Name;
}
public class Customer
{
    ///
<summary> /// 这里声明为公有的属性
 /// </summary>
    public string Name { get; set; }
}

条目2 用运行时常量(readonly)而不是编译期常量(const)

C#有两种类型的常量:编译期常量运行时常量。二者有着截然不同的行为,使用不当可能会带来很严重的的后果,而使用者还茫然不知所措。

编译期常量使用const关键字声明,运行时常量使用readonly关键字声明
编译期常量可以声明在可以声明在类或者方法中,运行时常量只能声明在类中不能声明在方法中

///
<summary> /// 编译时常量
/// </summary>
public const int ConstAge = 23;

///
<summary> /// 运行时常量
/// </summary>
public static readonly int ReadonlyAge = 23;

编译期常量与运行时常量的行为不同之处在于对他们的访问方式不同。编译期常量的值是在目标代码中进行替换的。如

if (ReadonlyAge == ConstAge)

将会与如下代码编译成同样的IL

if (ReadonlyAge == 23)

这种差异性会导致的后果就是当引用了编译时常量const关键字定义的常量时,若定义常量的程序集发生了变化,而引用的程序集没有重新编译的,这样就造成莫名其妙的错误。如:程序集 Utility中定义了一个const字段和一个readonly字段:

namespace Utility
{
    public class DemoClass
    {
        ///
<summary> /// 编译时常量
 /// </summary>
        public const int ConstNum = 3;
        public static readonly int ReadonlyNum = 3;
    }
}

在程序集ConsoleApplication1引用了Utility程序集,并进行如下调用:

static void Main(string[] args)
{
    Console.WriteLine("ConstNum:" + Utility.DemoClass.ConstNum);
    Console.WriteLine("ReadonlyNum:" + Utility.DemoClass.ReadonlyNum);
}

输出结果为:

ConstNum:3
ReadonlyNum:3

修改Utility程序集如下:

namespace Utility
{
    public class DemoClass
    {
        ///
<summary> /// 编译时常量
 /// </summary>
        public const int ConstNum = 10;
        public static readonly int ReadonlyNum = 10;
    }
}

更新ConsoleApplication1中对Utility程序集的引用,并没有重新编译ConsoleApplication1程序集,输出如下:

ConstNum:3
ReadonlyNum:10

没有如我们预料的那样输出为

ConstNum:10
ReadonlyNum:10

编译时常量只能定义为基本类型:整数、浮点数、枚举和字符串,其它类型即使为值类型也无法定义为常量const如:

///

 

/// 这样定义是不行滴,编译都通不过
///

 

public const DateTime Now = DateTime.Now;

总结:除非数据绝对不可能发生改变,否则不要定义public类型的const常量,若需要的话都定义成private类型的,实在要定义常量时请使用static readonly代替,一来readonly支持的类型更多也更灵活,二来const除了在性能上有那么丁点优势外其他的和static readonly没有任何区别

条目3 推荐使用 is 或 as 操作符而不是强制类型转换

需要使用类型转换的地方,尽量使用as操作符,强制类型转换可能会带来意向不到的负面影响,因为相对于强制类型转换来说,as更加安全,也更加高效,使用as进行转换时不会抛出异常,若转换失败后会返回NULL。

条目4 使用 Conditional 特性而不是#if 条件编译

这点感觉实际用处不大,毕竟实际中用#if的时候都不多,没必要花费那个精力去研究啥Conditional ,略过

条目5 为类型提供 ToString() 方法

同上,这点感觉实际用途不大,为每个实体类实现ToString()方法,有些画蛇添足的感觉,就为了观察每个类的属性信息,就来个 ToString()而且还要关心到底有没有把全部属性都加进去了,若修改了属性啥的咋办,而且代码越多出错概率也越大哈,除非闲的蛋疼否则不去干这费力 不讨好的事情

条目6 理解几个等同性判断之间的关系

实际用途不大,主要介绍了几种相等判断的方法,更多的篇幅用来介绍Equals方法并推荐去重写所有值类型的Equals方法,本人实在不敢苟同可能是咱的理解层次太低吧,而且我们又有多少实际定义值类型的机会呢

条目7 理解 GetHashCode() 的陷阱

关于这点咋说呢,俺基本上还木有使用过GetHashCode()做过啥实际有意义的事情(无意识中用到的不算),所以陷阱之类的对我来说不存在哈哈,略过,以后用到的时候再来好好研究

条目8 推荐使用查询语法而不是循环

这点主要是推荐大家在日常开发中使用linq语法代替繁杂的for、foreach、while循环(虽然有时感觉for循环也并不复杂,但显 然使用linq更简洁),带来的好处就是代码更加简洁,能够更加清晰的表达你的意图,可读性、易用性大幅高,而且也更加容易使用并行计算充分利用多核 CPU的优势,只需简单加上.AsParallel,并行编程就是这么简单

条目9 避免在API中使用转换操作符

事实上就是在任何地方都鲜有使用转换操符的应用,更别提在API中使用了,至少我还木有遇到过,略过

条目10 使用可选参数减少方法重载的数量

.Net4.0开始支持参数默认值了,应用参数默认值使参数变为可选参数,我们终于可以从无尽的重载中解脱出来了,当然可选参数也不是万能的,同条目1一样,修改了默认的参数值后,相关引用的程序集、代码都需要重新编译才可以,不过相对于这个小小的不便,我还是要为可选参数喝彩。
以前我们要实现如下的调用:
static void Main(string[] args)
{
SetUserName(“LazyRabbit”);
SetUserName(“LazyRabbit”, “cnblogs”);
}

需要写下面这样的两个重载才可以实现:

public static void SetUserName(string last)
{

}

public static void SetUserName(string last, string first)
{

}

来看看可选参数的魅力,我们只需要定义一个方法就ok了,实现一样的效果

public static void SetUserName(string last, string first = “LazyRabbit”)
{

}

条目11 理解短小方法的优势

我们平时编码很多人都知道尽量不要让一个方法代码过多,代码过多会造成可读性变差同时意味着单个方法的职责变多,不利于扩展、复用和修改,这里 介绍的观点不是讨论这些,主要介绍了JIT在编译阶段的优化,.Net运行时将调用JIT编译器来将C#编译器生成的IL翻译成机器码,这个过程平摊在应 用程序的整个生命周期中,JIT刚开始不会完全编译所有的IL,CLR会按照函数的粒度来逐一进行JIT编译,这样就大幅降低了程序启动的代码,没有被调 用的方法根本不会被JIT编译,因此将那些不是很重要的逻辑分支分解成更多的小分支要比把所有逻辑放到一起形成大型复杂函数有着更好的性能。短小的方法让 JIT能更好的平摊编译的代码,提高程序运行时的效率。如下代码,第一次调用DemoMethod方法时,if-else这两个分支都会被JIT编译,而实际上仅需要编译其中一个分支。

public static void DemoMethod(bool condition)
{

    if (condition)
    {
    //do Method1 thing
    }
    else
    {
    //do Method2 thing
    }
}

而若改成如下形式的话,在第一次调用DemoMethod方法时,仅会编译Method1()或者Method2()其中的一个,这样显然更加高效

public static void DemoMethod(bool condition)
{

    if (condition)
    {
    Method1();
    }
    else
    {
    Method2();
    }
}

public static void Method1()
{
    //do Method1 thing
}

public static void Method2()
{
    //do Method2 thing
}

以上代码有些牵强,而实际上到底能带来多大的提升呢,这个提升可能微乎其微,但可以肯定 的是方法越复杂带来的提升也就越大,同时方法越小也可以让JIT更容易进行寄存器选择工作,可以让局部变量存放在寄存器而不是栈上,更小的函数意味着更少 的局部变量,更方便JIT对寄存器进行优化。

至此,第一章 C#语言习惯阅读完毕,记录于此以作备忘和回顾,有些观点以前就已经知道了通过阅读加深了一些理解和认识,也有一些不知道的,感觉有用的就好好研读一番,感觉实用价值不高的也一行行看完,权当了解,没准哪天就用到了。

[转载]C#生成PDF页脚第几页共几页

mikel阅读(1254)

[转载]C#生成PDF页脚第几页共几页 – Luck_c – 博客园.

我在网上找了好久都没找到生成的PDF在页脚显示第几页/共几页,然后自己摸索着做出来,分享给大家。

 

 

我用的是这个组件来实现的.net生成PDF。

 

首先创建一个工程,然后引用这个组件


 

然后创建一个页面,添加一个 按钮


然后开始写后台了。。不多说,直接贴代码。

protected void Button1_Click(object sender, EventArgs e)
{
PDF();
}
?
private void PDF()
{
string filePath = "C:\\PDF";
if (false == Directory.Exists(filePath))
Directory.CreateDirectory(filePath);
string filename = filePath + "/PDF.pdf";//设置保存路径

Document doc = new Document(iTextSharp.text.PageSize.A4, 25, 25, 50, 40);//定义pdf大小,设置上下左右边距
PdfWriter writer = PdfWriter.GetInstance(doc, new FileStream(filename, FileMode.Create));//生成pdf路径,创建文件流

doc.Open();
writer.PageEvent = new HeaderAndFooterEvent();

HeaderAndFooterEvent.PAGE_NUMBER = true;//不实现页眉跟页脚
First(doc, writer);//封面页

doc.NewPage();//新建一页

PdfHeader(doc, writer);//在新建的一页里面加入数据
HeaderAndFooterEvent.PAGE_NUMBER = false;//开始书写页眉跟页脚

writer.Flush();
writer.CloseStream = true;
doc.Close();
}

private void PdfHeader(Document doc, PdfWriter writer)
{
string totalStar = string.Empty;
writer.PageEvent = new HeaderAndFooterEvent();
string tmp = "这个是标题";
doc.Add(HeaderAndFooterEvent.InsertTitleContent(tmp));
}

private void First(Document doc, PdfWriter writer)
{
string tmp = "分析报告";
doc.Add(HeaderAndFooterEvent.InsertTitleContent(tmp));

tmp = "(正文 页,附件 0 页)";
doc.Add(HeaderAndFooterEvent.InsertTitleContent(tmp));

//模版 显示总共页数
HeaderAndFooterEvent.tpl = writer.DirectContent.CreateTemplate(100, 100); //模版的宽度和高度
PdfContentByte cb = writer.DirectContent;
cb.AddTemplate(HeaderAndFooterEvent.tpl, 266, 714);//调节模版显示的位置

}

然后再新建一个类这个类是用来重写Itext组件的一些方法的。

该类要继承类PdfPageEventHelper和接口IPdfPageEvent

然后重写方法

public static PdfTemplate tpl = null;//模版
public static bool PAGE_NUMBER = false;//为True时就生成 页眉和页脚

iTextSharp.text.Font font = BaseFontAndSize("黑体", 10, Font.NORMAL, BaseColor.BLACK);

//重写 关闭一个页面时
public override void OnEndPage(PdfWriter writer, Document document)
{
if (PAGE_NUMBER)
{
Phrase header = new Phrase("PDF测试生成页眉分析报告", font);

Phrase footer = new Phrase("第" + (writer.PageNumber - 1) + "页/共 页", font);
PdfContentByte cb = writer.DirectContent;

//模版 显示总共页数
cb.AddTemplate(tpl, document.Right - 54 + document.LeftMargin, document.Bottom - 8);//调节模版显示的位置

//页眉显示的位置
ColumnText.ShowTextAligned(cb, Element.ALIGN_CENTER, header,
document.Right - 140 + document.LeftMargin, document.Top + 10, 0);

//页脚显示的位置
ColumnText.ShowTextAligned(cb, Element.ALIGN_CENTER, footer,
document.Right - 60 + document.LeftMargin, document.Bottom - 10, 0);
}
}

//重写 打开一个新页面时
public override void OnStartPage(PdfWriter writer, Document document)
{
if (PAGE_NUMBER)
{
writer.PageCount = writer.PageNumber-1;
}
}
//关闭PDF文档时发生该事件
public override void OnCloseDocument(PdfWriter writer, Document document)
{
BaseFont bf = BaseFont.CreateFont(@"c:\windows\fonts\SIMYOU.TTF", BaseFont.IDENTITY_H, false); //调用的字体
tpl.BeginText();
tpl.SetFontAndSize(bf, 16);//生成的模版的字体、颜色
tpl.ShowText((writer.PageNumber - 2).ToString());//模版显示的内容
tpl.EndText();
tpl.ClosePath();
}
//定义字体 颜色
public static Font BaseFontAndSize(string font_name, int size, int style, BaseColor baseColor)
{
BaseFont baseFont;
BaseFont.AddToResourceSearch("iTextAsian.dll");
BaseFont.AddToResourceSearch("iTextAsianCmaps.dll");
Font font = null;
string file_name = "";
int fontStyle;
switch (font_name)
{
case "黑体":
file_name = "SIMHEI.TTF";
break;
case "华文中宋":
file_name = "STZHONGS.TTF";
break;
case "宋体":
file_name = "SIMYOU.TTF";
break;
default:
file_name = "SIMYOU.TTF";
break;
}
baseFont = BaseFont.CreateFont(@"c:/windows/fonts/" + file_name, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);//字体:黑体
if (style &lt; -1)
{
fontStyle = Font.NORMAL;
}
else
{
fontStyle = style;
}
font = new Font(baseFont, size, fontStyle, baseColor);
return font;
}

//定义输出文本
public static Paragraph InsertTitleContent(string text)
{

iTextSharp.text.Font font = BaseFontAndSize("华文中宋", 16, Font.BOLD,BaseColor.BLACK);

//BaseFont bfSun = BaseFont.CreateFont(@"c:\windows\fonts\STZHONGS.TTF", BaseFont.IDENTITY_H, false); //调用的字体
//Font font = new Font(bfSun, 15);

Paragraph paragraph = new Paragraph(text, font);//新建一行
paragraph.Alignment = Element.ALIGN_CENTER;//居中
paragraph.SpacingBefore = 5;
paragraph.SpacingAfter = 5;
paragraph.SetLeading(1, 2);//每行间的间隔
return paragraph;
}

好了,大功告成了!!!

下载地址:

http://download.csdn.net/detail/aasswwe/4356788

[转载]Javascript 页面模板化

mikel阅读(937)

[转载]Javascript 页面模板化 ——大部分人没有使用过的方法 – 川山甲 – 博客园.

介绍
今天遇到一个问题,这个问题也是我以前遇到的问题,以前的方式,也是大多数人使用的方式。大家可以看看我的文章。
从前的方式——我估计也是大多数人使用的方式
比如以下的代码——摘自新浪首页的一段js代码,这种方式也是大多数人使用的方式。

上面的代码,你看完有什么反应?你也许会把+=这种形式改成数组的push形式,有人说push比这种字符链接要快,但是也快不了多少。

 如果这种嵌入的html代码很多,你也要这样处理吗?如果修改(添加或者删除)某个html,那么你就要不断的翻代码,找对应的标签,然后在找结束标签,你不觉得麻烦吗?也许有人会这样写(这个是我早期写的,也是我最惯于使用的方式)。

 

 

上面的代码,我维护起来也是非常费劲的,很多时候如果ui那边添加了个标签,或者修改了下html结构,那我就头疼了。

 

页面模板化——全新的方式
这是我最近看了facebook的js代码,偶然间发现以下的方式看起来不错,希望与大家分享。
看我的index.html代码

 看我的js代码

 

 今天偶然间发现这种方式更加合理,因为那个for循环看着太闹心了

 维护上面的html代码我们是很容易的,下次无论html代码结构有什么变动,改起来都很方便。

 

总结

上面的代码有很多问题,大家不要见怪,这个只是我的简易版。

当前的模板只支持简单的变量替换,有很多功能未开发出来,比如if标签,for标签,如果大家有兴趣,可以试着做一下,如果做的比较成功,告知一下。如果能做个类似smarty的开源框架,呵呵,咱们也可以为开源事业做点贡献不是。

 

推荐

[转载]用中间表提高互联网程序的开发效率

mikel阅读(1025)

[转载]用中间表提高互联网程序的开发效率 – 李玉龙 – 博客园.

基本的设计思想:

以业务的核心数据为中心,生成一张中间表,合并主要的业务数据。以简化互联网程序设计的复杂度。这个设计可以用于电子商务、在线视频等大部分互联网产品。下面以dangdang网为例说明:

举例说明

如果我们需要做一个dangdang那样的图书销售页面,假设我们有200万图书,数据存储到mySQL,我们至少需要:

  1. 生成首页推荐:包括推荐图书的标题、价格、评分、评论信息
  2. 生成排行榜:包括图书标题、价格、评分、评论信息
  3. 生成图书详情页:包括图书标题、价格、评分、评论信息
  4. 做一个搜索引擎,可以根据标题,作者等等检索图书,检索页包含图书标题、价格、评分、评论信息

我们设计数据库的时候,一般会这么设计:

  1. 图书基本信息表 (c_id, c_title, c_authors, c_publish … )
  2. 价格表 (c_id, c_price, c_discount …)
  3. 评分表 (c_id, c_score …)
  4. 评论表 (c_id, c_comment_count …)
  5. 首页推荐图书(c_pos, c_ids)
  6. xxx
  7. xxx

实现前3个需求并不太复杂,读取每个表中的数据到内存,得到一个数据集,然后输出静态页面即可。但是需要读取很多个表,然后合并数据,这个操作仍然 是很繁琐的(相信你不会去用一条SQL联合很多个表吧),并且,随着运营需求的变化,需要读的表会越来越多,我们需要不断的调整程序,以读取更多的表。第 4个搜索的需求就比较麻烦一些了,本来我们有sphinx可以用,但是由于表的复杂关系,sphinx没法用。

如果这些信息都存在一张表中,上面的问题可以迎刃而解。比如这样:(c_id, c_title, c_authors, c_publish, c_price, c_discount, c_score, c_comment_count ) 但是这种DB设计是不可接受的。原因有很多:

  1. 单条记录过长,DB效率低
  2. 模块耦合度大,所有的模块都通过DB耦合在一起了
  3. 更重要的,如果我们某个模块的存储结构不是mySQL db怎么办呢。

我的设计方案:

表结构的设计和我们前面讲的一样,但是增加一张中间表,即那张包含了图书所有信息的大表(c_id, c_title, c_authors, c_publish, c_price, c_discount, c_score, c_comment_count … )。我们的程序每隔一段时间(比如2分钟),把其他表中的数据merge起来同步到这个大表。我们前面需求中的业务逻辑都通过这张大表进行。现在只要一条 sql就可以读到所有图书的信息,然后我们用这个信息生成页面就可以了,不用再merge任何表。

这个设计方案可能的缺点有哪些呢?

  1. 单条记录过长,效率低。效率确实会低,但是没有关系,我们这个表是只读的,用来生成页面的,用来检索的,慢一点儿有什么关系呢?如果你还嫌她慢,那就增加一层memcached/redis/leveldb的缓存好了。
  2. 实时性问题,由于这个中间表是定时更新的,所以肯定有几分钟的延迟,但是有什么关系呢,除了详情页中的价格和评论数,其他的信息都不是敏感信息,只要我们在详情页中实时获取这些敏感信息就可以了。

所以这些缺点都不是太大的问题。

这个设计方案有什么优点呢?优点就多了

  1. 解决了搜索的问题,让我们尽情的用sphinx好了,即使数据存储在了非mysql的存储结构中,我们只要能把数据导入到这个中间表,sphinx依然可用,而且只要一条简单sql就搞定;
  2. 简化了程序开发,所有获取数据的过程都是1条sql搞定;
  3. 减少了程序耦合,如果没这张中间表,如果我们想把其中一个模块迁移到其他DB或非DB的引擎,那么很多程序都得修改,现在只要我们修改同步数据到中间表的程序就可以了