
暂无个人介绍
常用命令 命令 说明 vagrant up 运行vm vagrant status 查看当前虚拟机运行状态 vagrant suspend 暂停虚拟机 vagrant ssh ssh方式登录虚拟机 vagrant halt 关闭虚拟机,但是保存了工作内容 vagrant destroy 销毁虚拟机。关闭虚拟机,并进行格式化。 运行界面 登录成功 Udacity创建开发环境 https://www.udacity.com/account#!/development_environment Github support the emoji. Like this 本文转自快乐八哥博客园博客,原文链接http://www.cnblogs.com/liminjun88/p/5511003.html如需转载请自行联系原作者 快乐八哥
tygtug@newsmth.net发文询问“能创建一个对静态类中的一组方法的访问代理吗?” “某些类都有一组静态方法 现在想在运行时传入类名 根据类名决定执行相应的类的静态方法。 类似于指定一个类的变量似的。” “涉及到对旧有代码的改造时的折中考虑,才会有这个需要的,不能把问题归于类的结构涉及 不合理而搪塞阿。” 偶写了一个实现Demo: 1using System; 2 3namespace DelegateTest 4{ 5 class MainClass 6 { 7 8 [STAThread] 9 static void Main(string[] args)10 {11 WhoAmIDelegate d1=DelegateFactory.CreateWhoAmIDelegate("DelegateTest.ClassA");12 WhoAmIDelegate d2=DelegateFactory.CreateWhoAmIDelegate("DelegateTest.ClassB");1314 Console.WriteLine(d1());15 Console.WriteLine(d2());1617 long start,end;18 int loops = 100000000;1920 start=DateTime.Now.Ticks;21 for(int i=0;i<loops;i++)22 {23 ClassA.WhoAmI();24 }25 end = DateTime.Now.Ticks;26 Console.WriteLine("{0}次直接调用ClassA.WhoAmI耗时:{1}ms",loops,(end-start)/10000);2728 start=DateTime.Now.Ticks;29 for(int i=0;i<loops;i++)30 {31 d1();32 }33 end = DateTime.Now.Ticks;34 Console.WriteLine("{0}次调用WhoAmIDelegate耗时:{1}ms",loops,(end-start)/10000);3536 }37 }383940 public delegate string WhoAmIDelegate();414243 public class ClassA44 {45 //方法很丑陋,嘿嘿。为了性能测试方便嘛。46 public static string WhoAmI()47 {48 return "ClassA";49 }50 }5152 public class ClassB53 {54 public static string WhoAmI()55 {56 return "ClassB";57 }58 }5960 public class DelegateFactory61 {62 public static WhoAmIDelegate CreateWhoAmIDelegate(string className)63 {64 return (WhoAmIDelegate)Delegate.CreateDelegate(Type.GetType("DelegateTest.WhoAmIDelegate"),Type.GetType(className),"WhoAmI");65 }66 }67}68 Debug下运行结果: ClassA ClassB 100000000次直接调用ClassA.WhoAmI耗时:961ms 100000000次调用WhoAmIDelegate耗时:3134ms Release下运行结果: ClassA ClassB 100000000次直接调用ClassA.WhoAmI耗时:210ms 100000000次调用WhoAmIDelegate耗时:2964ms 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2005/08/09/211073.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
烦透了闭包。目前在修改一个项目,该项目的原始代码中使用了大量的闭包。一个套一个,甚至套了三四层,那函数啊,那局部变量啊,叫一个多啊,那执行流程啊,叫一个乱啊。 下面是个draw函数,730行,把它的结构提取出来,有下面的树: draw 15变量 function0 1变量 function1 13变量 function11 function12 function13 function14 function141 function142 function15 function151 function1511 function16 function161 function17 function18 function19 function1A function2 function3 8变量 function31 function32 function4 26变量 function41 function42 function43 function44 function45 function46 function47 function48 如果只看draw作用域,那么draw下的15个变量对于从函数function0-function48来说就是外部变量。而对于function41-function48来说,外部变量就有26+15=31个。 这不就是最古老最邪恶的结构化编程的变种吗! 对这个函数来说,大量使用闭包,存在三个问题: ·阅读难——执行流程上串下跳的; ·调试难——调用栈结构层次就像天书一样; ·重构难——外部变量太多。 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2010/06/04/1751704.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
一、程序的基本结构 程序的控制核心是Context类,它持有: ·类型管理器TypeManager,管理该运用程序域加载的命名空间及类型的树,树结构如下: TypeDictionary(Root) |--TypeDictionary | |--TypeDictionary | |--TypeDictionary | | | |--Type | |--Type | | | |--TypeDictionary | |--Type |--Type | 其中TypeDictionary对应的是命名空间,Type对应的是类型。TypeManager还管理一个名为Now的TypeDictionary,表示当前所在的TypeDictionary。 ·AliasCmds ,命令缩写字典。 ·Instances,用户变量字典。 ·CmdDispatcher是命令指派器。控制台获取指令后传给Context。代码: while ((cmd = Console.ReadLine().Trim()) != "exit") { if (!String.IsNullOrEmpty(cmd)) { cxt.Invoke(cmd); } Console.Write(">> "); } Context又传给CmdDispatcher处理。CmdDispatcher解析命令,根据命令的特征选择不同的CmdHandler来处理。目前编写了5个CmdDispatcher: CdClassCmdHandler:进出命名空间的处理,针对cdc指令; ListClassCmdHandler:列出命名空间和类型,针对lsc,dirc指令; ListInstanceCmdHandler:列出用户变量,针对 my 指令; ListAliasCmdHandler:列出指令缩写,针对 alias 指令; CscCmdHandler:编译并运行代码,其它CmdDispatcher 处理不了的都交给它。 CmdDispatcher.Dispatch()方法代码: public void Dispatch() { String[] results = InputCmdString.Split(SPLITS, StringSplitOptions.None); if(results.Length == 0) return; String cmd = results[0]; String mark = String.Empty; IList<String> args = new List<String>(); Int32 argIndex = 1; if (results.Length > 1 && results[1].StartsWith("-")) { argIndex ++; mark = results[1]; } for(;argIndex < results.Length;argIndex++) { args.Add(results[argIndex]); } switch (cmd.ToLower()) { case "debug": // 开启debug开关 Context.Debug = true; break; case "undebug": // 关闭debug开关 Context.Debug = false; break; case "cdc": // 改变命名空间 new CdClassCmdHandler(Context, InputCmdString, mark, args).Run(); break; case "lsc": // 列出命名空间的内容 case "dirc": new ListClassCmdHandler(Context, InputCmdString, mark, args).Run(); break; case "my": // 列出用户变量 new ListInstanceCmdHandler(Context, InputCmdString, mark, args).Run(); break; case "alias": // 列出alias列表 new ListAliasCmdHandler(Context, InputCmdString, mark, args).Run(); break; default: String fullCmd = Context.GetFullCmd(cmd); if (fullCmd != null) // 处理 alias { if (mark != null) fullCmd += " " + mark; if (args != null && args.Count > 0) { foreach(String s in args) { fullCmd += " " + s; } } Context.Invoke(fullCmd); } else // 编译代码并运行 { new CscCmdHandler(Context, InputCmdString).Run(); } break; } return; } 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2008/02/29/1085815.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
刷机不是用rom包吗?怎么可以使用fastboot flashall -w将*.img文件刷入呢? 在Mac上面可以参考这篇文章进行刷机.概括来说解释从官方下载rom包,解压后运行./flash-all.sh脚本。在这里我下载了Nexus 6p的官方的rom包,我们看看这个文件中的内容是什么? 下面就是脚本文件的内容 fastboot flash bootloader bootloader-angler-angler-02.45.img fastboot reboot-bootloader sleep 5 fastboot flash radio radio-angler-angler-02.50.img fastboot reboot-bootloader sleep 5 fastboot -w update image-angler-mmb29p.zip image-angler-mmb29p.zip又是什么呢?解开后就是这么几个文件: android-info.txt boot.img cache.img recovery.img system.img userdata.img vendor.img 有没有很熟悉,其实就是我们编译后,生成的几个Image文件。 Nexus 6P 怎么没有提供驱动包呢?到哪里去下载呢? 在制作rom包的过程中,我们还需要特定Nexus机器的驱动包,因为这些驱动包不属于AOSP,需要单独下载跟其他AOSP产生的*.img文件一起打包才成为一个完整的rom包.以往的驱动包,google都会提供,这次的驱动包google没有提供了.从Hamilton Turner的回答中,我们知道Nexus 5x/6p的刷机包中驱动不再是必须的,因为这两款机型中所有的驱动都存在于一个单独的vendor分区,因此没有必要在编译的时候就放入system.img中。 执行lunch命令的时候,弹出Can not find SDK 10.6错误 具体的错误如下所示: myhost:android-4.4.4_r2.0.1 carl$ lunch aosp_x86-eng build/core/combo/HOST_darwin-x86.mk:65: ***************************************************** build/core/combo/HOST_darwin-x86.mk:66: * Can not find SDK 10.6 at /Developer/SDKs/MacOSX10.6.sdk build/core/combo/HOST_darwin-x86.mk:67: ***************************************************** build/core/combo/HOST_darwin-x86.mk:68: *** Stop.. Stop. 具体错误的原因是,在编译脚本中会检查当前的Xcode sdk的版本是否是兼容的,这可能是因为你mac上的xcode版本比较新,在Android源代码中还没有添加上.理论上说应该用Android代码对应的Xcode SDK版本来编译,不然可能会有兼容性的问题。但是我自己实测最新的xcode版本是可以正常编译Android源代码的,具体的方法如下所示: 你可以到Xcode的sdk路径下(/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs)查看当前系统的SDK版本是多少。比如我的SDK版本是MacOSX10.11.sdk,接下来编辑build/core/combo/mac_version.mk中的mac_sdk_versions_supported所在的行添加10.11就行了。在运行下lunch命令,是不是就好了。 本文转自陈哈哈博客园博客,原文链接http://www.cnblogs.com/kissazi2/p/5244438.html如需转载请自行联系原作者 kissazi2
1、其实高级语言和面向过程的语言最求的目标都是一致的,高可复用性,另外,封装性。我发现自己在写C语言的时候,总是不自觉地就引入了高级语言的一些封装性的思想(如以下代码段1所示),而我的同学却总是按着最原始的方式对函数进行命名。学过编译原理的同学就会知道,最原始的C++编译器其实就是将C++转化成C语言,然后用C语言的编译器进行实现的。C++中的类转化成C语言,其实总体的思想就是在函数名上多添加了一个类名,在函数签名上有一些添加信息协助编译器翻译成C语言。代码段1的好处就是里面的函数跟别的C函数库中的函数不容易重合,另外,通过命名提醒调用者这些函数是一类的东西。 1 #ifndef __I2C_H__ 2 #define __I2C_H__ 3 4 //========函数区============================================ 5 extern void I2C_start(); //开始信号 6 extern void I2C_stop(); //停止 7 extern void I2C_respons(); //应答 8 extern void I2C_write_byte(unsigned char date); 9 extern unsigned char I2C_read_byte(); 10 extern void I2C_write_address(unsigned char address,unsigned char date); 11 extern unsigned char I2C_read_address(unsigned char address); 12 //========函数区结束============================================ 13 #endif 代码段1 加上前缀的C函数 1 extern bit ack; 2 //起动总线函数 3 extern void Start_(); 4 //结束总线函数 5 extern void Stop_(); 6 //应答子函数 7 extern void Ack_(bit a); 8 //字节数据发送函数 9 extern void SendByte(unsigned char c); 10 //有子地址发送多字节数据函数 11 extern bit ISendStr(unsigned char sla,unsigned char suba,unsigned char *s,unsigned char no) ; 12 //无子地址发送多字节数据函数 13 extern bit ISendStrExt(unsigned char sla,unsigned char *s,unsigned char no); 14 //无子地址读字节数据函数 15 extern unsigned char RcvByte(); 代码段2 没加上前缀的C函数 2、硬件调试和软件调试的相同点。对于开发人员来说,能够及时地得到反馈是一件幸福的事情,我初学51单片机和EDA的时候,总是喜欢把整个程序都写完,然后一股脑地进行调试。这其实很打击人的自信心,特别是当你刚刚入门的时候。后来,我想为什么不像用Java或C#开发软件一样,设个断点,弹个窗口?但是keilC的调试工具是在让人蛋疼,因为常常需要跟硬件结合在一起。是软件模拟不来的,但是总是有办法弄一些提示信息的。比如我们在51单品机上面可以弄一个LED、用串口调试来显示软件运行中的信息。 本文转自陈哈哈博客园博客,原文链接http://www.cnblogs.com/kissazi2/archive/2013/06/10/3131094.html如需转载请自行联系原作者 kissazi2
一、实验目的 (1)进一步熟悉Quartus II软件和GW48-PK2S实验系统的使用方法; (2)用状态机实现序列检测器的设计,了解一般状态机的设计与应用 二、实验内容 1. 基本命题 利用Quartus II实现一个8位的序列检测器设计;给出仿真波形。最后进行引脚锁定并进行测试,硬件验证设计电路对给定序列的检测功能。 2. 扩展命题 在上述设计基础上,通过修改设计,实现以最简便的预置方法,获得n位序列检测器的功能。 三、实验仪器与器材 计算机1台,GW48-PK2S实验箱1台,QuartusⅡ6.0 1套。 四、实验 1. 基本命题 ① 总体设计 设计两个进程。 进程Com1:实现序列的检测 进程Com2:实现检测结果的输出 ② 程序设计 LIBRARYIEEE; USEIEEE.STD_LOGIC_1164.ALL; ENTITY SCHK IS PORT(DIN, CLK, CLR :INSTD_LOGIC;--串行输入数据位/工作时钟/复位信号 AB :OUTSTD_LOGIC_VECTOR(3DOWNTO0));--检测结果输出 END SCHK; ARCHITECTURE behav OF SCHK IS SIGNAL Q :INTEGERRANGE0TO8; SIGNAL D :STD_LOGIC_VECTOR(7DOWNTO0); --8位待检测预置数 BEGIN D <="11100101" ;--8位待检测预置数:密码:E5H Com1:PROCESS( CLK, CLR ) BEGIN IF CLR = '1' THEN Q <=0; ELSIF CLK'EVENTAND CLK='1' THEN--时钟到来时,判断并处理当前输入的位 CASE Q IS WHEN0=> IF DIN = D(7)THEN Q <=1;ELSE Q <=0;ENDIF; WHEN1=> IF DIN = D(6)THEN Q <=2;ELSE Q <=0;ENDIF; WHEN2=> IF DIN = D(5)THEN Q <=3;ELSE Q <=0;ENDIF; WHEN3=> IF DIN = D(4)THEN Q <=4;ELSE Q <=0;ENDIF; WHEN4=> IF DIN = D(3)THEN Q <=5;ELSE Q <=0;ENDIF; WHEN5=> IF DIN = D(2)THEN Q <=6;ELSE Q <=0;ENDIF; WHEN6=> IF DIN = D(1)THEN Q <=7;ELSE Q <=0;ENDIF; WHEN7=> IF DIN = D(0)THEN Q <=8;ELSE Q <=0;ENDIF; WHENOTHERS=> Q <=0; ENDCASE; ENDIF; ENDPROCESS; Com2:PROCESS( Q ) --检测结果判断输出 BEGIN IF Q =8 THEN AB <="0001"; --序列数检测正确,输出 "1" ELSE AB <="0011"; --序列数检测错误,输出 "3" ENDIF; ENDPROCESS; END behav ; ③ 仿真分析 图6-1 仿真波形图 仿真预测:当输入的结果是预置序列的时候,输出0001;反之,输出0011。 ④ 硬件测试 表1-1 SCHK在GWAC6板上目标芯片EP1C6Q240C8的引脚锁定信息 端口名称 端口符号 GWAC6板输入输出元件 GWAC6板接口 目标器件引脚 备注 输入引脚D DIN 按键1 PIO1 233 模式No.5 清零端 CLR 按键2 PIO2 234 主频率 CLK 按键3 PIO3 235 4位数字量输出引脚 AB 数码管7 PIO40~47 161-164 图6-2引脚的配置 2. 扩展命题 ① 总体设计 本命题通过改写case判断语句,将要检测的序列和序列的位数关联,通过IF语句对状态进行判断。 ② 程序设计 LIBRARYIEEE; USEIEEE.STD_LOGIC_1164.ALL; ENTITY SCHK IS PORT(DIN, CLK, CLR ,BIT_COUNT :INSTD_LOGIC;--串行输入数据位/工作时钟/复位信号/要检测的位数标识码 AB :OUTSTD_LOGIC_VECTOR(3DOWNTO0));--检测结果输出 END SCHK; ARCHITECTURE behav OF SCHK IS SIGNAL Q :INTEGERRANGE0TO8; SIGNAL D :STD_LOGIC_VECTOR(7DOWNTO0); --8位待检测预置数 SIGNAL Count:INTEGERRANGE0TO8; --要检测的位数(最多支持8位) BEGIN D <="11100000" ;--8位待检测预置数:--11100000 Com1:PROCESS( CLK, CLR ) BEGIN IF BIT_COUNT='0' THEN Count<=3;--判断要检测多少位 ELSE Count<=4;ENDIF; IF CLR = '1' THEN Q <=0; ELSIF CLK'EVENTAND CLK='1' THEN--时钟到来时,判断并处理当前输入的位 if Q=(Count+1)THEN Q<=0;ENDIF;--开始新的一轮的检测 if(DIN = D(7-Q))THEN Q<=Q+1;ELSE Q<=0;ENDIF;--进行状态的检测 ENDIF; ENDPROCESS; Com2:PROCESS( Q ) --检测结果判断输出 BEGIN IF Q =(Count+1) THEN AB <="0001"; --序列数检测正确,输出 "1" ELSE AB <="0011"; --序列数检测错误,输出 "3" ENDIF; ENDPROCESS; END behav ; ③ 仿真分析 当BIT_COUNT为低电平的时候,实现4位序列检测器的功能;当BIT_COUNT为高电平的时候,实现5位序列检测器的功能。设待检测的序列为“11100000”。 图6-3 5位序列检测器波形图 ④ 硬件测试 表1-2 SCHK在GWAC6板上目标芯片EP1C6Q240C8的引脚锁定信息 端口名称 端口符号 GWAC6板输入输出元件 GWAC6板接口 目标器件引脚 备注 输入引脚D DIN 按键1 PIO1 233 模式No.5 清零端 CLR 按键2 PIO2 234 主频率 CLK 按键3 PIO3 235 检测位数标识 BIT_COUNT 按键4 PIO4 236 4位数字量输出引脚 AB 数码管7 PIO40~47 161-164 图6-4 引脚的配置 五、实验思考题 如果带检测预置数必须以右移方式进入序列检测器,那么该程序该做如何修改? ① 总体设计 同序列检测中数据右移输入的思路,但是我们这次不进行检测,收到什么就将其存入程序中的预置数变量中。我们需要额外设置一个输入变量,变量ISE用来标示是否进行预置数序列的输入,当ISE=1,输入口DIN用来输入预置数序列的;当ISE=0,输入口DIN用来输入要检测的序列。其中预置数序列的长度与BIT_COUNT相关。 ② 程序设计 LIBRARYIEEE; USEIEEE.STD_LOGIC_1164.ALL; ENTITY SCHK IS PORT(DIN, CLK, CLR ,BIT_COUNT,ISE :INSTD_LOGIC;--串行输入数据位/工作时钟/复位信号/要检测的位数标识码/预置数序列的输入使能 AB :OUTSTD_LOGIC_VECTOR(3DOWNTO0));--检测结果输出 END SCHK; ARCHITECTURE behav OF SCHK IS SIGNAL Q :INTEGERRANGE0TO8; SIGNAL D :STD_LOGIC_VECTOR(7DOWNTO0); --8位待检测预置数 SIGNAL Count:INTEGERRANGE0TO8; --要检测的位数(最多支持8位) BEGIN Com1:PROCESS( CLK, CLR ) BEGIN IF BIT_COUNT='0' THEN Count<=3;--判断要检测多少位 ELSE Count<=4;ENDIF; IF CLR = '1' THEN Q <=0; ELSIF CLK'EVENTAND CLK='1' THEN--时钟到来时,判断并处理当前输入的位 if Q=(Count+1)THEN Q<=0;ENDIF;--开始新的一轮的检测 --ISE=1的时候,开始预置数序列的输入 if ISE='1' then D(7-Q)<=DIN;Q<=Q+1; else if(DIN = D(7-Q))THEN Q<=Q+1;ELSE Q<=0;ENDIF;--进行状态的检测 endif; ENDIF; ENDPROCESS; Com2:PROCESS( Q ) --检测结果判断输出 BEGIN IF Q =(Count+1)AND ISE='0' THEN AB <="0001"; --当序列数检测正确并且预置数使能为低电平的时候,输出 "1" ELSE AB <="0011"; --序列数检测错误,输出 "3" ENDIF; ENDPROCESS; END behav ; ③ 仿真分析 图6-5 带有预置数功能的4位序列检测器波形图 ④ 硬件测试 表1-3 SCHK在GWAC6板上目标芯片EP1C6Q240C8的引脚锁定信息 端口名称 端口符号 GWAC6板输入输出元件 GWAC6板接口 目标器件引脚 备注 输入引脚D DIN 按键1 PIO1 233 模式No.5 清零端 CLR 按键2 PIO2 234 主频率 CLK 按键3 PIO3 235 检测位数标识 BIT_COUNT 按键4 PIO4 236 预置数使能 ISE 按键5 PIO5 237 4位数字量输出引脚 AB 数码管7 PIO40~47 161-164 图6-6 引脚的配置 本文转自陈哈哈博客园博客,原文链接http://www.cnblogs.com/kissazi2/p/3175467.html如需转载请自行联系原作者 kissazi2
http://news.xinhuanet.com/focus/2005-11/02/content_3715522.htm ...... 江苏省墙改办主任乔增东介绍:目前,中国人均占有耕地量只有1.4亩左右,仅相当于世界 人均水平的40%,而全国每年因为烧制实心粘土砖而耗用了超过70万亩的良田,相当于每年 有近六十万人失去了耕地,如果不加以控制,未来我们的子孙很可能面对无地可耕的局面。 ...... 浙江省新墙办主任金关标介绍,至2004年底,浙江省境内有粘土砖瓦企业2446家,共占用土 地8.1万亩,2004年一共挖土制标砖222亿块,相当于10平方公里土地平均下降了8毫米。 ××××××××××××× 全国每年因为烧制实心粘土砖而耗用了超过70万亩的良田,相当于每年 有近六十万人失去了耕地 那个70万亩是合计,又不是增量,哼唧 哪有一个厂打一枪换一个地方,今年在这里明年在那里的。 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2005/11/02/267086.html如需转载请自行联系原作者 xioatie 集异璧实验室(GEBLAB)
代码是第一手文献。现在阅读大型C#,java代码已经不成问题了。现在要开始联系阅读C代码。拿Postgresql和QuakeIII开刀吧。另外,Geotools没事还得看看。剩下的,就看时间允许不允许了:感兴趣的代码:C: Postgresql/postgisQuakeIIILinux kernelMonoC#:Axiom.Net framework libraryNetron graph libWorld windJava:GeotoolsJTSJUMPEclipseHibernate 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2005/08/25/222256.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
腰腹减肥操五种 腰肥腹凸不仅有碍形体美,而且对健康不利。建议锻炼者每l一2个星期或每月换1种方法进行练习。锻炼一段时间后,增加运动量,一次做两种操。练习要不疾不缓,从容不迫,最大限度地放松肌肉。 准备活动:中等速度,用鼻自然呼吸,动作幅度要大。 第一种 1、双脚分立同宽。头部做顺、逆时针环绕运动,各4次。重复2—3遍。 2、双脚分立,双臂前平举,双手叉握。身体向左右各转动4次。重复2—3遍. 3、站或坐,双手叉腰。先低头4次,后弯腰(体前屈)4次。重复2—3遍。 4、站立,双手搭肩,肘向前。抬右腿,尽量用右膝盖触左肘。还原。拾左腿,触右肘,重复4—6遍。 5、坐姿,双脚尽量向两侧分开。双手前伸,向前屈体,手向前摸得越远越好。重复4—6遍。 6、仰卧,双手胸前交叉,做仰卧起坐,脚不要离地。再躺下,屈膝拾腿,膝盖尽量贴靠胸部。重复4—6遍。 第二种 l、双脚开立,双手抱头。破部做顺时针绕环练习20—25次,幅度越大越好。 2、坐姿,双手身后支撑。脚跟贴地慢慢屈腿,膝盖尽量贴靠胸部。重复8一15遍。 3、仰卧,双手紧压腹部。克服双手的压力,用力挺起腹部,同时深吸气。3—5秒钟后,双手再用力下压腹部,慢慢呼气还原。休息5秒钟再做。重复8—15遍。 4、俯卧,双手放在脸下,慢抬腰,胸部离地越高越好,坚持越久越好。短时间歇后再做,重复2遍。 第三种 l、双手叉腰站立。用力向两侧屈体,先右后左,做15—20次。 2、坐姿,双后身后支撑。双腿先倒向右侧,右腿触地。再倒向左侧,左腿触地。重复8一12遍。 3、仰卧,双后抱头。慢慢起坐,再慢慢躺下。锻炼一段时间后,把脚放在椅子上做。重复4—6遍。 4、俯卧,双手前伸,双腿分开。抬起右边身体,右手尽量上伸。还原。做20—25温。稍休息后,换方向再做。 第四种 l、坐姿,双脚分开,双手叉腰。上体用力向左右各转动6—8次。重复2遍。 2、仰卧,双手叉腰,微抬起上体,尽量向右转动,还原。换方向再做。能做多少次就做多少次。 3、仰卧,双臂侧平举。双臂前平伸,同时抬起上身,用力收腹——吸气。还原,呼气。重复4—6遍。 4、俯卧,双手撑起身体。后抬踢右腿,能做多少次就做多少次。换左腿再做。 第五种 l、直立,面前横一棍(或绳子)。左右脚轮流迈进棍子。迈脚时背要直,不能弯腰。做2—5分钟。 2、跪地,前臂支撑,尽量向前屈体收腹,绷紧臂部。还原。再抬起身体,头后仰,尽量向后屈体。重复10—20遍。 3、右侧卧。有节奏地屈伸左腿。屈腿时膝盖尽量贴靠胸部,到累了为止。稍休息后换侧再做。 4、右侧卧,左腿放在椅子上,双臂胸前叉抱。反复向左侧屈体,到累了为止。稍休息后换侧再做。 做套腰部健美操 时间保持一种站姿或坐姿,容易使腰椎关节僵硬、腰肌劳损。特别是有的人一到中年就经常腰酸背痛且腰部脂肪堆积。如果有时间,不妨做做腰部健美操,防病又健美。 屈腰前屈时双腿并拢坐床上,用头触膝盖。后屈时俯卧床上,双手撑起上半身,尽量使上半身与腿的夹角接近90°。侧屈时两脚分开与肩宽站立,左手贴住左大腿向小腿下滑,右手同样。注意不要向前弯腰。各5下。 拧腰仰卧床上,双手侧平伸与身体呈十字状,以腰带动一侧手去触另一侧手,尽可能腰部以下贴紧床面。各5次。然后双手枕于脑后,屈起右腿用膝盖触碰左边的床面,尽可能腰部以上贴紧床面,左腿同样。各5次。 旋腰双脚分开与肩宽站立,两手相握伸直,身体前倾90°然后慢慢转成侧倾90°,后仰尽可能小于120°,再慢慢经另一侧的侧倾转到前倾位置。顺时针逆时针方向各3圈。 做腰部健美操要注意动作缓慢,量力而行,循序渐进,切勿操之过急或因动作猛烈而扭闪了腰。 最佳的腰部锻炼姿势 最佳的腰部锻炼姿势: 对侧运动。针对腰侧的赘肉,是塑成纤细腰身的关键! 作法:仰面平躺,屈膝,脚平放地上,用左脚踝触右膝。手置于头后,肘部向外。右肩缓缓上抬贴向左膝,含胸(肘部始终保持与耳朵一线,不要置于耳前,不要前倾头部或颈部)。保持姿势,然后缓缓下降。另一侧重复。 错误的腰部练习: 借住外力扭动。因为没有受到阻力,所以腹部不会变得结实。唯一的效果就是伸伸腰和热身一下。 增加腰部曲线运动 腰部除了要细之外,更要有结实的肌肉,线条才会优美。而肌肉的结实就必须要靠运动来锻炼,因为,日常生活当中我们已经很少会使用到腰部肌肉,所以,必须靠外力的运动来锻炼,才会使腰部的线条更细更美。 此套腰部锻炼法除了是瘦腰之外,还可以将皮下脂肪的囤积去除。所以,除了将外在的腰部曲线锻炼好,更可以将内部的脂肪消除,真是一举二得的运动法唷! 腰部曲线运动 方法一:锻炼腰部结实 准备一张椅子,将左腿先放上,然后侧面向椅子的方向弯曲、伸直的动作,然后腰侧与背部之间的赘肉就会被拉扯到。二边各做10下在换边。 方法二:腹肌的锻炼 步骤一:平躺于床上或地板上,然后将一只腿抬高与地板呈90度 步骤二:将抬高的那一只腿,朝反相向移动,直到距离地面20公分为止。 方法三:增强腹部线条优美 步骤一:将单脚一口气往前跨出,然后伸出的大腿就尽可能的与地面平行。 步骤二:然后双手往后伸,靠住耳朵然后可以将上半身往后拉的姿势。然后呈静止状态3秒。 方法四:美化侧腰线条 两脚张开,与肩同宽。保持背部挺直,然后上半朝侧方弯曲,保持静止后,然后回复原来的姿势,朝另外一个方向。 方法五:维持腰部曲线 两脚张开与肩同宽,然后双手朝两侧平直伸展。然后右手变腰触碰左脚掌,左手弯腰触碰右脚掌。弯腰时要停留1-2秒钟,然后每边做5下后换边做。 腰部曲线塑身操 “腰”—是女性的性感指标之一,尤其在亲蜜爱人面前,这个部位有几两肉通通掩藏不住,现在请偷偷捏捏自己的腰身,如果感觉多了几寸几分,可别放著不管喔,这些小小的尺码很可能让您在爱人面前的魅力,少了1/4。 —重建腰身运动开始— (方法一) *背向墙壁站著,距离约30公分 *双脚打开与肩同宽 *上半身(当然包括头喽)向后转,以双手手掌都去碰到墙壁为标准 *左右两边轮流交替 *ps:下半身不能动,脚尖维持向前 *效果:可有效的消除腰部上侧和背部赘肉 (方法二) *身体站直,双脚打开与肩同宽 *两手伸直,在头顶上方互握 *身体尽量向上延伸(脚跟不可离地) *以数到5的速度向一边侧弯(身体尽量弯下) *再以数到10的速度回到原状 *两边互相交替做 *效果:能消除腰部两侧的赘肉,修饰曲线,让您更有腰身。 腰部健美操 腰是女性最能显示苗条体形的部位,腰背锻炼可以去除腰部多余的脂肪,增添活力。 (一)转腰8×8拍 预备姿势:分腿站立,两臂自然下垂。 第一个八拍: 1—8拍,上体向左侧扭振8次,同时左臂从体后抱右腿,右臂从体前抱左肩,头随之左转。 第二个八拍同第一个八拍,但方向相反。 第三个八拍: 1—2拍,重心移成左侧弓步,同时上体前屈向左扭转,左臂侧后举,掌心向后,右臂胸前屈,握拳,用肘关节触左膝,眼看前方。3—4拍同1—2拍,但重心移成右侧弓步,动作相反。 5—8拍同1—4拍。 第四个八拍同第三个八拍。 第五个八拍: 1—2拍,半蹲,两臂屈肘向上,上体向左侧扭转,还原。3—4拍、5—6拍、7—8拍同l一2拍。 第六个八拍同第五个八拍,但动作相反。 第七个八拍: 1—2拍,上体向左扭转,用右手触左脚。3—4拍同1—2拍,但方向相反。5—8拍同1—4拍。 第八个八拍同第七个八拍。 (二)腰部侧、前屈4×8拍 预备姿势:分腿站立,两臂自然下垂。 第一个八拍: 1—2拍,两臂上举,身体尽量向左侧屈,还原。3—4拍、5—6拍、7—8拍同1—2拍。 第二个八拍同第一个八拍,但方向相反。 第三个八拍: 1—2拍,两臂下举,身体尽量前屈,还原。3—4拍、5—6拍、7—8拍同1—2拍。 第四个八拍同第三个八拍,但上体向后屈。 (三)振腰4×8拍 预备姿势:俯卧。 第一个八拍: 1—2拍,肘撑地,上体后屈振动,还原。3—4拍、5—6拍、7—8拍同1—2拍。 第二个八拍: 1—2拍,侧卧,上体左屈振动,还原。3—4拍、5—6拍、7—8拍同1—2拍。 第三个八拍同第一个八拍。 第四个八拍同第二个八拍,但方向相反。 (四)绕腰4×8拍 预备姿势:分腿站立,两臂自然下垂。 第一个八拍: 1—8拍,上体前屈,两臂下举,腰部由左、后、有做360度水平绕环。 第二个八拍同第一个八拍,但方向相反。 第三、四个八拍同第一、二个八拍。 要求:腰部转动和振动绕环时幅度由小到大,速度均匀。 向小蛮腰看齐~腰部雕塑 检查:你的腰身走样了吗? 1.拿出布尺量量你的腰围和臀围。若腰围和臀围比大于0.8时,水筒腰可能已经找上你了。 2.以手轻捏腰侧的脂肪,如果能轻易捏起赘肉,就表示已有脂肪囤积的现象。 战前心理准备 凉夏一到,众家姐妹无不想尽办法把能露的地方小露一番,尤其是近年流行的小可爱装,正好可以让小肚脐出来见见人,但若是露出来的是一截水桶腰,可就大煞风景了。 腰部可说是身体曲线的关键点,腰身恰好者,即使胸前不够伟大、臀部不够翘挺,视觉效果上仍是曲线玲珑,反之,若是让水桶腰搭配姣好的上下围,非但三围突显不出更可能让视觉焦点集中在粗粗的腰际上,你还敢忽略腰部这一环节呢? 一般来说,腰部脂肪囤积最常发生在上腹凸起,和腰臀之间赘肉的产生,而且脂肪囤积过多,也是慢性病的高危险徵兆,不得不特别注意。想要"以腰见人"吗?重新找回你的标准三围,就是现在罗! 改造小蛮腰开步走 扭腰紧腹 口诀:扭扭腰再弯弯身、紧腹消脂一起来 方法: 1. 站立姿势,双脚打开与肩同宽,两手插腰。 2. 先缓慢吸气,将上半身向右扭转,维持十秒后慢慢吸气恢复原状后,再换边扭转。 3. 缓慢吐气将身体向右侧弯下,维持十秒钟后吸气恢复原状再换边弯身。 4. 扭腰及弯身动作可让腰腹产生拉伸的感觉,对于腰侧脂肪特别有效。 增加腰部柔软度 口诀:单手伸一伸、身段愈柔软 方法: 1. 站立姿势,双脚打开与肩同宽,双手自然垂在两侧。 2. 吐气时,身体以侧弯的方向,先将右手向下伸直,左手则自然弯曲,以帮助右手向下拉展。 3. 伸展到气吐尽后,再吸气恢复原状,换手再做。过程中要注意身体不可向前倾,若向下伸展无法超过膝盖,可能是腰部脂肪过厚或是柔软度欠佳造成。 修饰腰部线条 口诀:双手一起伸、美腰更加倍 方法: 1. 以站立姿势双脚打开与肩同宽,脚尖朝向正前方,双手向上伸直在头顶正上方交握。 2. 手保持在耳朵两侧,先将整个人尽量向上延伸,但脚跟不可离地,维持五秒左右。 3. 再将身体慢慢向左侧弯,须让侧腰用力并保持身体正面向前,接着再恢复原状,换边侧弯。 细腰柔软操 拥有纤细的腰围不只增添女人味,还可使你的曲线更为完美,衬托出匀称的骨架,只要多做细腰柔软操,你也可以达到。 以下的柔软操可运动到你腰围两侧的肌肉,改善松弛的脂肪。 步骤一: 背部挺直站立,两脚张开与肩同宽。 步骤二: 举起右手,身体向左侧弯曲到极限,静止几秒后恢复原状。 步骤三: 左右各做10次。 每天早上做些柔软操,不只可美化身段,让你紧绷的身体因体操而变柔软,也会让你睡意全消,有精神面对一天的忙碌。 以下的扭转运动,可帮助你运动到腰部肌肉,使腰围更纤细,但动作必须轻柔,绝不可过于激烈,以免伤到肌肉。 步骤一: 身体挺直站立,双脚张开与肩同宽。 步骤二: 双手向两旁伸直,身体呈现「大」字形。 步骤三: 身体下弯,以左手碰右脚尖,静止2秒。 步骤四: 重复以上动作,左右各做10次。 五分钟细腰体操 使腰变细的毛巾操 必备用品:毛巾一条 1、 双腿向前伸直坐正,臀部肌肉收紧。 2、 双手各持毛巾的一端,两臂向前伸直。 注意:肩膀不可用力,手臂不可弯曲。 3、 保持手持毛巾、手臂伸直的姿势,向左右转动,臀部也要同时迅速扭动。运动到稍微出汗为止,最少10次。运动时,脸朝向正前方,手臂要伸直。 注意:本节操对提臀也颇有效。 矫正肌关节及瘦腰运动 必备用品:一把椅子或其他固定物。 1、 仰躺着,手臂向头上方伸直,与身体在同一平面上。双手抓住椅子腿或其他固定物。 2、 膝盖弯曲,双脚撇向外侧,双腿呈M字形支撑于地板上。 3、 左膝向外张开静止5秒钟,恢复原来姿势的同时,右膝向外张开。左右重复各做30次。 矫正脊椎及瘦身伸展操 必备用品:长30厘米左右的毛巾一条,浴巾2条。 1、 将毛巾卷成小圆筒。将2条浴巾重叠,长的一边折三折,再卷成筒状。 2、 仰躺着,在颈部和腰部放卷好的毛贴,放松5-30分钟,腰部的毛贴要时常上下移动,效果会更好。 消除小腹赘肉的运动 1、 仰躺着,臀部紧缩,两脚分开与腰同宽。 2、 两腿尖向内侧靠拢,双手枕在脑后。 3、 边吐气,双腿边往上抬至离地5厘米高,并伸殿跟腱。两手支撑着头部往上抬,伸展颈部。充分伸展之后,吸气,憋住,直到憋不住时,恢复原来姿势,重复做10次。 注意:两脚尖靠在一起时应呈直角。 强化腹肌伸展操 1、 仰躺着,两臂两脚分开呈大字形。 2、 肩膀尽量贴着地面,上半身向左倾,伸展左脚跟腱。 3、 上半身保持不动,伸展跟腱往上抬10厘米,数1、2、3再迅速放下。左右各5次。 注意:本节操对大腿减肥也很效。 改善肥胖体质的运动1 1、 仰躺在地板上,臀部紧缩。双脚分开与腰同宽,一边伸展跟腱,一边脚尖向内倾斜。 2、 脚尖保持内倾姿势,做仰卧起坐10-20次。 注意:仰躺时,脚尖习惯朝外(外八字)的人要向内倾斜;反之,习惯朝内(内八字)的人要向外倾斜。脚尖自然向 上的人,就保持此姿势,伸展跟腱。 跟腱伸展了,仰卧起坐就轻松多了。不过,请别做得太过火,否则,腹肌容易疲劳。 改善肥胖体质的运动2 必备用品:毛巾一条,事先烫热的碗一只。 将事先烫热的碗反盖着,铺上毛巾。身体俯卧着,腹部贴在碗上面。保持这个姿势,做腹部深呼吸5-30分钟。 注意:碗可以稍微移动,使整个腹部都能碰触到。当腹部感觉不舒服时,别勉强,可缩短运动的时间。 臀部保健操 这套臀部肌肉练习操,可帮助您把臀部的肌肉绷紧,使臀部肌肉结实而有弹性,再现您矫健的身姿。 ①身体直立,双脚并拢。左腿向前弯曲90度,右腿向后成弓步。以左腿为支点用臀部肌肉的力量向下压,然后再换右腿,双腿交替各做8次为一组。休息5—10秒后,再做3组。 ②俯卧,慢慢抬起弯曲跪在地上的双腿,尽可能抬得高一些。做3组,每组16次,逐渐加大运动幅度。中间可休息5—10秒。 ③仰卧,双腿垫高,比如说,把腿放在低凳上。绷紧臀部肌肉,抬高骨盆,慢慢数到8,恢复原状。休息5—10秒后再做3组。 ④肘弯和膝盖着地,慢慢抬起再放下弯曲的腿,腰部挺直。双腿交替各做32次,休息5—10秒后再做1组。 塑造腰部柔美的线条 腰部若太粗,则造成圆筒状的身体,同时侧腹与胃部均易堆积脂肪。因此,在腰部容易囤积脂肪的地方,必须加紧努力运动。 扭摆运动可轻松进行,并可提高腰部的柔软性。如提高柔软性后,可增加肌肉与关节的运动范围,并使较弱的肌肉机能恢复。腰部即可拥有吸引人的魅力线条了。 什么样的腰才算标准呢?世界小姐选美的标准中,女性参赛者的平均尺寸为B91、W61、H91。即使不与其比较,但腰部仍比胸部或殿部更易受人注目。 1、小腰、侧腹的有效运动 以跪立资势,或坐在椅子上可以。双手撑于腰际,腰部向扭转。感觉有舒畅感时再返回作。左右各进行数次。 2、以节奏式的扭转,使腰部舒畅。 双手与肩同高并伸直,微侧身向方扭转。此时手臂与地板平行动。回转一次约,2~3秒左右各反复数次。 3、使侧腹紧张,腰部纤细。 对腰部有效的扭转运动。右手扶左腰,左手由后方持右腰,上半身充分扭转后静止不动。此时,意识集中于腹部。腰部不感到有手支撑的力量。脸部则朝与扭转方向的反侧。 使腰部细下来最佳方法 这是你从未想象过的姿势,因为它要你的躯干撑起来和克服地心引力, 你将使所有腹部肌肉得到锻炼-----尤其是斜肌-----还有你的后腰。(这可是使腰部纤细下来最佳的一举多得的方法啊!)注意在做动作时不要放松臀部。 A. 从左侧开始,举起你的大腿,臀部和躯干离开地面,用前臂和脚支撑你的重量。把腿向外伸,右脚交叠在左脚上。把右手放在脑后,肘部向上与肩成一条直线。 B. 慢慢的向前弯曲, 以右肘作指导直到它触及你指尖前面的地面。回到起始位置。重复3到5遍,换方向。 如何健美腰腹? 腰腹部是人体健美的重要部位,常言道:“腰肥而体笨,腰健而体美”,腰腹部如果缺乏锻炼就会变得肌肉松弛没有力量,同时还会造成大量脂肪的堆积,对于女性朋友尤为如此,因其生理特点,脂肪更易堆积于这些部位。那么,怎样锻炼才能保持体形,塑造“窈窕”的身段呢?本文就简单易行的原则,介绍几种锻炼腰腹部的健美方法: 一、左右压腿:取坐姿两腿分开(130度—150度),左手握左踝,右臂上举贴耳,以右臂带动上体向左侧压后还原。连续做8次,然后交换另侧,右手握右踝,左臂上举贴耳向右侧压8次。注意:上举臂应一直保持伸直姿态并与躯干在同一平面内,防止手臂弯曲并落于体前。 二、侧踢腿:侧卧。右小臂放平支撑上体,左手于体前辅助支撑。左右腿伸直并拢,上下重叠后,左腿直膝向侧上方踢(上踢腿与躯干在同一平面内,脚尖下绷,努力够头,上踢角度范畴在( 90度—150度),上踢到最大角度后慢慢还原。连续踢8次,然后换另侧,用同样的方法踢右腿8次。 三、仰卧举腿:仰卧并腿,两臂上举、两手抓牢物体使上肢固定,两腿伸直,脚尖下绷后,收腹吸气,直膝上举两腿与地面垂直,然后呼气慢慢地、有控制地将腿还原,如此连续做8次。 四、举腿交叉:并腿坐,上体后仰,两小臂支撑于体后。两腿伸直上举至60度—80度后,两腿分开1—2个肩宽,保持2秒钟,向内交叉使一腿在上,一腿在下,再保持2秒钟,如此分开交叉连续做4次后还原,注意:做本节操时,要始终保持两腿伸直的姿态。 五、俯卧起上体:取俯卧姿态,固定下肢不动,两手相握后背于腰部,背肌用力,使上体向上立起接近于垂直,再还原趴下,连续做8次。 六、放松腰腹:两手、两膝着地成跪撑姿势,首先收腹吸气,同时低头含胸,两臂伸直,使背部尽量向上“拱起”,保持2秒钟;其次塌腰呼气,同时抬头挺胸,两臂弯曲,使腰部尽量下沉,显出曲线,再保持2秒钟,如此反复“拱起、下塌”做8次。 我们知道,腰腹部的肌肉是由腹肌、腹外侧肌和背肌等组成,上述六种方法中,一、二种方法锻炼腹部侧肌肉;三、四种方法锻炼腹肌,第五种练习背肌;第六种锻炼和协调腹肌和背肌,依照上述方法锻炼时,应视个人的身体情况和生理反应来定运动量。如果做完后没有感觉到累,这说明运动量较小,可通过增加练习次数和时间来加大运动量;如果身体出现酸痛情况则可减小运动量,但不要停止练习,坚持一段时间,身体就会适应,然后再慢慢加大运动量。 轻松美丽腰肢法 这套腰腹减肥健身操,不仅简明易学,能独自在家里练习,而且效果明显。 直立,两腿分开与肩同宽,双手叉腰,向左侧扭转腰部,直至极限。向右侧做相同动作。连续做10~20个为一组,每天做3~4组。 直立,两腿分开与肩同宽,双手叉腰,分别向前、后、左、右弯腰屈体, 10~20个为一组,每天做3~4组。 站立,背对墙,两臂上举后伸,腰向后弯,两手扶墙渐渐下移,直至极限,每天做8次。 站立,两腿分开,两臂由前伸向上后举,头和上体随臂尽量后仰,至极限后转为向前,低头弯腰,两臂自然下垂,手尽量摸脚尖,注意,膝关节不得弯屈。 直立,双手叉腰,两腿分开。先按顺时针方向扭转腰部10圈,再按逆时针方向扭转腰部10圈,最后向前、后、左、右各弯腰屈体5次。 跪地,双手前支撑,像猫一样练习弓背,低头,腰部用力上拱。然后慢慢抬头,松腰背,使脊柱呈“U”形。弓背时深吸气,塌腰时呼气,每天做10~20个。 仰卧,双腿并拢屈膝,慢慢向左侧扭转双腿,直至左膝触地。再反方向做,注意,上身保持不动。每天做15~20个。 仰卧,以头脚为支撑点,腰臀尽量上挺,使身体面“桥”形,保持30秒后臀腰部下放。休息1分钟再做。每天起床时和睡觉前各做5次。 以上练习在慢跑或快走20分钟后做,效果更佳,只要持之以恒,你不公能缩减腰腹部多作的脂肪和赘肉,而且能增强腰部肌肉的弹性和脊椎的灵活性,纠正驼背,脊柱侧弯等不良体态。 美腰三部组合操 关于腰部塑身操之前我们已经介绍过一些,不知道有没有符合你的需求呢?不喜欢没关系,今天我们还有一套完整的腰部锻练组合法要告诉你。 这部美腰法除了能瘦腰,消除脂肪,还可以帮我们把腰部曲线练的更美,来试试吧! *美腰之结实篇* 1.面对椅子,将左腿放上 2.身体向左转至极限,再恢复原来动作 3.各做10次后换脚(边) *美腰之腹肌篇* 1.平躺在床上 2.将双腿举高与地板呈90度(如有困难可依自己能力而定) 3.之后慢慢的放下 4.每回做10次 *美腰之优美线条篇* 1.单脚往前跨(伸出的大腿尽可能与地面平行) 2.双手往头后方伸,将上半身往后拉 3.停约5秒 4.每回做10次 美化你的腰肢 这是为妇学员编制的一套腰腹站减肥健身操,不套操不仅简明易学,能独自在家里练习,而且效果明显,为许多腰粗的女性解除了烦恼。现将这套操介绍给大家。 直立,两腿分开与肩同宽,双手叉腰,向左侧扭转腰部,直至极限。向右侧做相同动作。连续做10-20个为一组,每天做3-4组。 直立,两腿分开与肩同宽,双手叉腰,分别向前、后、左、右弯腰屈体,10-20个为一组,每天做3-4组。 站立,背对墙,两臂上举后伸,腰向后弯,两手扶墙渐渐下移,直至极限,每天做8次。 站立,两腿分开,两臂由前伸向上后举,头和上体随臂尽量后仰,至极限后转为向前,低头弯腰,两臂自然下垂,手尽量摸脚尖,注意,膝关节不得弯屈。 直立,双手叉腰,两腿分开。先按顺时针方向扭转腰部10圈,再按逆时针方向扭转腰部10圈,最后向前、后、左、右各弯腰屈体5次。 跪地,双手前支撑,像猫一样练习弓背,低头,腰部用力上拱。然后慢慢抬头,松腰背,使脊柱呈“U”形。弓背时深吸气,塌腰时呼气,每天做10-20个。 仰卧,双腿并拢屈膝,慢慢向左侧扭转双腿,直至左膝触地。再反方向做,注意,上身保持不动。每天做15-20个。仰卧,以头脚为支撑点,腰臀尽量上挺,使身体面“桥”形,保持30秒后臀腰部下放。休息1分钟再做。每天起床时和睡觉前各做5次。 以上练习在慢跑或快走20分钟后做,效果更佳,只要持之以恒,你不公能缩减腰腹部多作的脂肪和赘肉,而且能增强腰部肌肉的弹性和脊椎的灵活性,纠正驼背,脊柱侧弯等不良体态 健美操让你轻松拥有小蛮腰 腰部是身体的中心,与体型的美丑有很大关系,要想使腰部长得匀称,给人一种健美的感觉,可以进行以下腰部健美操锻炼: 站在地上,两手叉腰,两腿分开,先自左向右扭转腰部,使身体转动,再自右向左扭转腰部, 使身体转动,左右各20次。 站在地上,两腿分开,腰部向前弯,先用右手摸左脚,再用左手摸右脚,各摸10次。 立在地上,背部靠近墙,两手自肩上扶助墙,身体尽量向后弯,头向后仰,一直弯到不能再弯为止,连续5次。 立在地上,两手叉腰,先使腰部向前弯,再使腰部向后弯,然后再分别向左、右弯,四个方向轮流弯腰,每个方向弯5次。 仰卧,屈膝至胸前,两臂向左右张开。肩膀、手背贴地。转动躯干向右,右膝盖碰地,两臂不动;转动躯干向左,左膝盖碰地,两臂不动。反复做10次。仰卧地上,双掌托盆骨,支起下身及腰部,足尖挺直,背、头及两臂着地。左右脚交替向头部屈下,膝盖不得弯曲,连续做16次。 防病又健美 腰部健美操 2001年04月24日10:29 新民晚报 时间保持一种站姿或坐姿,容易使腰椎关节僵硬、腰肌劳损。特别是有的人一到中年就经常腰酸背痛且腰部脂肪堆积。如果有时间,不妨做做腰部健美操,防病又健美。 屈腰 前屈时双腿并拢坐床上,用头触膝盖。后屈时俯卧床上,双手撑起上半身,尽量使上半身与腿的夹角接近90°。侧屈时两脚分开与肩宽站立,左手贴住左大腿向小腿下滑,右手同样。注意不要向前弯腰。各5下。 拧腰 仰卧床上,双手侧平伸与身体呈十字状,以腰带动一侧手去触另一侧手,尽可能腰部以下贴紧床面。各5次。然后双手枕于脑后,屈起右腿用膝盖触碰左边的床面,尽可能腰部以上贴紧床面,左腿同样。各5次。 旋腰 双脚分开与肩宽站立,两手相握伸直,身体前倾90°然后慢慢转成侧倾90°,后仰尽可能小于120°,再慢慢经另一侧的侧倾转到前倾位置。顺时针逆时针方向各3圈。 做腰部健美操要注意动作缓慢,量力而行,循序渐进,切勿操之过急或因动作猛烈而扭闪了腰。(张谨) 法式美腰健背操 人的体形是与生俱来的,但柔美的腰姿和挺拔的背部却要经过适当的锻炼才能获得。法国女郎素以体形优美著称于世,她们又是怎么做的呢?下面介绍的法式美腰健背操不受时间和空间的限制,简便实用,特别适合家庭操练。 美腰 美腰操的作用是拉伸腰部肌肉,使其变得更柔软。此操共有三节。 1.俯卧,用双手握住脚踝。在呼气的同时,使脚跟尽可能地接近臀部,大腿不离开地面,腰不弯;吸气,复原。连续做10次。 2.背挺直,左腿绷直,右脚伸到左腿外侧,右手放在身后,左手搭在右大腿上。呼气,将躯干和头向右转,胯骨不得抬起;吸气,躯干和头位复原。该动作反复做10次。 3.站立,背挺直,双手平按桌面上,右腿弯曲,骨盆对正桌子,固定不动。将左腿向右腿后边伸展,脚向外侧摆放,保持该姿势10秒钟并反复做10次。 建议:全神贯注做好每一个动作,以防止错误姿势。 健背 健背操旨在进行肩胛肌肉训练,使背部挺直。此操共有三节。 1.站直,双腿微岔,与肩同宽,背部挺直。吸气,胳膊从两侧向上举起,手掌张开向上。保持双臂上举的姿势,呼气,向后振臂,吸气,向前振臂。往复30次。 2.屈膝跪下,双手在背后交叉。呼气,鼻子缓慢前探,两臂尽可能高抬;吸气,身子挺直,两臂下压。做8次。 3.席地盘腿,肘部抬起,双手平展,探至肩胛骨之间。吸气,双肘后张;呼气,双肘前收。反复做8次。 建议:白天应保持背部挺直,久之,您会背直头正,体态优美。 打造窈窕小蛮腰 专门赶走因吃多、没做伸展操造成的粗腰。虽好运动的你运动量够,但是吃进去的比消耗掉的还多,运动前后又懒得做伸展操,才会变成没腰美人儿! 训练程度:*** 训练大纲:预防不知不觉吃过量食物 活动篇 运动前后的伸展操很重要,肌肉才不会硬硬没曲线!看看塑出腰部线条的简单伸展操很关键啊! 体操篇 1.先坐在地上,上半身向后倾斜45度角,双脚并拢膝盖弯曲90度,双手微弯贴在大腿上。 2.双手先向左侧大腿方向伸直。 3.左右两侧各维持4秒,持续15-20次。 瘦腰魔鬼体操2: 1.先全身放松的平铺,双脚合并,膝盖微弯,上半身保持不动,下半身向左侧躺约90度。 2.左右交替,各维持4秒,持续15-20次。 不要叫我水桶腰! 女人和男人体型上的差异,便是女性的胴体比男人更有曲线、明显,窈窕的曲线就是女人味的象征,尤其腰、臀间的起伏的曲线,更是引起人无限的想象;胸部的大、小各人喜好不同,但每个人对腰线的要求,却是有其一致的准则,纤细的小蛮腰正是那男人、女人梦寐以求的标准身段。 但要维持小蛮腰的身段却是难上加难,女人的腹部是最容易堆积脂肪的部位了,有些人就算全身纤细,但那藏在衣服下的小腹却大得吓人,另外还有人虽然没小腹,但脂肪全累积在腰侧,腰部一点曲线也没有,还不时被人笑是水桶腰的身材,真是自己都感到悲哀啊! 要改善水桶腰的缺点,首先要消除腰侧的脂肪,最好的运动方法,便是常做腰部扭转的运动,结实腰侧肌肉,侧拉腰部的体操,再加上手臂的运动,不只可雕塑你的腰线,还可纤细手臂喔! ※预备动作: 背部挺直站立、膝盖放轻松,双脚打开比肩稍宽,将双手向左右平行张开,手掌朝上、肘关节微弯。 步骤1: 左手保持固定不动,右手臂与上半身一起往左侧拉,尽量将右手掌拍到左手掌。 步骤2: 静止2秒后,回到预备动作。 ※实行方法: 一边做10次后,再换另一边做,共做3组。 8分钟纤细腰肢操 女性们都想拥有纤纤细腰,有细腰,才令身材玲珑,才体现上天恩赐的体态美。何况,在炎热的夏日,穿着轻薄的衣服或在沙滩上穿泳衣走动,假若腰肢过粗,会影响雅观呢。 就做一些令腰腹收缩的体操吧。这套控制腰肢的体操,只需8分钟,效果不错,若能坚持每天做一次,成绩会更好。 ①躺在地上,背要贴地,弯曲双膝,双手放在脑后,紧握,使双肘指向侧面。然后,将左膝向胸部伸去,背部要尽量不离地面,当身体快接触到左膝时,将它扭向右肘侧,放松回到原来位置。再换有膝左手肘重夏动作,共来回做10次。花两分钟可完成动作。 ②站直,双脚分开,距离为一个臀位,双手臂自然垂下。然后,右手按在右边腰部,身体倾斜向左边,尽可能弯去,但不必拉紧肌肉。记住,在做动作时,要令臀部保持向后方向,回到原来位置后,再换另一用手及方向,重复动作每边各做10次,共2O次,需时两分钟。 ③站立,双脚又开如②,向前伸出双臂,由手肘处弯曲,从腰以上扭转身体,先发后右,保持臀部向后,左右各扭转10 次,两分钟共做20次。 ④躺下,弯曲双膝。 双臂在身体两侧伸直,然后提升头部,双肩尽量离地,但不能拉紧,如此保持定型动作数秒钟,放松,再重复做10次。共历时两分钟。 本文转自kenty博客园博客,原文链接http://www.cnblogs.com/kentyshang/archive/2006/11/06/551832.html如需转载请自行联系原作者 ‘ ‘kenty
批量去水印是个邪恶的技术,以老赵的衣橱为例子就更邪恶了。之所以以衣橱为例子而不以时尚网为例子,是标题需要。实际上,时尚网的水印批量去除效果更佳。闲话少说,摆原理,上图片(代码就不上了)。 ====================================== 通过观察可以发现,如果以图片的右下角为基准,很多网站图片的水印位置是固定的,因此,可以通过以下步骤去除: 第一步:制定一张mask图片,这张图片标记了水印的位置。 mask图片可以用photoshop或其它工具做。我最初的想法是让程序智能提取,输入两张带有水印的图片,根据水印区域相似性来找到水印区。这个思路实现起来比较难,试了几个方法,都不理想。还是用photoshop扣掉非水印区域省心。 下面是我做的“衣橱”的mask图片,其中非白色的像素便是水印区: 第二步:对于要去水印的图片,根据mask图片,通过程序抠掉其中的水印区域。这一步可以省略,抠不抠都不影响最终结果。放在这里是为了把原理讲的更清晰些。经过第二步,我们得到了一张局部残缺的图片。 第三步:图片复原。图片复原是一种图像处理技术,英文叫Image Inpainting。原理很复杂,网上的论文大把抓,有兴趣的可以去看看。 我用的是OpenCV的图像复原函数。先看看OpenCV图像复原的演示,对这个技术有个直观认识。这是一张残缺的照片: 复原后的照片: 效果很棒。:P 我按上面思路写了一个 demo,拿老赵的衣橱做实验,结果如下: 换张图片试试: 再换张: 水印去掉了,复原后的图片,仔细看,还能看出痕迹。mask模板再制作精良点,选择更好的算法估计效果会好一些。 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2009/12/19/1627669.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
assert的初步认识 assert宏指令是用来诊断程序是否有误的,函数原型如下 void assert(int expression) 那为什么我们要使用assert而不用printf呢?因为assert可以帮我们监测出是哪个条件不满足了,而且可以直观地显示出是代码中的哪一行出问题了,并且,当我们不打算用assert来诊断程序的时候,可以很方便地通过宏定义#define NDEBUG 取消assert的诊断。相比于自己写一个一个printf,自己判断程序哪一行出错,显然assert更有优势。 其实assert的时候很简单,因为他的使用就跟if一样,assert(expression);expression其实就相当于if里面的表达式。来一段代码 #include <assert.h> int main(){ int i=0; assert(i>0); //这里我们故意让assert不成立看看结果 printf("打印出i的值是:%d\n",i); //看看是否能执行到这一步 return 0; } 运行结果: 图1 运行结果 从程序里面看出,我们可以直观地看出哪一行出错了,并且是条件’i>0’不成立出错的;并且当assert诊断出错后,程序就退出了。这在调试的时候很有用,但是当实际投入使用的时候,我们就要把assert(expression)诊断函数禁用掉,毕竟稳定性很重要。我们可以很方便地在文件头加上宏定义#define NDEBUG 取消assert的诊断。 #define NDEBUG #include <assert.h> 注意:#define NDEBUG要在#include <assert.h>之前,不然禁用assert不会成功。运行结果如图2. 图2 禁用assert之后 可以看出,即使条件不成立,也不会再有assert的诊断信息了。并且很执行到printf那行。 assert的进一步讨论 当assert诊断失败后,assert会向stderr打印消息。从图1可以看出,assert诊断信息的表达形式是:Assertion failed: 表达式(expression),程序(file) 出错的文件名(file name), 行号(line nnn)。然后,asset会调用abort中断函数的执行,源代码的文件名(The Source filename)和行号(line number)定义在预处理宏(preprocessor macros)__FILE__ 和 __LINE__中。 关于assert的用法总结与注意事项,点此。 参考 《The C Programming Language》 本文转自陈哈哈博客园博客,原文链接http://www.cnblogs.com/kissazi2/p/3142864.html如需转载请自行联系原作者 kissazi2
最近做的一个项目,我最开始选用的是 MySql 5.0 数据库,项目提交之后,对方要求换成 MS SQLServer 2000,还好数据层操作基本采用的是标准的SQL语句,也未使用存储过程,于是将原有的数据接入层代码改写成泛型类,只改了不到一百行代码,具体的SQL操作语句一句未动,便实现了数据库之间的切换。下面简述: 数据库不是很复杂,因此我采用了两个类:(1) DataProvider 泛型类 public class DataProvider<ConnType, CmdType> where ConnType : IDbConnection, new() where CmdType : IDbCommand,new () { } 提供数据库表的Insert,Update,Select,Delete操作。 因为 IDbCommand能够由 IDbConnection 获取,其实只需要有一个泛型参数ConnType 的,不过这样以来,具体的代码改动比较大,偶就采用了两个泛型参数。(2)ConnectionPool 泛型类 public class ConnectionPool<T> where T : IDbConnection,new () { } 最开始没在MySql 5.0 中找到对数据库连接池的支持,于是自己写了一个简单的数据库连接池。 下面,就是采用 C# 的NB语法――using:(1)如果使用 MySql 数据库: using DataProvider = DataProvider<MySql.Data.MySqlClient.MySqlConnection, MySql.Data.MySqlClient.MySqlCommand>; (2)如果使用 SQLServer 数据库: using DataProvider = DataProvider<System.Data.SqlClient.SqlConnection, System.Data.SqlClient.SqlCommand>; 这样一来,其它地方的代码一句都不用动了。 懒惰是程序员的美德。我的一台电脑上装的是 SQLServer 2000数据库,一台电脑上装的是 MySql数据库,我经常一会在这台电脑上干活,一会在另外一台电脑上干活,两个电脑上的代码应该一致啊。因此,采用预编译指令: #if MSSQLSERVERusing System.Data.SqlClient;using DataProvider = DataProvider<System.Data.SqlClient.SqlConnection, System.Data.SqlClient.SqlCommand>;#elif MYSQLusing MySql.Data;using DataProvider = DataProvider<MySql.Data.MySqlClient.MySqlConnection, MySql.Data.MySqlClient.MySqlCommand>;#endif 这样在编译时指定条件编译符号 " MYSQL " 得到的就是MySql版本的代码,指定条件编译符号" MSSQLSERVER ",得到的就是 SQLServer 版本的代码。 其它: (1)理论上来说,采用反射得到的解决方案更完美,不过那样工期会更长,没必要啦啦啦啦啦啦。。。。。。。。。。。。。 (2)数据库设计,偶采用的是免费软件 Toad Data Modeler 免费版,里面提供了数据库切换功能,切换过去,稍微改动改动。 (3)这次开发工具采用的全是开源软件或者免费软件,都是超级好用的东东,感觉开发速度并不比庞大的收费软件慢。用到的工具如下: IDE:VS 2005 Express (C++版,C#版,Web开发版),.Net的aspnet命令行编译工具 版本管理:SVN,小乌龟SVN Client Shell:Windows Power Shell(这玩意既然出来了,就要充分利用) UML建模:StarUML(功能强大的开源UML,比偶以前用过的JUDE,ArgoUML强大很多,支持C#) 数据库建模:Toad Data Modeler 免费版 数据库管理工具:EMS SQL Manager 2005 lite for MySQL 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2007/01/02/610121.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
现在很多网页都是由数据库自动生成的,数据分散在html代码之中:有的位于URL链接中,有的位于<td></td>之中,有的位于javascript代码之中.如何挖掘这些数据为我所用?小的不才,最近写了一个网络数据库挖掘程序,挖掘了几千万条数据.源代码不能公开,这里简单述说一下设计思路和基本结构吧. 本来是用.net写的,写了几天,因为找不到好的c#的html解析器,最后还是改成了java.在这里,我尽量从语言中性的角度来解释设计思路和关键点所在,就算是小项目分析吧,供大家参考.设计目的:解析类如 http://xxx.xxx.xxx.xxx/xxx.xxxx?xxxxx={keyword}&xxxx=xxxx&xxxxxx={page}&xxxxxxx之类的网页. 这类网页一下特点:1,根据id或keyword由数据库动态生成 2,每id或keyword针对1页或多页页面,可以通过翻页来浏览.翻页逻辑体现在url或内部html代码中. 3,每一页面有1条或多条数据,每条数据可根据一定的字符串模式匹配. 差不多大部分网络数据库都有这些特点,下面是一个例子: 软件结构如下图: 各部分角色如下: (1) 控制器:调度线程,轮询线程,调度html解析器,抽取数据生成实体,调度数据接入逻辑,控制关键词生成逻辑,控制翻页逻辑 (2) 线程调度:生成线程,中止线程. (3) html解析器:指定URL地址,负责获取页面,把页面解析成相应的NodeList. (4) 实体:实体类,体现了实体逻辑. (5) 数据存储:将已经获得的实体数据存储入数据库 下面逐一介绍,控制器逻辑最复杂,放在最后. (1) html解析器 html解析器我找的是开源实现.找到几个.net的html parser,老感觉不好用.接着又找java的,先找到了JSpider,看了几天,觉得不能满足我的需求,最后找到htmlParser,决定用这个. 用到的htmlParser功能很简单:给出一个URL地址,生成一个parser,parser访问页面,根据过滤器类型,解析成一个个的NodeList,如,包含<td>节点的NodeList,包含link的NodeList........使用很简单. 1Parser htmlParser = new Parser(urlString); //创建Parser:2NodeList allList =htmlParser.parse(null); //获得所有节点3NodeList tdList = allList.extractAllNodesThatMatch(new NodeClassFilter(TableColumn.class),true); //获得td节点 htmlParser可以设置cookies. 因为htmlParser是调用block IO,所以需要在虚拟机上设置ConnectTimeout和ReadTimeout,不设置的话,一旦网络慢下来,总会有几个线程在傻等.我觉得都设置为30秒比较合适. (2) 实体 因为我要用OR-Mapping,所以就单独提取一个实体层出来.根据要挖掘的数据类型可构造出实体类. 这个就不详说了. (3) 数据存储 采用OR-Mapping. 对于网络数据库,所设计的数据表的个数不多,于是偶将数据库访问逻辑再封装在类DatabaseHelper中.DatabaseHelper作为数据层的Facade,所有上层数据访问必须通过DatabaseHelper进行. DatabaseHelper有一个静态变量 private static boolean DEBUG = false; (c#格式: private static bool DEBUG = false)另外有一个方法: 1 public static void Debug()2 {3 DEBUG = true;4 } 调用DatabaseHelper.Debug()方法可以将DatabaseHelper设置为调试状态,所有读取数据库操作照常,只是不进行实质性的写入数据库操作.开发过程中因为要经常调试,为了不污染数据库,特意设计这个东东. (4) 线程调度 采用Worker Thread模式.见偶的blog <调度模式·Worker-Channel-Request>. 控制器不断的向channel中放入Request, 工作线程获取并执行Request. (5) 控制器 控制器由6个重要接口IDispatcher, IDispatcheHelper, ISpider, ISpiderHelper, IHandler, IDigger组成.每一个接口有对应的抽象类骨架,分别为: Dispatcher, DispatcheHelper, Spider, SpiderHelper, Handler, Digger. 带helper的都是可能调用DatabaseHelper的类. 下面详细介绍这些接口和基础类的功能: IDispatcher 与Dispatcher: IDispatcher主要有dispatch(),dispatch(Object key), registAfterCrawled(ISpider spider)三个方法. 运行dispatch(),则默认扫表网络数据库的所有的keywords, dispatch(Object key)只扫描数据库的指定的keyword. 针对每个keyword, Dispatcher将产生相应的ISpider,ISpider扫描完毕后通过registAfterCrawled(ISpider spider)通知Dispatcher. 具体的指派逻辑在Dispatcher中实现.主要逻辑如下: (a) 通过IDispatcheHelper获得需要指派给Spider的keywords(存入ElementsSet)和以往已指派抓取完毕的keywords(存入DispatchedSet). (b) 通知Channel,开启全部工作线程.Dispatcher的构造函数Dispatcher(int threadCount),可指定开启的工作线程数. (c) 再产生一个轮询线程,逐一轮询工作线程,查看线程执行状态. (d) 遍历ElementsSet,对于其中的keyword,如果不在DispatchedSet之中,则指派keyword进行扫描 (e) 对于指派的keyword,产生一个Spider,包装成Request,放入Channel中,供工作线程执行. (e) 如果没有需要调度Request,则通知Channel,没Request了,工作线程执行完Channel上的Requests后自动中止. IDispatchHelper与DispatchHelper IDispatchHelper的主要方法是getDispatchedSet()和getElementsSet(),获得需要指派给Spider的keywords(存入ElementsSet)和以往已指派的keywords(存入DispatchedSet). IDispatchHelper还有两个方法: isDispatched(Object key)和commit(Object key), 前一个用来查询某个keyword是否已指派抓取完成,后一个主要是供Dispatcher调用,在指派完一个Spider,Spider完成后通过调用registAfterCrawled,向ElementsSet中注册,表明已指派完该keyword. getDispatchedSet()和getElementsSet(),可以从数据库中生成, 也可以从文件中读取,也可以是根据某些逻辑条件生成. ISpider, Spider , ISpiderHelper与SpiderHelper ISpider与Spider的角色是根据指定的keyword,获取该keyword的所有查询页面的数据,生成实体,并存储入数据库. Spider包装在Request中,一个线程一次只能调用一个Request,也就是一个线程一次只能执行一个Spider. ISpider的主要方法是crawl(),负责所有的爬行逻辑和后续操作,具体逻辑封装在. Spider之中. 1个Spider拥有1个SpiderHelper和1个Handler. SpiderHelper主要作用是(1)从数据库中获取该keyword已经抓取的纪录CrawledSet(因为可能由于网络原因,有的Spider抓了一半,就停止了,但数据库中已经抓了不少纪录);(2) 通过dump(digger)将digger抓取的数据存储入数据库.Hander的作用是(1)判断是否还有下一页,(2)构建当前页的URL,根据URL产生Parser,由Parser产生Digger. crawl()的代码骨干如下: 1 IDigger digger;2 while ((digger = this.handler.next()) != null) {3 this.helper.dump(digger);4 }5 this.helper.saveRecord(); this.helper.saveRecord()作用更新数据库中数据,表明该keyword对应数据已经抓取完毕.这样,当再次运行程序, IDispatchHelper. getElementsSet()就不会包含该Spider所对应的keyword了. IHandler, Handler IHandler 的主要方法是IDigger.next(),获得下一页所对应的IDigger.不存在则返回null. Hander有几个主要的抽象方法: 根据页数构造URL--buildUrlString(int pageage), 根据所构造的URL构造Parser--buildParser(), 根据Parser构造Digger--buildDigger(). IHandler .next()根据Parser所返回来的NodeList判断是否存在下一个页面(具体的判断逻辑由具体类实现),如果有则根据下一页的页数,重新一次调用buildUrlString(int pageage), buildParser(),buildDigger(),返回IDigger. IDigger 与 Digger IDigger 与 Digger主要作用是分析Parser所抓获解析所得的页面NodeList,解析成实体对象. IDigger的主要方法是获取实体--ArrayList dig()和获取当前页面的URL--String getUrlString().Digger提供了protected NodeList getTdList(),protected NodeList getLinkList(),...........等方法,供具体类调用. 具体的解析逻辑就在Digger的具体类中的实现了. 进一步的做法是从中提出一般性框架出来,然后还需要一套规则体系.就看有没时间了.:P 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2005/12/06/291569.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
网上的视频很多都是分片的flv文件,怎么把他们合为一体呢?GUI工具就不考虑了,不适合批量执行,不适合在后台运行。有没有命令行工具或库可以实现呢? ffmpeg 提供了一个方法: (1)先把flv文件转换成mpeg; (2)将多个mpeg文件合并成1个独立的mpeg文件(二进制合并即可) (3)将独立的mpeg文件转换成独立的flv文件。 网上搜到的最多的也是这种解决办法。这种方法有两个缺点: (1)需要两遍转码,非常耗时; (2)转换后的独立的mpeg文件比原视频要短一点点。 木有办法了,只好另寻他路。有人说有一个flvmerge.exe 程序可以将多个flv合并成一个,可惜的是俺搜了很久,都没找到这个程序,最后还是在一款免费软件里把这个“flvmerge.exe”文件给揪出来了,不幸的是,这个“flvmerge.exe”得不到正确的结果。 润之同学说过,自己动手,丰衣足食。上 github 上搜“flvmerge”,发现两个项目,“flvmerge”和“flvmerger”,都是C写的。前者不依赖于第三方库,后者依赖于第三方库,那么就从第一个开始吧。 看了看它的代码,知道了flv文件合并的原理: (1) flv 文件由1个header和若干个tag组成; (2) header记录了视频的元数据; (3) tag 是有时间戳的数据; (4) flv合并的原理就是把多个文件里的tag组装起来,调整各tag的时间戳,再在文件起始处按个头部。 下面是我参照 flvmerge 项目,用linqpad写的一个C#版本的 flvmerge 代码: 1 void Main() 2 { 3 String path1 = "D:\\Videos\\Subtitle\\OutputCache\\1.flv"; 4 String path2 = "D:\\Videos\\Subtitle\\OutputCache\\2.flv"; 5 String path3 = "D:\\Videos\\Subtitle\\OutputCache\\3.flv"; 6 String output = "D:\\Videos\\Subtitle\\OutputCache\\output.flv"; 7 8 using(FileStream fs1 = new FileStream(path1, FileMode.Open)) 9 using(FileStream fs2 = new FileStream(path2, FileMode.Open)) 10 using(FileStream fs3 = new FileStream(path3, FileMode.Open)) 11 using(FileStream fsMerge = new FileStream(output, FileMode.Create)) 12 { 13 Console.WriteLine(IsFLVFile(fs1)); 14 Console.WriteLine(IsFLVFile(fs2)); 15 Console.WriteLine(IsFLVFile(fs3)); 16 17 if(IsSuitableToMerge(GetFLVFileInfo(fs1),GetFLVFileInfo(fs2)) == false 18 || IsSuitableToMerge(GetFLVFileInfo(fs1),GetFLVFileInfo(fs3)) == false) 19 { 20 Console.WriteLine("Video files not suitable to merge"); 21 } 22 23 int time = Merge(fs1,fsMerge,true,0); 24 time = Merge(fs2,fsMerge,false,time); 25 time = Merge(fs3,fsMerge,false,time); 26 Console.WriteLine("Merge finished"); 27 } 28 } 29 30 const int FLV_HEADER_SIZE = 9; 31 const int FLV_TAG_HEADER_SIZE = 11; 32 const int MAX_DATA_SIZE = 16777220; 33 34 class FLVContext 35 { 36 public byte soundFormat; 37 public byte soundRate; 38 public byte soundSize; 39 public byte soundType; 40 public byte videoCodecID; 41 } 42 43 bool IsSuitableToMerge(FLVContext flvCtx1, FLVContext flvCtx2) 44 { 45 return (flvCtx1.soundFormat == flvCtx2.soundFormat) && 46 (flvCtx1.soundRate == flvCtx2.soundRate) && 47 (flvCtx1.soundSize == flvCtx2.soundSize) && 48 (flvCtx1.soundType == flvCtx2.soundType) && 49 (flvCtx1.videoCodecID == flvCtx2.videoCodecID); 50 } 51 52 bool IsFLVFile(FileStream fs) 53 { 54 int len; 55 byte[] buf = new byte[FLV_HEADER_SIZE]; 56 fs.Position = 0; 57 if( FLV_HEADER_SIZE != fs.Read(buf,0,buf.Length)) 58 return false; 59 60 if (buf[0] != 'F' || buf[1] != 'L' || buf[2] != 'V' || buf[3] != 0x01) 61 return false; 62 else 63 return true; 64 } 65 66 FLVContext GetFLVFileInfo(FileStream fs) 67 { 68 bool hasAudioParams, hasVideoParams; 69 int skipSize, readLen; 70 int dataSize; 71 byte tagType; 72 byte[] tmp = new byte[FLV_TAG_HEADER_SIZE+1]; 73 if (fs == null) return null; 74 75 FLVContext flvCtx = new FLVContext(); 76 fs.Position = 0; 77 skipSize = 9; 78 fs.Position += skipSize; 79 hasVideoParams = hasAudioParams = false; 80 skipSize = 4; 81 while (!hasVideoParams || !hasAudioParams) 82 { 83 fs.Position += skipSize; 84 85 if (FLV_TAG_HEADER_SIZE+1 != fs.Read(tmp,0,tmp.Length)) 86 return null; 87 88 tagType = (byte)(tmp[0] & 0x1f); 89 switch (tagType) 90 { 91 case 8 : 92 flvCtx.soundFormat = (byte)((tmp[FLV_TAG_HEADER_SIZE] & 0xf0) >> 4) ; 93 flvCtx.soundRate = (byte)((tmp[FLV_TAG_HEADER_SIZE] & 0x0c) >> 2) ; 94 flvCtx.soundSize = (byte)((tmp[FLV_TAG_HEADER_SIZE] & 0x02) >> 1) ; 95 flvCtx.soundType = (byte)((tmp[FLV_TAG_HEADER_SIZE] & 0x01) >> 0) ; 96 hasAudioParams = true; 97 break; 98 case 9 : 99 flvCtx.videoCodecID = (byte)((tmp[FLV_TAG_HEADER_SIZE] & 0x0f)); 100 hasVideoParams = true; 101 break; 102 default : 103 break; 104 } 105 106 dataSize = FromInt24StringBe(tmp[1],tmp[2],tmp[3]); 107 skipSize = dataSize - 1 + 4; 108 } 109 110 return flvCtx; 111 } 112 113 int FromInt24StringBe(byte b0, byte b1, byte b2) 114 { 115 return (int)((b0<<16) | (b1<<8) | (b2)); 116 } 117 118 int GetTimestamp(byte b0, byte b1, byte b2, byte b3) 119 { 120 return ((b3<<24) | (b0<<16) | (b1<<8) | (b2)); 121 } 122 123 void SetTimestamp(byte[] data, int idx, int newTimestamp) 124 { 125 data[idx + 3] = (byte)(newTimestamp>>24); 126 data[idx + 0] = (byte)(newTimestamp>>16); 127 data[idx + 1] = (byte)(newTimestamp>>8); 128 data[idx + 2] = (byte)(newTimestamp); 129 } 130 131 int Merge(FileStream fsInput, FileStream fsMerge, bool isFirstFile, int lastTimestamp = 0) 132 { 133 int readLen; 134 int curTimestamp = 0; 135 int newTimestamp = 0; 136 int dataSize; 137 byte[] tmp = new byte[20]; 138 byte[] buf = new byte[MAX_DATA_SIZE]; 139 140 fsInput.Position = 0; 141 if (isFirstFile) 142 { 143 if(FLV_HEADER_SIZE+4 == (fsInput.Read(tmp,0,FLV_HEADER_SIZE+4))) 144 { 145 fsMerge.Position = 0; 146 fsMerge.Write(tmp,0,FLV_HEADER_SIZE+4); 147 } 148 } 149 else 150 { 151 fsInput.Position = FLV_HEADER_SIZE + 4; 152 } 153 154 while(fsInput.Read(tmp, 0, FLV_TAG_HEADER_SIZE) > 0) 155 { 156 dataSize = FromInt24StringBe(tmp[1],tmp[2],tmp[3]); 157 curTimestamp = GetTimestamp(tmp[4],tmp[5],tmp[6],tmp[7]); 158 newTimestamp = curTimestamp + lastTimestamp; 159 SetTimestamp(tmp,4, newTimestamp); 160 fsMerge.Write(tmp,0,FLV_TAG_HEADER_SIZE); 161 162 readLen = dataSize+4; 163 if (fsInput.Read(buf,0,readLen) > 0) { 164 fsMerge.Write(buf, 0, readLen); 165 } else { 166 goto failed; 167 } 168 } 169 170 return newTimestamp; 171 172 failed: 173 throw new Exception("Merge Failed"); 174 } 测试通过,合并速度很快! 不过,这个方法有一个缺点:没有将各个文件里的关键帧信息合并,这个关键帧信息,切分flv文件时很重要,合并时就没那么重要了。如果确实需要的话,可以用 yamdi 来处理。 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/p/3441030.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
在《dotnet程序优化心得》一文中我主要采用DateTime.Now.Ticks来测量时间,文中我说DateTime.Now.Ticks能够得到精确的时间,Ninputer指出这是一个错误的说法,DateTime.Now测不准ms级时间。我测试了一下,结果确实如此。测试代码如下: 1 2static void Main(string[] args) 3{ 4 long start,end; 5 6 start = DateTime.Now.Ticks; 7 end = DateTime.Now.Ticks; 8 9 while(start==end) 10 { 11 end = DateTime.Now.Ticks; 12 } 1314 Console.WriteLine("DateTime.Now.Ticks时间精度:{0}ms",(end-start)/10000); 15}16 运行结果:DateTime.Now.Ticks时间精度:10ms yinh找到了一个程序QueryPerfCounter,可以进行精确的时间测量。代码如下: 1using System; 2using System.ComponentModel; 3using System.Runtime.InteropServices; 4 5public class QueryPerfCounter 6{ 7 [DllImport("KERNEL32")] 8 private static extern bool QueryPerformanceCounter( 9 out long lpPerformanceCount); 1011 [DllImport("Kernel32.dll")] 12 private static extern bool QueryPerformanceFrequency(out long lpFrequency); 1314 private long start; 15 private long stop; 16 private long frequency; 17 Decimal multiplier = new Decimal(1.0e9); 1819 public QueryPerfCounter() 20 { 21 if (QueryPerformanceFrequency(out frequency) == false) 22 { 23 // Frequency not supported 24 throw new Win32Exception(); 25 } 26 } 2728 public void Start() 29 { 30 QueryPerformanceCounter(out start); 31 } 3233 public void Stop() 34 { 35 QueryPerformanceCounter(out stop); 36 } 3738 public double Duration(int iterations) 39 { 40 return ((((double)(stop - start)* (double) multiplier) / (double) frequency)/iterations); 41 } 42}43 我搜了一下,该程序原出处应该是http://channel9.msdn.com/wiki/default.aspx/PerformanceWiki.HowToTimeManagedCode 采用以下代码测试其时间精度: 1static void Main(string[] args) 2{ 3 QueryPerfCounter q = new QueryPerfCounter(); 45 q.Start(); 6 q.Stop(); 78 Console.WriteLine("QueryPerfCounter时间精度:{0}ns",q.Duration(1)); 9} 结果: QueryPerfCounter时间精度:3911.1116077602ns 如果测试之前这么来一下: // Call the object and methods to JIT before the test run QueryPerfCounter myTimer = new QueryPerfCounter(); myTimer.Start(); myTimer.Stop(); 结果大概是1微妙。 也就是说QueryPerfCounter的测试精度是1微妙,这主要是因为方法调用和执行要花去一些时间。为了得到更精确的时间测量,必须对此进行时间校准,扣除方法调用和执行的时间.我在构造函数里面加了校准项。完整代码如下: 1using System; 2using System.ComponentModel; 3using System.Runtime.InteropServices; 4 5 public class QueryPerfCounter 6 { 7 [DllImport("KERNEL32")] 8 private static extern bool QueryPerformanceCounter( 9 out long lpPerformanceCount); 1011 [DllImport("Kernel32.dll")] 12 private static extern bool QueryPerformanceFrequency(out long lpFrequency); 13 14 private long check;15 private long start; 16 private long stop; 17 private long frequency; 18 Decimal multiplier = new Decimal(1.0e9); 1920 public QueryPerfCounter() 21 { 22 if (QueryPerformanceFrequency(out frequency) == false) 23 { 24 // Frequency not supported 25 throw new Win32Exception(); 26 }27 28 check = 0;2930 QueryPerformanceCounter(out start);31 QueryPerformanceCounter(out stop);3233 QueryPerformanceCounter(out start);34 QueryPerformanceCounter(out stop);35 check+=stop-start;36 } 3738 public void Start() 39 { 40 QueryPerformanceCounter(out start); 41 } 4243 public void Stop() 44 { 45 QueryPerformanceCounter(out stop); 46 } 4748 public double Duration(int iterations) 49 { 50 return ((((double)(stop - start-check)* (double) multiplier) / (double) frequency)/iterations); 51 } 52 }53 采用下面这个方法测试校准效果: 1 static void TestCheckPrecision() 2 { 3 QueryPerfCounter q; 4 5 int loop = 10000; 6 int exCount = 0; 7 8 for (int i=0;i<loop;i++) 9 {10 q = new QueryPerfCounter();11 q.Start();12 q.Stop();13 if(q.Duration(1)!=0)14 {15 exCount++;16 Console.WriteLine("QueryPerfCounter时间精度异常:{0}ns",q.Duration(1));17 }18 }1920 Console.WriteLine("共进行{0}次QueryPerfCounter的时间精度测试",loop);21 Console.WriteLine("其中{0}次QueryPerfCounter的时间精度异常",exCount);22 Console.WriteLine("时间校准有效性{0}%",100-(100.0*exCount)/loop);23 }24 运行结果: QueryPerfCounter时间精度异常:-838.095344520044ns QueryPerfCounter时间精度异常:-558.730229680029ns QueryPerfCounter时间精度异常:-558.730229680029ns QueryPerfCounter时间精度异常:-838.095344520044ns QueryPerfCounter时间精度异常:-838.095344520044ns QueryPerfCounter时间精度异常:-838.095344520044ns QueryPerfCounter时间精度异常:-838.095344520044ns QueryPerfCounter时间精度异常:-558.730229680029ns QueryPerfCounter时间精度异常:-838.095344520044ns QueryPerfCounter时间精度异常:-838.095344520044ns QueryPerfCounter时间精度异常:-558.730229680029ns QueryPerfCounter时间精度异常:-558.730229680029ns QueryPerfCounter时间精度异常:-838.095344520044ns QueryPerfCounter时间精度异常:-345854.012171938ns QueryPerfCounter时间精度异常:279.365114840015ns QueryPerfCounter时间精度异常:-55314.2927383229ns QueryPerfCounter时间精度异常:279.365114840015ns QueryPerfCounter时间精度异常:5307.93718196028ns QueryPerfCounter时间精度异常:279.365114840015ns QueryPerfCounter时间精度异常:279.365114840015ns QueryPerfCounter时间精度异常:279.365114840015ns 共进行10000次QueryPerfCounter的时间精度测试 其中22次QueryPerfCounter的时间精度异常 时间校准有效性99.78% 也就是说99.78%情况下都得到了很好的校准,只有极少数情况校准会出现异常现象不为零,有时这一波动甚至很大,到了5.3微妙。这个结果很满意了,因为异常的测试结果可以很简单的发现出来。但是是不是说明,校准后的类的测量误差就差不多就是纳秒级的呢?我后来又做了一个测试发现不是这样,测试代码如下: 1 static void TestPrecision() 2 { 3 QueryPerfCounter q; 4 5 for(int j=0;j<200;j++) 6 { 7 q = new QueryPerfCounter(); 8 9 q.Start();10 q.Stop();1112 Console.WriteLine(""); 1314 Console.WriteLine("QueryPerfCounter时间精度:{0}ns",q.Duration(1)); 1516 q.Start();17 int i=0;18 for(i=0;i<j;i++)19 {20 int l;21 }22 q.Stop();2324 Console.WriteLine("第{0}次循环,耗时{1}ns",i,q.Duration(1));25 }26 }27 对于循环 for(i=0;i<j;i++) { int l; } 当j很小的时候,耗时为0ns,随着n逐渐增大,耗时会一下子跳到279.365114840015ns,然后是558.730229680029ns。我摘几条结果: QueryPerfCounter时间精度:0ns 第73次循环,耗时0ns QueryPerfCounter时间精度:0ns 第74次循环,耗时279.365114840015ns QueryPerfCounter时间精度:0ns 第75次循环,耗时0ns QueryPerfCounter时间精度:0ns 第76次循环,耗时279.365114840015ns QueryPerfCounter时间精度:0ns 第88次循环,耗时558.730229680029ns QueryPerfCounter时间精度:0ns 第89次循环,耗时279.365114840015ns 也就是说,对我的机器现在配置来说(1.5G迅驰,2003 server,.net framework 1.1),测量的最基本时间单位是279.365114840015ns,不能得出更精确的时间了。 因此,推荐的测试方法是采用校对后的QueryPerfCounter,多测量几次,去掉最高值,去掉最低值(这两个一定要去掉),然后取平均值,这样能够得到非常精确的测量结果,测量误差约为0.3微妙。 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2005/08/16/216015.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
二、类型管理 1、程序集与类型的管理 在Context初始化时便将AppDomain中的类型全部加载并交给TypeManager管理: public Context() { …… TypeManager = new TypeManager(); Assemblys = new Dictionary<String, Assembly>(); Assembly[] al = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly a in al) { AddAssembly(a); } AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(CurrentDomain_AssemblyLoad);…… }private void AddAssembly(Assembly a) { if (a != null) { Assemblys.Add(a.FullName, a); Type[] tl = a.GetTypes(); foreach (Type t in tl) { if(!t.FullName.StartsWith("<PrivateImplementationDetails>")) TypeManager.AddType(t); } } } void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args) { Assembly a = args.LoadedAssembly; if (!Assemblys.ContainsKey(a.FullName)) { AddAssembly(a); } } 开发时发现,程序集中有一批类型名字以"<PrivateImplementationDetails>"开头的类型,貌似时临时类型,这些东西数量较多,干脆把它屏蔽掉了。 2、进出命名空间 在CdClassCmdHandler 中实现,目前不支持级联操作,即:cdc ..;cdc .; cdc namespaceName这样是可以的,cdc ../ns1/ns2 这样是不支持的。输入的命名空间名称可以只是部分,程序自动进行匹配,如只有1个匹配项则自动进入该项,否则不进行操作,同时打印所有匹配项。 3、列出命名空间和类型 在 ListClassCmdHandler 中实现,支持正则表达式匹配。幕后工作由TypeDictionary在做: Context.TypeManager.Now.ListDir(match); Context.TypeManager.Now.ListType(match); public void ListType(String match) { Regex re = null; if (match != null) { re = new Regex(match); } foreach (Type t in Types.Values) { String name = t.Name; if (re != null) { if (!re.IsMatch(name)) continue; } Console.WriteLine("C:\t" + Context.EnsureAtLeastLength(name,20) + "\t" + t.FullName); } } public void ListDir(String match) { Regex re = null; if (match != null) { re = new Regex(match); } foreach (TypeDictionary dic in SubTypeDictionary.Values) { String name = dic.Name; if (re != null) { if (!re.IsMatch(name)) continue; } Console.WriteLine("N:\t" + Context.EnsureAtLeastLength(name, 20) + "\t" + dic.FullName); } } 4、查看类型 扩展方法确实是好东西,有了它这里实现起来很简单。在 ClassExtensionMethods里 实现: public static class ClassExtensionMethods{ …… public static void methods(this Type t) { foreach (MethodInfo mi in t.GetMethods()) { Console.WriteLine(" " + mi); } } public static void methods(this Object obj) { if (obj == null) return; methods(obj.GetType()); } public static void props(this Type t) { foreach (PropertyInfo pi in t.GetProperties()) { Console.WriteLine(" " + pi); } } public static void props(this Object obj) { if (obj == null) return; props(obj.GetType()); } public static void members(this Type t) { foreach (MemberInfo mi in t.GetMembers()) { Console.WriteLine(" " + mi); } } public static void members(this Object obj) { if (obj == null) return; members(obj.GetType()); } public static void creaters(this Type t) { foreach (ConstructorInfo ci in t.GetConstructors()) { Console.WriteLine(" " + ci); } } public static void creaters(this Object obj) { if (obj == null) return; creaters(obj.GetType()); } } 三、执行代码片断 在 CscCmdHandler 中实现。核心方法为 CscCmdHandler.Run(),代码如下: public override void Run() { if (String.IsNullOrEmpty(InputCmdString)) return; String fullCmd = String.Empty; if (Context.TypeManager.Now != Context.TypeManager.Root) { fullCmd += " using " + Context.TypeManager.Now.FullName + ";"; } fullCmd +=@" using System; using System.IO; using System.Text; using System.Collections.Generic; using Orc.Shell.Core; namespace Orc.Shell.Core.Dynamic { public class DynamicClass { public Orc.Shell.Core.Context Context; public void Save(String name, Object obj) { Context.Save(name,obj); } public Object My(String name) { return Context[name]; } public void MethodInstance(Context context) { Context = context; " + InputCmdString + @"; } } }"; CompilerResults cr = Context.CodeProvider.CompileAssemblyFromSource(Context.CompilerParameters, fullCmd); if (Context.Debug) { Console.WriteLine("Source:"); Console.WriteLine("--------------------------------"); Console.WriteLine(fullCmd); Console.WriteLine("--------------------------------"); Console.WriteLine("Results"); } if (cr.Errors.HasErrors) { Console.WriteLine("编译错误:"); foreach (CompilerError err in cr.Errors) { if (Context.Debug) { Console.WriteLine(String.Format("line {0}: {1}", err.Line, err.ErrorText)); } else { Console.WriteLine(err.ErrorText); } } } else { Assembly assem = cr.CompiledAssembly; Object dynamicObject = assem.CreateInstance("Orc.Shell.Core.Dynamic.DynamicClass"); Type t = assem.GetType("Orc.Shell.Core.Dynamic.DynamicClass"); MethodInfo minfo = t.GetMethod("MethodInstance"); minfo.Invoke(dynamicObject, new Object[] { Context }); } } 其中 CodeProvider,CompilerParameters 在 Context 中初始化: CodeProvider = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } }); CompilerParameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll", "Orc.Shell.Core.dll", "OrcShell.exe" }); CompilerParameters.GenerateExecutable = false; CompilerParameters.GenerateInMemory = true; 可以通过 Save(String name, Object obj) 和 My(String name) 来同环境进行交互。其中,Save(String name, Object obj) 是将代码片断中的对象 obj 保存为环境变量,变量名称为 name。My(String name)取出名称为name 的环境变量,加载到代码段上。my 指令可以查看所有环境变量。 采用$name的方式操作环境变量更简介、直观,但这样一来代码难度加大不少,没想到什么简洁的实现,就没采用。 四、其它 1、扩展方法 对于常用的方法通过扩展方法来方便使用。如,打印一个对象 obj 到控制台上,正常写法是System.Console.WriteLine(obj.ToString()),比较麻烦,通过扩展方法,可以使它简化为:obj.p();相关代码如下: public static class ClassExtensionMethods { public static void Print(this Object obj) { Console.WriteLine(obj); } public static void p(this Object obj) { Print( obj ); } public static void P(this Object obj) { Print(obj); } public static void print(this Object obj) { Print(obj); } …… } 2、变量缩写(Alias) 指令缩写可明显降低操作量。可通过编辑程序集目录下的 Alias.xml 文件来添加、删除或更改指令缩写。 Alias 指令可以查看目前的指令缩写。 五、缺乏的功能。 到现在为止,OrcShell只实现了Shell的雏型。由于只开发了一个晚上,测试也不是很完善,另外许多重要功能还未涉及,主要包括: 1、手动加载程序集; 2、常用系统管理功能,如常用的Shell 指令; 3、远程控制; 4、指令的自动完成。 留待后续。 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2008/02/29/1085834.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
写小程序比较flash和silverlight的性能。和我预料的完全不一样(我原以为sl要比flash高) 测试发现:整数操作、浮点数操作两个差不多。方法调用,flash比silverlight要高5倍左右。 可以放心去写AS3的图像处理库了,看来性能不是问题。 还是成熟的东东好啊。 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2010/01/06/1640055.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
1、顺序结构描述的队列 在这一章中,我们选择用Rear队尾的指针来指示最后一个入队的元素在队列中的位置。我们选择队列内存储的数组的Data[0]作为队头,每次取数据的时候,队头弹出(out)。具体的代码如下所示: using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DataStructure.Queue { class SeqQueue<T>: IQueue<T> { int Rear =-1;//队尾 constint MaxSize=100;//队列的最大容量 T[] Data =new T[MaxSize];//一个数组 public SeqQueue() { } public SeqQueue(T[] data) { Array.Copy(data,this.Data,data.Length); Rear = data.Length -1; } publicint GetLength(out States states) { if(Rear==-1) { states = States.Fail; return0; } else { states = States.Success; return Rear +1; } } publicbool IsEmpty() { return Rear ==-1?true:false; } publicvoid Clear() { Rear =-1; } privatebool isFull(){ return Rear +1== MaxSize ?true:false; } publicvoid In(T item,out States states) { if(isFull()) { states = States.Fail; return; } this.Data[++Rear]= item; states = States.Success; } public T Out(out States states) { //判断队列是否为空 if(IsEmpty()) { states = States.Fail; returndefault(T); } States mStates; //当队列中只有一个元素的时候 if(this.GetLength(out mStates)==1) { Rear--; states = States.Success; returnthis.Data[0]; } //普通的情况 //(1)将所有元素往前移动一位 T DataToOut=this.Data[0]; for(int i =1; i <this.GetLength(out mStates); i++) { this.Data[i -1]=this.Data[i]; } --Rear; states = States.Success; //(2)返回要弹出的值 return DataToOut; } public T GetFront(out States states) { if(this.IsEmpty()==true) { states = States.Fail; returndefault(T); } else { states= States.Success; return Data[0];//返回队头的元素 } } } } 从代码的实现上看,这种实现的方式有一种明显缺点:那就是每次当我们出队一个元素的时候,我们都需要将除队头外的元素全部往前移动一个,这样子的时间复杂度为O(n)。我们将在循环队列中讨论相应的解决方案。 2、循环队列 在循环队列中,比较困难的地方就是在队列长度和是否为满的判断。由于这方面的教程很多,大家可自行百度。另外,队头(Front)和队尾(Rear)的初始化位置(比如以下代码中是Front=0,Rear=0,如果改为Front=0,Rear=-1呢?)将会极大地影响代码的书写。 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DataStructure.Queue { /// <summary> /// 循环队列 /// </summary> class CSeqQueue<T>: IQueue<T> { int Front =0;//队头 int Rear =0;//队尾 constint MaxSize =4;//队列的最大容量 T[] Data =new T[MaxSize];//一个数组 public CSeqQueue() { } public CSeqQueue(int[] data) { Array.Copy(data,this.Data, data.Length); Rear = data.Length -1; } publicint GetLength() { return(Rear - Front + MaxSize)% MaxSize; } publicbool IsEmpty() { returnthis.GetLength()==0?true:false; } publicvoid Clear() { Front =0;//队头 Rear =0;//队尾 } publicbool isFull() { return(Rear +1)% MaxSize == Front ?true:false; } publicvoid In(T item,out States states) { if(isFull()) { states = States.Fail; return; } this.Data[Rear]= item; Rear =(Rear +1)% MaxSize; states = States.Success; } public T Out(out States states) { if(IsEmpty()) { states = States.Fail; returndefault(T); } T dataToOut =this.Data[Front]; Front =(Front +1)% MaxSize; states = States.Success; return dataToOut; } public T GetFront(out States states) { thrownew NotImplementedException(); } } } 2、链队 由单链表的实现,我们可知单链表有一个Head字段,通过Head字段我们可以遍历单链表。在链队中,我们打算直接利用Head字段,只不过我们将其命名Front。通常当我们需要遍历一个单链表时,我们得从链表头遍历都最后,才能找到链表尾。这样操作的话,时间复杂度为O(n),我们打算引用Rear字段,用来指示每次新入队的元素。 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DataStructure.Queue { class LinkQueue<T>:IQueue<T> { class Node<T> { private T data; /// <summary> /// 数据域 /// </summary> public T Data { get {return data;} set { data = value;} } private Node<T> next; /// <summary> /// 引用域 /// </summary> public Node<T> Next { get {return next;} set { next = value;} } //头结点构造函数 public Node(Node<T> node) :this(default(T), node) { } //普通结点构造函数 public Node(T data, Node<T> node) { this.Data = data; this.Next = node; } /// <summary> /// 空构造函数 /// </summary> public Node() :this(default(T),null) { } //尾结点构造函数 public Node(T data) :this(data,null) { } } private Node<T> Front;//队头 private Node<T> Rear;//队尾 publicbool IsEmpty() { return Front ==null?true:false; } publicvoid Clear() { Front =null; } publicvoid In(T item,out States states) { if(IsEmpty()) { Front =new Node<T>(item); states = States.Success; Rear = Front; } else { Node<T> newNode=new Node<T>(item); Rear.Next = newNode; Rear = newNode; states = States.Success; } } public T Out(out States states) { if(this.IsEmpty()) { states = States.Fail; returndefault(T); } else { states = States.Success; T DataToOut =this.Front.Data; Front = Front.Next; return DataToOut; } } public T GetFront(out States states) { if(this.IsEmpty()) { states = States.Fail; returndefault(T); } else { states = States.Success; returnthis.Front.Data; } } publicint GetLength() { if(IsEmpty()) { return0; } int Lenth=0;//长度 for(Node<T> last = Front; last.Next !=null; last=last.Next) { Lenth++; } return Lenth; } } } 本文转自陈哈哈博客园博客,原文链接http://www.cnblogs.com/kissazi2/p/3203428.html如需转载请自行联系原作者 kissazi2
思考下面代码 1 public class SampleActivity extends Activity { 2 3 private final Handler mLeakyHandler = new Handler() { 4 @Override 5 public void handleMessage(Message msg) { 6 // ... 7 } 8 } 9 } 如果没有仔细观察,上面的代码可能导致严重的内存泄露。Android Lint会给出下面的警告: In Android, Handler classes should be static or leaks might occur. 但是到底是泄漏,如何发生的?让我们确定问题的根源,先写下我们所知道的 1、当一个Android应用程序第一次启动时,Android框架为应用程序的主线程创建一个Looper对象。一个Looper实现了一个简单的消息队列,在一个循环中处理Message对象。所有主要的应用程序框架事件(如活动生命周期方法调用,单击按钮,等等)都包含在Message对象,它被添加到Looper的消息队列然后一个个被处理。主线程的Looper在应用程序的整个生命周期中存在。 2、当一个Handle在主线程被实例化,它就被关联到Looper的消息队列。被发送到消息队列的消息会持有一个Handler的引用,以便Android框架可以在Looper最终处理这个消息的时候,调用Handler#handleMessage(Message)。 3、在Java中,非静态的内部类和匿名类会隐式地持有一个他们外部类的引用。静态内部类则不会。 那么,到底是内存泄漏?好像很难懂,让我们以下面的代码作为一个例子 1 public class SampleActivity extends Activity { 2 3 private final Handler mLeakyHandler = new Handler() { 4 @Override 5 public void handleMessage(Message msg) { 6 // ... 7 } 8 } 9 10 @Override 11 protected void onCreate(Bundle savedInstanceState) { 12 super.onCreate(savedInstanceState); 13 14 // 延时10分钟发送一个消息 15 mLeakyHandler.postDelayed(new Runnable() { 16 @Override 17 public void run() { } 18 }, 60 * 10 * 1000); 19 20 // 返回前一个Activity 21 finish(); 22 } 23 } 当这个Activity被finished后,延时发送的消息会继续在主线程的消息队列中存活10分钟,直到他们被处理。这个消息持有这个Activity的Handler引用,这个Handler有隐式地持有他的外部类(在这个例子中是SampleActivity)。直到消息被处理前,这个引用都不会被释放。因此Activity不会被垃圾回收机制回收,泄露他所持有的应用程序资源。注意,第15行的匿名Runnable类也一样。匿名类的非静态实例持有一个隐式的外部类引用,因此context将被泄露。 为了解决这个问题,Handler的子类应该定义在一个新文件中或使用静态内部类。静态内部类不会隐式持有外部类的引用。所以不会导致它的Activity泄露。如果你需要在Handle内部调用外部Activity的方法,那么让Handler持有一个Activity的弱引用(WeakReference)以便你不会意外导致context泄露。为了解决我们实例化匿名Runnable类可能导致的内存泄露,我们将用一个静态变量来引用他(因为匿名类的静态实例不会隐式持有他们外部类的引用)。 1 public class SampleActivity extends Activity { 2 /** 3 * 匿名类的静态实例不会隐式持有他们外部类的引用 4 */ 5 private static final Runnable sRunnable = new Runnable() { 6 @Override 7 public void run() { 8 } 9 }; 10 11 private final MyHandler mHandler = new MyHandler(this); 12 13 @Override 14 protected void onCreate(Bundle savedInstanceState) { 15 super.onCreate(savedInstanceState); 16 17 // 延时10分钟发送一个消息. 18 mHandler.postDelayed(sRunnable, 60 * 10 * 1000); 19 20 // 返回前一个Activity 21 finish(); 22 } 23 24 /** 25 * 静态内部类的实例不会隐式持有他们外部类的引用。 26 */ 27 private static class MyHandler extends Handler { 28 private final WeakReference<SampleActivity> mActivity; 29 30 public MyHandler(SampleActivity activity) { 31 mActivity = new WeakReference<SampleActivity>(activity); 32 } 33 34 @Override 35 public void handleMessage(Message msg) { 36 SampleActivity activity = mActivity.get(); 37 38 if (activity != null) { 39 // ... 40 } 41 } 42 } 43 } 静态和非静态内部类的区别是比较难懂的,但每一个Android开发人员都应该了解。开发中不能碰的雷区是什么?不在一个Activity中使用非静态内部类, 以防它的生命周期比Activity长。相反,尽量使用持有Activity弱引用的静态内部类。 译文链接 本文转自陈哈哈博客园博客,原文链接http://www.cnblogs.com/kissazi2/p/4121852.html如需转载请自行联系原作者 kissazi2
Slider是常用控件。Adobe官方提供的控件中,fl库所带的控件不好用,便有兄弟《自制Flash Slider滚动条》。Flex库中也提供有Slider控件,但是Flex控件太重量级,动不动就是一二百K的尺寸,很多场合没法用。并且,Flex的Slider也不容易定制,比如,它的highlight track比track要低1像素,这在很多情况下是不合适的。 本文修改MinimalComps项目的Slider控件代码,提供一个小巧的Slider控件解决方案,本方案具有以下优势: (1)小巧。不依赖于fl控件或Flex库,使用本控件不会让程序尺寸增加多少; (2)容易定制。很容易定制 highlight track、track和thumb部分; (3)可数据绑定。 MinimalComps项目提供了简单的Slider控件,该控件源代码为: View Code 然而,MinimalComps的Slider控件的外观(http://www.minimalcomps.com/?page_id=5)很简单,见下图。 由于外观直接写死了,没办法换皮肤,因此,难以用于生产环境。 下面是我的改写版本: View Code BaseComponent类见我的其它博文:《只学一点点:我的技术学习策略 》(http://www.cnblogs.com/xiaotie/archive/2011/12/10/2283384.html)和《使用铁哥SmartFlash快速开发方案:66行代码搞定抽奖程序!》(http://www.cnblogs.com/xiaotie/archive/2011/06/15/2081386.html)。 下面演示Slider控件的使用: View Code 在线演示: 从上到下,可以看到三个Slider: 第一个Slider的皮肤由三个形状组成,track是黑色矩形,highlightTrack是蓝色矩形,thumb是圆。highlightTrack和track都规定了高度,这个高度,就是显示出来的highlightTrack和track高度,如果想让highlightTrack和track变粗,改变它的高度即可。Thumb规定了长和宽,如果修改thumb的大小,改变它的长宽即可,当然,如果长宽不等,则就是椭圆而非圆形了。 第二个Slider是由三个图片组成的,以应对复杂精致的皮肤,所使用的三个图片如下: 使用不同的图片,便是不同的Slider外观。 第二个Slider和第一个Slider还有个不同,就是ignoreThumbSize属性为true。第一个Slider的thumb是圆形,把thumb拖到两端可以发现,thumb遮盖不住两端(如果是方形的thumb则没这个问题),因此,引入了ignoreThumbSize字段。ignoreThumbSize字段默认为false,当ignoreThumbSize为true时,以thumb的中线为定位基准,这样便可避免当thumb是圆形时移动到两端,盖不住track两端的情况。 第三个Slider的orientation设为vertical,因此,便是竖着的Slider。 Slider可供数据绑定,比如: <gc:Label x="130" y="50" autoSize="true" text="{slider1.value}" /> 通过这样简单的代码便可使slider1的值改变时,对应的Lable的文本也发生变化。 是不是既小巧、又易用又灵活呢? 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2012/02/11/2346956.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
2周前将机器上的SQL Server 2005给删了,删除的原因是:基本不用,占的空间又大。今天看了首页的番茄家园相关文章,顺手又将机器上的MSDN给删了。目前正在下载VC# 2008 Express,等下载完了,再将VS2008给删了,从此告别.Net盗版开发环境。.Net是很优秀的东东,即使全用非D版免费软件,我们也能得到很大的生产力。以前曾经在Linux下冰天雪地裸体开发C#程序,开发工具和记事本差不多,编译用命令行,就这样开发效率比java还要高。全用免费版工具,比起使用D版,当然会有一些牺牲,但这牺牲不会很大,同时,还能收获更多,收获的是你对系统的理解,收获的是设计能力。一、运行环境操作系统:一般品牌机自带,专业版的稍微贵点,也就贵一天、两天、三天的工资而已。 Runtime, SDK:免费 文档:.Net下最主要的文档时类库文档。MSDN里99%时间我在翻类库文档。类库文档SDK自带。其它的文档,到微软msdn网站上去找二、数据库 数据库:目前我主要用的是mysql。相比较,sqlserver太大,看着就不爽,能不用就不用,实在要用让公司或客户出钱。 数据库设计:我用的是Toad Data Modeler Freeware,免费的,很好用。支持主流数据库,自动生成sql和文档。 数据库管理:EMS SQL Manager lite for Mysql,也是免费的,很好用。EMS这一系列工具都很棒。 三、开发环境 IDE: 备选方案1--VS 2008 Express。在使用VS2008 Team System版过程中,我发现它比VS 2008 Express多的那些功能,我基本都没用过。重构我也只用过rename,其它的重构项用的不很理想。 备选方案2--SharpDevelop。项目大了可能不稳定。 备选方案3--Eclipse + emonic + nant。Eclipse很多地方比VS2008好用。但emonic不成熟,目前虽有自动提示功能,不过不好用,另外,不支持debug。 备选方案4--nant + 文本编辑器。目前没好的文本编辑器,不过可以考虑把SharpDevelop中的那个剥离出来。 目前我选择方案1。方案3,4的路还长,但发展成熟了,结合Code Generator插件,也是很有生产力的开发方案。版本管理: 服务器:SVN 客户端:TortoiseSVN。其实控制台客户端也挺好用的。 UML:以前用的是jude,2M大。现在没怎么用了。四、制作安装程序 NSIS + EclipseNSIS。开源软件,非常好用,感觉比VS2008自带的要好用很多。第一,NSIS是脚本驱动的,很有柔性。第二,EclipseNSIS提供了图形化制作界面,对于通用的安装程序的制作,非常简单。 下面是一个简单的Demo: 六、录制Flash录像 Wink,免费。 总结: (1)除了操作系统,其它都不要钱,并且主要的功能都有。(2)除了操作系统,其它都很好用很好用。(3)小就是美。这些工具都很小。大多几兆几十兆的。现在看见动辄几百M几G的软件我都头大。WOW除外。上述工具,google一下就找到了,不给出处了。 BTW. 这篇blog是用notepad++写的。 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2008/08/20/1272621.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
MVC框架的一个很重要的优势在于可拓展性很高。权限的管理在每一个Web应用程序中都非常重要,虽然微软提供了Membership的默认权限设置,但在更多的情况下,Membership默认的权限设置并不能满足我们实际的需要。 下面本文将用一种简单的办法来自定义权限。 在MVC框架中,属性常用来限定控制器(Controller)的访问。所以我们首先从AuthorizeAttribute类中继承一个自定义的权限类。 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.Mvc; 6 7 namespace MvcApplication_AuthorizeAttribute.Models 8 { 9 public class MyAuthAttribute : AuthorizeAttribute 10 { 11 // 只需重载此方法,模拟自定义的角色授权机制 12 protected override bool AuthorizeCore(HttpContextBase httpContext) 13 { 14 if (!httpContext.User.Identity.IsAuthenticated)//判断用户是否通过验证 15 return false; 16 string[] StrRoles = Roles.Split(',');//通过逗号来分割允许进入的用户角色 17 if (string.IsNullOrWhiteSpace(Roles))//如果只要求用户登录,即可访问的话 18 return true; 19 bool isAccess = JudgeAuthorize(httpContext.User.Identity.Name, StrRoles); 20 if (StrRoles.Length > 0 && isAccess) //先判断是否有设用户权限,如果没有不允许访问 21 return false; 22 23 return true; 24 } 25 /// <summary> 26 /// 根据用户名判断用户是否有对应的权限 27 /// </summary> 28 /// <param name="UserName"></param> 29 /// <param name="StrRoles"></param> 30 /// <returns></returns> 31 private bool JudgeAuthorize(string UserName, string[] StrRoles) 32 { 33 string UserAuth = GetRole(UserName); //从数据库中读取用户的权限 34 return StrRoles.Contains(UserAuth, //将用户的权限跟权限列表中做比较 35 StringComparer.OrdinalIgnoreCase); //忽略大小写 36 } 37 38 39 40 // 返回用户对应的角色, 在实际中, 可以从SQL数据库中读取用户的角色信息 41 private string GetRole(string name) 42 { 43 switch (name) 44 { 45 case "aaa": return "User"; 46 case "bbb": return "Admin"; 47 case "ccc": return "God"; 48 default: return "Fool"; 49 } 50 } 51 } 52 } 以上的代码只是示例而已。你可以将实际的权限控制逻辑写在自定义的权限控制类(MyAuthAttribute)里面。如果在特定的业务过程中,用户没有访问权限,就返回false。然后我们要做的就是把这个类属性放在要控制的控制器(Controller)或者Action上面。代码如下所示。 1 [MyAuth(Roles = "User")] 2 public ActionResult About() 3 { 4 return View(); 5 } 这样,我们就完成了简单的自定义权限了。 本文示例代码 参考链接 本文转自陈哈哈博客园博客,原文链接http://www.cnblogs.com/kissazi2/archive/2013/01/12/2857992.html如需转载请自行联系原作者 kissazi2
git log --pretty=oneline --author="xxxx" -(n) 仅显示最近的 n 条提交 --since,--after 仅显示指定时间之后的提交 --until,--before 仅显示指定时间之前的提交 --author 仅显示指定作者相关的提交 --committer 仅显示指定提交者相关的提交 --reverse 按时间倒序显示 --p 按补丁格式显示每个更新之间的差异 --stat 显示每次更新的文件修改统计信息 --shortstat 只显示 –stat 中最后的行数修改添加移除统计 --name-only 仅在提交信息后显示已修改的文件清单 --name-status 显示新增、修改、删除的文件清单 --abbrev-commit 仅显示 SHA-1 的前几个字符,而非所有的 40 个字符 --relative-date 使用较短的相对时间显示(比如,“2 weeks ago”) --graph 显示 ASCII 图形表示的分支合并历史 --pretty 使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式) 本文转自demoblog博客园博客,原文链接http://www.cnblogs.com/0616--ataozhijia/p/4194115.html如需转载请自行联系原作者 demoblog
在《C#模板编程(1):有了泛型,为什么还需要模板?》文中,指出了C#泛型的局限性,为了突破这个局限性,我们需要模板编程。但是,C#语法以及IDE均不支持C#模板编程,怎么办呢?自己动手,丰衣足食,编写自己的C#预处理器。 一、C#预处理机制设计 问题的关键就是在C#的源文件中引入include机制,设计下面的语法: (1) 引入:#region include <path> #endregion (2) 被引:#region mixin … #endgion 例子:假设A.cs需要引用B.cs中的代码。A文件内容为: XXX#region include "B.cs"#endregion XXX B.cs 文件内容为: YYY#region mixin MMM#endregion ZZZ 运行预处理器,对A文件进行处理,生成第三个文件A_.cs: XXX MMM XXX 二、实现 编写预处理器:Csmacro.exe[Csmacro.zip](意思是CSharp Macro)程序,代码如下: Csmacro 编译之后,放在系统路径下(或放入任一在系统路径下的目录)。然后,在VS的项目属性的Build Events的Pre-build event command line中写入“Csmacro.exe $(ProjectDir)”,即可在编译项目之前,对$(ProjectDir)目录下的所有cs程序进行预处理。 Csmacro.exe 对于包含#region include <path> #endregion代码的程序xx.cs,预处理生成名为 xx_Csmacro.cs的文件;对于文件名以"Csmacro.cs”结尾的文件,则不进行任何处理。 使用时要注意: (1)#region include <path> 与 #endregion 之间不能有任何代码; (2)#region mixin 与 #endgion 之间不能有其它的region (3)不支持多级引用 三、示例 下面,以《C#模板编程(1):有了泛型,为什么还需要模板?》文尾的例子说明怎样编写C#模板程序: (1)建立一个模板类 FilterHelper_Template.cs ,编译通过: FilterHelper_Template.cs 这里,我使用了命名空间Hidden,意思是这个命名空间不想让外部使用,因为它是模板类。 (2)编写实例化模板类 ImageU8FilterHelper.cs ImageU8FilterHelper.cs 注意:这里使用 partial class 是为了使代码与预处理器生成的代码共存,不产生编译错误。 (3)编译项目,可以发现,预处理器自动生成了代码文件ImageU8FilterHelper_Csmacro.cs,且编译通过: ImageU8FilterHelper_Csmacro.cs 四、小结 这样一来,C#模板类使用就方便了很多,不必手动去处理模板类的复制和粘帖。虽然仍没有C++模板使用那么自然,毕竟又近了一步。 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2010/03/25/1694278.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
我的Eclipse版本:3.0.11,下载Mingw.exe,安装(假定安装在C:]MinGW),设置环境变量: PATH = C:\MinGW\bin;%PATH% LIBRARY_PATH = C:\MinGW\lib C_INCLUDE_PATH = C:\MinGW\include CPLUS_INCLUDE_PATH = C:\MinGW\include\c++\3.2.3;C:\MinGW\include\c++\3.2.3\mingw32;C:\MinGW\include\c++\3.2.3\backward;C:\MinGW\include2,下载org.eclipse.cdt-2.1.1-win32.x86.zip,安装。注意,CDT 2.*适用于Eclipse 3.0.*。CDT3.*适用于Eclipse 3.1.*3,打开eclipse,进入可以创建C/C++项目了!写一个helloworld程序会发现不能build。因为Eclipse是调用make命令构件,而C:\MinGW\bin下只有mingw32-make.exe。把mingw32-make.exe改成make.exe就行了。简单使用了一下,感觉比VS亲切。 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2005/10/02/248076.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
reference to : http://trickyandroid.com/gradle-tip-1-tasks/ http://blog.csdn.net/lzyzsd/article/details/46934187 今天要讲的就是Gradle tasks以及task的配置和运行。可能有的读者还不了解Gradle task,用真实的例子来展示应该更容易被理解。下面的代码展示了三个Gradle task,稍后会讲解这三者的不同。 task myTask { println "Hello, World!" } task myTask { doLast { println "Hello, World!" } } task myTask << { println "Hello, World!" } 我的目的是创建一个task,当它执行的时候会打印出来”Hello, World!”。当我第一次创建task的时候,我猜测应该是这样来写的: task myTask { println "Hello, World!" } 现在,试着来执行这个myTask,在命令行输入gradle myTask,打印如下: user$ gradle myTask Hello, World! :myTask UP-TO-DATE 这个task看起来起作用了。它打印了”Hello, World!”。 但是,它其实并没有像我们期望的那样。下面我们来看看为什么。在命令行输入gradle tasks来查看所有可用的tasks。 user$ gradle tasks Hello, World! :tasks ------------------------------------------------------------ All tasks runnable from root project ------------------------------------------------------------ Build Setup tasks ----------------- init - Initializes a new Gradle build. [incubating] .......... 等等,为什么”Hello, World!”打印出来了?我只是想看看有哪些可用的task,并没有执行任何自定义的task! 原因其实很简单,Gradle task在它的生命周期中有两个主要的阶段:配置阶段 和 执行阶段。 可能我的用词不是很精确,但这的确能帮助我理解tasks。 Gradle在执行task之前都要对task先进行配置。那么问题就来了,我怎么知道我的task中,哪些代码是在配置过程中执行的,哪些代码是在task执行的时候运行的?答案就是,在task的最顶层的代码就是配置代码,比如: task myTask { def name = "Pavel" //<-- 这行代码会在配置阶段执行 println "Hello, World!"////<-- 这行代码也将在配置阶段执行 } 这就是为什么我执行gradle tasks的时候,会打印出来”Hello, World!”-因为配置代码被执行了。但这并不是我想要的效果,我想要”Hello, World!”仅仅在我显式的调用myTask的时候才打印出来。为了达到这个效果,最简单的方法就是就是使用Task#doLast()方法。 task myTask { def text = 'Hello, World!' //configure my task doLast { println text //this is executed when my task is called } } 现在,”Hello, World!”仅仅会在我执行gradle myTask的时候打印出来。Cool,现在我已经知道如何配置以及使task做正确的事情。还有一个问题,最开始的例子中,第三个task的<<符号是什么意思? task myTask2 << { println "Hello, World!" } 这其实只是doLast的一个语法糖版本。它和下面的写法效果是一样的: task myTask { doLast { println 'Hello, World!' //this is executed when my task is called } } 但是,这种写法所有的代码都在执行部分,没有配置部分的代码,因此比较适合那些简小不需要配置的task。一旦你的task需要配置,那么还是要使用doLast的版本。 分类: Android Pro,Gradle 本文转自demoblog博客园博客,原文链接http://www.cnblogs.com/0616--ataozhijia/p/5193902.html如需转载请自行联系原作者 demoblog
在很多种情况下,我们都必须对字符串进行匹配,以便判断字符串的格式是否符合要求,对字符串中的内容进行提取。比如,我要从一段话aabdfe中,判断这段话是否有包含ab这个词,那么如果用if-else来判断的话,那么我们必须遍历整个字符串,当遇到一个a,记录一下状态,判断下一个是否是所要的b。这个过程随着要判断的内容(在这里是ab)和要被字符串的长度的增长,恶心程度递增。但是又因为字符串的判断实在是太常要用到啦,所以就有了正则表达式这么个东西,正则表达式其实就是一个字符串识别的规则,通过这个规则,我们就可以让程序根据这个规则去识别了。在Java里面使用正则表达式需要涉及到两个Pattern和Matcher。Pattern和Matcher之间的关系就好比Pattern是做模具的师傅,pattern将模具(正则表达)做好之后,指派一个小工(matcher)去匹配,matcher要做的就是原材料(即要被匹配的源字符串)和模具(即Pattern中的正则表达式)配对、比较。本文将探讨跟Java正则表达式有关的一些应用,并尝试着通过代码说明Pattern和Matcher的用法。 --对于正则表达式不熟悉的同学,请自行到《正则表达式30分钟入门教程》中学习。 1、Matcher中的分组 使用Matcher类,我们必须要先了解一下正则表达式中分组(Group)的概念。不清楚组的概念的同学,请自行到《正则表达式30分钟入门教程》中的分组一节学习。简单的说,分组其实就是为了能够指定同一个规则可以使用多少次,有点像我们买苹果,假设我们要买6颗苹果,那么我们可以用连续使用6次又大又红的规则来挑6个苹果。正则表达式中的分组是就是整个大的正则表达式和用()圈起来的内容。下面举一个例子。 在这个正则表达式"\\w(\\d\\d)(\\w+)"中, 分组0:是"\\w(\\d\\d)(\\w+)" 分组1:是(\\d\\d) 分组2:是(\\w+) 如果我们稍稍变换一下,将原先的正则表达式改为"(\\w)(\\d\\d)(\\w+)" 我们的分组就变成了 分组0:是"\\w(\\d\\d)(\\w+)" 分组1:是"(\\w)" 分组2:是"(\\d\\d)" 分组3:是"(\\w+)" 我们看看和正则表达式”\\w(\\d\\d)(\\w+)”匹配的一个字符串x99SuperJava, group(0)是匹配整个表达式的字符串的那部分A22happy group(1)是第1组(\d\d)匹配的部分:22 group(2)是第二组(\w+)匹配的那部分happy 读者也可是用下面的代码验证一下 public static void main(String[] args) { String Regex="\\w(\\d\\d)(\\w+)"; String TestStr="A22happy"; Pattern p=Pattern.compile(Regex); Matcher matcher=p.matcher(TestStr); if (matcher.find()) { int gc=matcher.groupCount(); for (int i = 0; i <= gc; i++) { System.out.println("group "+i+" :"+matcher.group(i)); } } } 记得要引用 import java.util.regex.Matcher; import java.util.regex.Pattern; 2、Matcher常用方法 public Matcher reset() 这个方法将Matcher的状态重新设置为最初的状态。 public Matcher reset(CharSequence input) 重新设置Matcher的状态,并且将候选字符序列设置为input后进行Matcher, 这个方法和重新创建一个Matcher一样,只是这样可以重用以前的对象。 public int start() 这个方法返回了,Matcher所匹配的字符串在整个字符串的的开始下标: 下面我们用一个小例子说明Matcher.start的用处 public static void testStart(){ //创建一个 Matcher ,使用 Matcher.start()方法 String candidateString = "My name is Bond. James Bond."; String matchHelper[] = {" ^"," ^"}; Pattern p = Pattern.compile("Bond"); Matcher matcher = p.matcher(candidateString); //找到第一个 'Bond'的开始下标 matcher.find(); int startIndex = matcher.start(); System.out.println(candidateString); System.out.println(matchHelper[0] + startIndex); //找到第二个'Bond'的开始下标 matcher.find(); int nextIndex = matcher.start(); System.out.println(candidateString); System.out.println(matchHelper[1] + nextIndex); } 结果截图: public int start(int group) 这个方法可以指定你感兴趣的sub group,然后返回sup group(子分组)匹配的开始位置。 public int end() 这个和start()对应,返回在以前的匹配操作期间,由给定组所捕获子序列的最后字符之后的偏移量。 其实start和end经常是一起配合使用来返回匹配的子字符串。 public int end(int group) 和public int start(int group)对应,返回在sup group(子分组)匹配的子字符串最后一个匹配字符的位置。 public String group() 返回由以前匹配操作所匹配的字符串。 这个方法提供了强大而方便的工具,他可以等同使用start和end,然后对字符串作substring(start,end)操作。 看看下面一个小例子: /** * 测试matcher.group方法 */ public static void testGroup() { // 创建一个 Pattern Pattern p = Pattern.compile("Bond"); // 创建一个 Matcher ,以便使用 Matcher.group() 方法 String candidateString = "My name is Bond. James Bond."; Matcher matcher = p.matcher(candidateString); // 提取 group matcher.find(); System.out.println(String.format("group匹配的字符串 : %s",matcher.group())); System.out.println(String.format("匹配的开始位置 : %d", matcher.start())); System.out.println(String.format("匹配的结束位置 : %d", matcher.end())); System.out .println("---再次使用matcher.find()方法,看看matcher中group、start、end方法的效果"); matcher.find(); System.out.println(String.format("group匹配的字符串 : %s",matcher.group()));; System.out.println(String.format("匹配的开始位置 : %d", matcher.start())); System.out.println(String.format("匹配的结束位置 : %d", matcher.end())); System.out.println(String.format("candidateString字符串的长度 : %d", candidateString.length())); } 结果截图: 3、最后来一个正则表达式的面试题来结束 1.判断身份证:要么是15位,要么是18位,最后一位可以为字母,并写程序提出其中的年月日。 public static void main(String[] args) { testID_Card(); } public static void testID_Card() { // 测试是否为合法的身份证号码 String[] strs = { "130681198712092019", "13068119871209201x", "13068119871209201", "123456789012345", "12345678901234x", "1234567890123" }; // 准备正则表达式(身份证有15位和18位两种,身份证的最后一位可能是字母) String regex = "(\\d{14}\\w)|\\d{17}\\w"; // 准备开始匹配,判断所有的输入是否是正确的 Pattern regular = Pattern.compile(regex); // 创建匹配的规则Patter StringBuilder sb = new StringBuilder(); // 遍历所有要匹配的字符串 for (int i = 0; i < strs.length; i++) { Matcher matcher = regular.matcher(strs[i]);// 创建一个Matcher sb.append("身份证: "); sb.append(strs[i]); sb.append(" 匹配:"); sb.append(matcher.matches()); System.out.println(sb.toString()); sb.delete(0, sb.length());// 清空StringBuilder的方法 } GetBirthDay(strs); } private static void GetBirthDay(String[] strs) { System.out.println("准备开始获取出生日期"); // 准备验证规则 Pattern BirthDayRegular = Pattern.compile("(\\d{6})(\\d{8})(.*)"); // .*连在一起就意味着任意数量的不包含换行的字符 Pattern YearMonthDayRegular = Pattern .compile("(\\d{4})(\\d{2})(\\d{2})"); for (int i = 0; i < strs.length; i++) { Matcher matcher = BirthDayRegular.matcher(strs[i]); if (matcher.matches()) { Matcher matcher2 = YearMonthDayRegular .matcher(matcher.group(2)); if (matcher2.matches()) { System.out.println(strs[i]+" 中的出生年月分解为: "+"年" + matcher2.group(1) + " 月:" + matcher2.group(2) + " 日:" + matcher2.group(3)); } } } } 结果截图: 参考链接 Java中正则Matcher类的matches()、lookAt()和find()的区别 学习正则表达式:Matcher类 本文转自陈哈哈博客园博客,原文链接http://www.cnblogs.com/kissazi2/p/3287206.html如需转载请自行联系原作者 kissazi2
由于最新的ADT、Android SDK需要最新版本的Eclipse才能使用,我无奈的只好升级Eclipse。看看自己的Eclipse已经两年没有升级了,也是时候升级了。升级前,有很多的顾虑。因为像这种大型软件,没有卸载好的话,可能就得重装系统才能装上。我的Eclipse中挂了很多之前做过的项目,这些项目零散地分布在电脑中,一不小心就可能丢了。 一、安装Eclipse 感觉Eclipse的安装还是蛮顺利的,从官网上下载安装包,因为Eclipse只要解压后就可以使用,因此直接将原来的Eclipse覆盖掉就行了。在下载地址http://www.eclipse.org/downloads/中根据你的系统选择版本。这里其实也需要根据你在电脑上安装的JDK的位数,如果你是JDK是32位,那么就装32位的。64位,就选64位。 二、ADT手动安装 ADT的安装弄了好久,之前想直接通过联网安装,但是连的上https://dl-ssl.google.com/android/eclipse/,却总是提示在contact with Server。搞了好几个小时,最后直接手动下载,最新版本(22.6.2)的ADT地址是:http://dl.google.com/android/ADT-22.6.2.zip。你也可以从google的官方地址http://developer.android.com/sdk/installing/installing-adt.html找到,然后下载(见下图) 下载完后 1、打开Eclipse→Help→Install New Software… 2、 选择Work with:后的Add...→在Local选项中输入Android Plugin→在Archive中找到ADT的压缩包→点击OK→在下面勾选Name中的选项即可。 三、离线安装Android SDK 无奈啊,我总是不能直接利用Eclipse在线更新。离线安装Android SDK的方法也很简单,直接到http://developer.android.com/sdk/index.html下载最新版本的SDK,我推荐你选红框内的。然后,根据安装程序一直点下一步就可以啦(安装的时候,记得先把Eclipse关掉)。 参考链接 安装配置Android ADT的详细步骤 本文转自陈哈哈博客园博客,原文链接http://www.cnblogs.com/kissazi2/p/3645212.html如需转载请自行联系原作者 kissazi2
ImageLoader类中包含了所有操作。他是一个单例,为了获取它的一个单一实例,你需要调用getInstance()方法。在使用ImageLoader来显示图片之前,你需要初始化它的配置-ImageLoaderConfiguration使用init(…)方法。然后,你就可以使用可以明确地根据需要使用不同形式的displayImage(…)。 总之,ImageLoader最简单的用法如下所示(使用默认配置): ImageView imageView = ... // view, where the image will be displayed String imageUrl = ... // image URL (e.g. "http://site.com/image.png", "file:///mnt/sdcard/img/image.jpg") ImageLoader imageLoader = ImageLoader.getInstance(); imageLoader.init(ImageLoaderConfiguration .createDefault(context)); imageLoader.displayImage(imageUrl, imageView); 现在,让我们看看完整的用法。 就像你已经知道的,我们首先需要用configuration对象来初始化ImageLoader。因为ImageLoader是一个单例,所以它需要在App一启动的时候就初始化。我建议在一个重载了了的Application.onCreate()来做这件事。对于一个已经初始化的ImageLoader,重新初始化将不会对程序有任何影响。 接下来,我们创建一个configuration,他是一个ImageLoaderConfiguration类的对象,我们使用Builder来创建它。 File cacheDir = StorageUtils.getCacheDirectory(context, "UniversalImageLoader/Cache"); ImageLoaderConfiguration config = new ImageLoaderConfiguration .Builder(getApplicationContext()) .maxImageWidthForMemoryCache(800) .maxImageHeightForMemoryCache(480) .httpConnectTimeout(5000) .httpReadTimeout(20000) .threadPoolSize(5) .threadPriority(Thread.MIN_PRIORITY + 3) .denyCacheImageMultipleSizesInMemory() .memoryCache(new UsingFreqLimitedCache(2000000)) // You can pass your own memory cache implementation .discCache(new UnlimitedDiscCache(cacheDir)) // You can pass your own disc cache implementation .defaultDisplayImageOptions(DisplayImageOptions.createSimple()) .build(); 下面让我们来看看每一个选项。 • maxImageWidthForMemoryCache() 和maxImageHeightForMemoryCache()用于将图片将图片解析成Bitmap对象。为了不储存整个图片,根据ImageView参数的值(要加载图片的那个)减少图片的大小。maxWidth和maxHeight(第一阶段),layout_width layout_height(第二阶段)。如果不定义这些参数(值为fill_parent和wrap_content被视为不确定的大小),然后尺寸的设定就会根据maxImageWidthForMemoryCache()和maxImageHeightForMemoryCache()的设置而定。原始图像的大小最大会缩小到2倍(适合用fast decoding),直到宽度或高度变得小于指定值; o默认值 - 设备的屏幕大小 • httpReadTimeout()设置图片从网络中加载的最大超时时间(以毫秒为单位) o 默认值- 30秒 • threadPoolSize() 设置线程池的大小. 每一个图片加载和显示的任务都是在一个个单独的线程中进行的,这些线程在从图片网络中被下载的时候就会进入线程池。因此,池的大小决定能同时运行的线程数。一个大的线程池能显著地拖慢UI的响应速度,例如,列表的滚动就会变慢. o 默认值- 5 • threadPriority()设置正在运行任务的所有线程在系统中的优先级(1到10); o默认值- 4 • 调用denyCacheImageMultipleSizesInMemory()强制UIL在内存中不能存储内容相同但大小不同的图像。由于完整大小的图片会存储在磁盘缓存中,后面当图片加载进入内存,他们就会缩小到ImageView的大小(图片要显示的尺寸),然而在某些情况下,相同的图像第一次显示在一个小的View中,然后又需要在一个大的View中显示。同时,两个不同大小的相同内容的图片就会被将被存储在内存中。这是默认的操作。denyCacheImageMultipleSizesInMemory()指令确保删除前一个加载的图像缓存的内存的大小 • 使用memoryCache(),你可以指定内存缓存的实现。你可以使用现成的解决方案(他们都是实现limited size-cache,如果超过缓存大小,就通过一定算法删除一个对象): o FIFOLimitedCache (根据先进先出的原则上删除多余对象) o LargestLimitedCache (空间占用最大的对象会被删除) o UsingAgeLimitedCache (最早被添加的对象会被删除) o UsingFreqLimitedCache (最少被用到的对象会被删除) 或者,你可以实现通过实现接口MemoryCacheAware <String,Bitmap>来实现自己的缓存; o 默认值 - 使用2 MB的内存限制的UsingFreqLimitedCache memoryCacheSize() 设置内存缓存的最大占用空间. 在这种情况下,默认的缓存策略是 - UsingFreqLimitedCache. o 默认值 - 2 MB • Using discCache(), 你可以定义自己的磁盘缓存. 也可以现成的解决方案 (文件跟特定的URL匹配,文件名为这些URL的哈希值): o UnlimitedDiscCache (通常的策略, 缓存大小没有限制) o FileCountLimitedDiscCache (限定大小的缓存) o TotalSizeLimitedDiscCache (限定文件个数的缓存策略) 另外,你也可以通过实现DiscCacheAware接口定义自己的缓存 o 默认值 - UnlimitedDiscCache • 使用defaultDisplayImageOptions(),你可以设置image显示选项,如果自定义选项没有传递给displayImage(),它将用于displayimage(…)方法的每一次调用。下面,我将详细讨论这些选项。 我们可以构建一个configuration对象或信任一个开发人员(比如我)然后使用默认的configuration: ImageLoaderConfiguration config = ImageLoaderConfiguration.createDefault(context); 因此,configuration就创建好了。现在,ImageLoader可以通过它初始化了: ImageLoader.getInstance().init(config); 就这样,ImageLoader已经可以使用了。在下一篇文章中我们还会讲到它。 原文链接 http://www.intexsoft.com/blog/item/72-universal-image-loader-part-2.html 本文转自陈哈哈博客园博客,原文链接http://www.cnblogs.com/kissazi2/p/3903899.html如需转载请自行联系原作者 kissazi2
(1) 如何开发一个通用型验证码识别引擎。难度系数:×××× (2) 我们在网上看见一件衣服,把这张图保存下来。我们在街上看见别人的衣服搭配很好,于是用数码相机或手机拍照。但是,如何根据照片去寻找哪里卖这些衣服,是一个很难的问题。最好有一个系统,输入一个照片,这个系统能够自动推荐类似的衣服,及这些衣服的taobao卖家(或别的URL地址)。难度系数:××××× (3) 2D 发型的变型。给一个现有2D发型,操作者可以对这个2D发型进行操作——改变尺寸、染色、改变局部的形状(涉及到inpainting)。难度系数:××× (4) 在线试衣间中3D服装制作的成本问题。淘宝试衣间制作一个数据包的收费是30元,太高了,如何降低这个成本。难度系数:××××× 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2009/11/16/1604124.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
上个月做了个小项目,模拟提交加简单的验证码识别,和对方聊天的时候,我帮他分析,你这个应该用什么工具抓包,怎么分析,怎么识别。内心中是想推脱不做这个项目的。小项目没肉不说还很麻烦。最终禁不住要求还是接下来了。最开始的聊天、咨询按一小时算吧,报价前详细的抓包分析花了一两个小时,接下来的开发时间预估计是半天,然后提交后估计聊天还得聊一两小时。虽说具体的开发也就半天的时间,但考虑到前后耗时,应该按一个工作日算,由于初次合作,加一点风险系数,报了个 600元。 最开始要求对方支付200预付款直接打到我帐号里,对方要求发一个淘宝单子支付,这意思是不见Demo不出钱,当然他的说法是可以增加双方信用。对这种中介托管情况,我一般要求是全款托管,钱多的话,可以是分期托管,600块钱没多大点还分期托管,不是直接预付,真够费事的。我就直接拒绝了,说免了太麻烦,直接做出来了你看行就给钱。 晚上花了几小时搞定,然后弄成带限制的Native版本(破解成本大于600块钱就行)发给了对方。对方说给客户了,客户给钱了就给我钱。我这时才知道对方也是中介方,也没说啥(这时说啥也于事无补,徒自浪费时间)。然后就是对方问我怎么打包成Native版本,然后聊天啊,截图啊,又是一小时。后来感觉这样没完没了的不是个事情,我就直接说让对方给钱我给源代码。对方说客户还没给钱。我说是不是客户不给你钱你就不给我钱了?他说不是,客户不给他钱,他还是会过几天给我钱的。我当场晕了,客户给他钱,他给我钱,客户不给他钱,他也会给我钱,直接给我钱不就得了。说这话(客户不给钱,也会给我钱)就有点虚伪了。我没说出来。因为我接项目时很痛快的没有让他预付,他还是决定给我200块钱。 当然,后面发生的事情就是很常见了。客户没给他钱。我也没收到他的后面的钱。后来,他的一个朋友出来解释,说客户没给他钱,他还付出了200块钱首付,却什么也没得到,然后向我道歉。 我谈了我的看法: (1)我提交之前不知道在他们背后还有个客户的存在。我报价是按自己的时间成本加上风险系数综合报价的。如果只是两方初次合作,这个安全系数假设是 80%,500元的时间成本除以这 80% 就是625;如果引入了这第三方,存在两个安全系数相乘的问题,且这第二个的风险我还不知道是多少,因此报价就不是现在这样子了。我和他之间的合作只是我们之间的约定,跟那个客户完全无关,当然,你直接据掉我给个预付而不要源代码,也符合行规,但你不能为了缓解自己的道德压力而以客户没给你钱来拒绝支付我的后期。这种情况根本没必要解释,越解释越糟糕。 (2)如果我是你这400块钱管他客户要不要我也会支付的。也没多少钱,都是可承受的。多一个朋友也抵得上这400块钱。随便之间介绍介绍项目,资源共享下这点钱就回来了还不止。二月份我也介绍了两个单子出去了。 呵呵,当然,对方也没给我这钱,只是说今后有项目给我介绍。 今天,最开始找我聊天的那个兄弟又找我了,说有一个asp项目问我能不能改,能改的话好结算我那400元。哈哈哈哈,还想合作啊。我就直接说一月以内按每天500元计算,一月以上每天400元。(当然,还有没说出来的。那就是,报价需要考虑到风险。因为有前面这件事情,合作的安全系数怎么也得从 80% 降低到 50% 吧。那得以 500/0.5=1000元/天来报价。)然后有让我介绍单子,说有单子了,好给我借钱。俺气结了,谈单子不花时间啊,不花信用啊。还有这种情况下,怎么可能介绍客户? ==== 事情就不是这样做的,整个双输。浪费时间不说,双方得到的都很少。换一种方式,就是双赢的结局。不说介绍一个单子就赚回来这点钱还不止,就我的那点公用代码,拿到手了也不止这点钱啊。 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2011/03/04/1971059.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
(1)PHP 不能用 5.3 版 (2)后台:./?m=admin (3)后台扩展: 可修改 webapp/modules/admin/templates/inc_header.tpl (4)前台扩展: 可修改 webapp/lib/util/util.php 的 util_get_c_navi 函数来增删 menu; 可修改 webapp/modules/pc/templates/common/layout.tpl 来调整内页的内容; 可修改 public_html/xhtml_style.php 来调整 css 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2011/08/11/2135543.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
.Net 下默认没有读写视频的类,要进行视频操作很麻烦,之前用过网上的代码,用的很不爽。AForge.Net 对 ffmpeg 进行了封装,不过它引用的dll有点多,且没提供seek方法,无法跳到指定帧,也有点不爽,俺在 AForge.Net 对FFMPEG 封装的基础上,进行了修改,得到 Geb.Video.FFMPEG 库,现分享出来。 主要功能如下: l 读取视频文件,获取视频参数; l 读取帧; l 查找帧; l 写入视频文件。 有了这些功能,可以写一个简单的视频播放器了,当然,视频转码之类的也不在话下。在此基础上,视频分析,视频合成,视频编辑等等,都可以进行。 License: LGPL v3 license (AFoege.Net 的 license,毕竟是从它修改的). 源码:https://github.com/xiaotie/GebVideoFFMPEG 演示: 代码下载:Geb.Video.FFMPEG.Demo 读取视频文件,获取视频参数 _reader = new VideoFileReader(); _reader.Open(path); String info = String.Format("Video info:\r\n\r\n Width-{0}\r\n Height-{1}\r\n FrameCount-{2}\r\n FrameRate-{3}\r\n Codec-{4}",_reader.Width,_reader.Height,_reader.FrameCount, _reader.FrameRate, _reader.CodecName); tbInfo.Text = info; 读取下一帧: ImageRgb24 img = _reader.ReadVideoFrame(); 查找帧: // 指定帧的编号 Int64 idx = _reader.FrameCount * 2 / 3; // 跳到指定帧附近的关键帧处,true 为跳到关键帧,false 为跳到任意帧 _reader.Seek(idx, true); 写入视频文件 VideoFileWriter _writer = new VideoFileWriter(); _writer.Open("output.avi", _reader.Width, _reader.Height, _reader.FrameRate, VideoCodec.MPEG4); // demo 代码,之处理 100 帧 for (int i = 0; i < 100; i++) { ImageRgb24 img = _reader.ReadVideoFrame(); if (img == null) break; _writer.WriteVideoFrame(img); img.Dispose(); } _writer.Close(); 不用时别忘记 Close()。 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2013/01/05/2845576.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
我在做基于HC6800的51单片机上,写串口通信程序遇到了许多坑。 一个大坑:为什么HC6800上U转串口不能发送数据。 因为我们向HC6800烧程序都是使用图1所示的软件,所以我想当然地认为可以直接通过HC6800上的U转串进行串口通信,而我的同学信誓旦旦地告诉我可以。坑爹。并且这个软件好像也在提示可以用直接用U转串,进行串口通信(见图2)。但是当我打开串口调试助手的时候(如图3),却发现单片机掉电了(如图4)。其实HC6800上有另一个串口的,我们只要把连接图5黄框处串口。并且在图4中的6处,改变短路帽的接法,使之从USB下载模式转到串口通信模式(请仔细比对图4和图6)。 图1 烧录软件 图2 软件的暗示 图3 打开串口调试助手 图4 单片机掉电 图5 黄框是另一个串口 图6 串口短路帽正确的接法 在完成了以上步骤之后,基本上我们就完成了串口通信的硬件连接准备。再来设置一下软件的配置。 下面的设置以波特率2400为示例 1、设置单片机内的波特率 2、设置串口调试助手的波特率 3、设置驱动中的波特率 注意: 服务器和客户端中波特率一定要一致,不然就收不到消息或收到错误的消息。 本文转自陈哈哈博客园博客,原文链接http://www.cnblogs.com/kissazi2/archive/2013/05/30/3108280.html如需转载请自行联系原作者 kissazi2
reference : http://blog.csdn.net/beijingshi1/article/details/38681281 不像在Eclipse,可以直接导出jar包。AndroidStudio只可以生成aar包。 在网上看到许多朋友问怎么可以像Eclipse一样导出jar包,其实我们只要知道它的原理就可以了。 用jar命令就可以打包你所需要的资源,并指定jar包名。 在网上下载Volley源代码,导出jar包为例子。 在Volley项目工程中,我修改了下他的gradle版本,改为0.12+, 因为我是最新的gradle2.0。现在它的build.gradle文件如下: buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.12.+' } } apply plugin: 'com.andriod.library' android { compileSdkVersion 19 buildToolsVersion = "20" sourceSets { defaultConfig { testPackageName 'com.android.volley.tests' } main { assets.srcDirs = ['assets'] res.srcDirs = ['res'] aidl.srcDirs = ['src'] resources.srcDirs = ['src'] renderscript.srcDirs = ['src'] java.srcDirs = ['src'] manifest.srcFile 'AndroidManifest.xml' } instrumentTest.setRoot('tests') instrumentTest.java.srcDirs = ['tests/src'] } } 因为再导入Volley工程的时候,我选择的是推荐的Gradle Wrapper,它的作用是使我们在Unix,windows平台 下实现兼容。我用的是windows,所以先到达Volley的根目录,Volley我下载到E:\下 cd e:\Volley 接着输入命令: gradlew clean build 就会看到Volley目录下有一个build文件夹,在 build/intermediates/classes/release下,我们可以看到Java文件生成的class文件,我们只要用jar打包这个文件夹就可以了 输入下面的命令,记得最后一个是'.',前面有空格。 jar cvf volley.jar -C build/intermediates/classes/release . 不懂什么意思的话,去http://blog.sina.com.cn/s/blog_93d133c601013hdm.html看下。 如无意外的话你会在Volley文件夹下看到volley.jar。 但是这并不是我们想要的啊,我想AndroidStudio自动的帮我搞好,生成jar包. 这就需要我们在build.gradle中设置了 由gradle官网可以知道,task和project是gradle最主要的东西,我当初的想法就是, 建立一个task,调用jar命令帮我生成jar包,结果成功了,如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.12.+' } } //定义一个函数,target是生成jar包的文件名,classDir是class文件所在的文件夹 def makeJar(String target,String classDir){ exec{ executable "jar" //调用jar args "cvf",target args "-C", classDir args "","." } } //新建一个task,名为buildLib,依赖build(build是一个自带的task) task buildLib(dependsOn:['build'])<< { makeJar("volley.jar","build/intermediates/classes/release") } apply plugin: 'android-library' android { compileSdkVersion 19 buildToolsVersion = "20" sourceSets { defaultConfig { testPackageName 'com.android.volley.tests' } main { assets.srcDirs = ['assets'] res.srcDirs = ['res'] aidl.srcDirs = ['src'] resources.srcDirs = ['src'] renderscript.srcDirs = ['src'] java.srcDirs = ['src'] manifest.srcFile 'AndroidManifest.xml' } instrumentTest.setRoot('tests') instrumentTest.java.srcDirs = ['tests/src'] } } 现在我们在Volley文件夹下运行buildLib这个task就行了 gradlew buildLib *_*成功了吧..具体更多的细节的话,你们可以去官网找下答案,或者留言问我也可以。。 分类: Android Pro 本文转自demoblog博客园博客,原文链接http://www.cnblogs.com/0616--ataozhijia/p/4817844.html如需转载请自行联系原作者 demoblog
reference : http://blog.sina.com.cn/s/blog_4df23d840100u25x.html 找到mongoose的安装目录 /usr/local/lib/node/mongoose/lib/mongoose/schema 下的文件:string.js 修改SchemaString.prototype.$conditionalHandlers = { '$lt': handleSingle, '$lte': handleSingle, '$gt': handleSingle, '$gte': handleSingle, '$all':handleArray, '$ne': handleSingle, '$in': handleArray, '$nin': handleArray }; 模糊查询: 在mongodb中: db.admins.find({loginName:{$all:[/^a.*/]}}); 在mongoose中 var q = new RegExp("^" + key +".*");//所有以传入参数开始的 userM.admins.find({loginName:{'$all':[q]}},function(err, results){ if (err) { console.log(err); } else { res.send(results); } }); $findCondition = array( 'Channel' => "$ch", 'Name' => array('$ne' => ""), 'Type' => array('$ne' => ""),'Stype' => array('$in' => array("d", "dr")), 'CTime' => array('$gt' => $minDate, '$lt' => $maxDate), ); 分类: Mongo 本文转自demoblog博客园博客,原文链接http://www.cnblogs.com/0616--ataozhijia/p/8056655.html如需转载请自行联系原作者 demoblog
Java中的类可以是static吗?答案是可以。在java中我们可以有静态实例变量、静态方法、静态块。类也可以是静态的。 java允许我们在一个类里面定义静态类。比如内部类(nested class)。把nested class封闭起来的类叫外部类。在java中,我们不能用static修饰顶级类(top level class)。只有内部类可以为static。 静态内部类和非静态内部类之间到底有什么不同呢?下面是两者间主要的不同。 (1)内部静态类不需要有指向外部类的引用。但非静态内部类需要持有对外部类的引用。 (2)非静态内部类能够访问外部类的静态和非静态成员。静态类不能访问外部类的非静态成员。他只能访问外部类的静态成员。 (3)一个非静态内部类不能脱离外部类实体被创建,一个非静态内部类可以访问外部类的数据和方法,因为他就在外部类里面。 基于上面的讨论,我们可以通过这些特性让编程更简单、有效。 /* 下面程序演示如何在java中创建静态内部类和非静态内部类 */ class OuterClass{ private static String msg = "GeeksForGeeks"; // 静态内部类 public static class NestedStaticClass{ // 静态内部类只能访问外部类的静态成员 public void printMessage() { // 试着将msg改成非静态的,这将导致编译错误 System.out.println("Message from nested static class: " + msg); } } // 非静态内部类 public class InnerClass{ // 不管是静态方法还是非静态方法都可以在非静态内部类中访问 public void display(){ System.out.println("Message from non-static nested class: "+ msg); } } } class Main { // 怎么创建静态内部类和非静态内部类的实例 public static void main(String args[]){ // 创建静态内部类的实例 OuterClass.NestedStaticClass printer = new OuterClass.NestedStaticClass(); // 创建静态内部类的非静态方法 printer.printMessage(); // 为了创建非静态内部类,我们需要外部类的实例 OuterClass outer = new OuterClass(); OuterClass.InnerClass inner = outer.new InnerClass(); // 调用非静态内部类的非静态方法 inner.display(); // 我们也可以结合以上步骤,一步创建的内部类实例 OuterClass.InnerClass innerObject = new OuterClass().new InnerClass(); // 同样我们现在可以调用内部类方法 innerObject.display(); } } 参考链接 http://stackoverflow.com/questions/7486012/static-classes-in-java 本文转自陈哈哈博客园博客,原文链接http://www.cnblogs.com/kissazi2/p/3971065.html如需转载请自行联系原作者 kissazi2
Android编程中一个共同的困难就是协调Activity的生命周期和长时间运行的任务(task),并且要避免可能的内存泄露。思考下面Activity的代码,在它启动的时候开启一个线程并循环执行任务。 1 /** 2 * 一个展示线程如何在配置变化中存活下来的例子(配置变化会导致创 3 * 建线程的Activity被销毁)。代码中的Activity泄露了,因为线程被实 4 * 例为一个匿名类实例,它隐式地持有外部Activity实例,因此阻止Activity 5 * 被回收。 6 */ 7 public class MainActivity extends Activity { 8 9 @Override 10 protected void onCreate(Bundle savedInstanceState) { 11 super.onCreate(savedInstanceState); 12 exampleOne(); 13 } 14 15 private void exampleOne() { 16 new Thread() { 17 @Override 18 public void run() { 19 while (true) { 20 SystemClock.sleep(1000); 21 } 22 } 23 }.start(); 24 } 25 } 当配置发生变化(如横竖屏切换)时,会导致整个Activity被销毁并重新创建,很容易假定Android将会为我们清理和回收跟Activity相关的内存及它运行中的线程。然而,这并非如此。这两者都会导致内存泄露而且不会被回收, 后果是性能可能显著地下降。 怎么样让一个Activity泄露 如果你读过我前一篇关于Handler和内部类的文章,那么第一种内存泄露应该很容易理解。在Java中,非静态匿名类隐式地持有他们的外部类的引用。如果你不小心,保存这个引用可能导致Activity在可以被GC回收的时候被保存下来。Activity持有一个指向它们整个View继承树和它所持有的所有资源的引用,所以如果你泄露了一个,很多内存都会连带着被泄露。 配置发生变化只加剧了这个问题,它发出一个信号让Activity销毁并重新创建。比如,基于上面的代码进行10次横竖屏变化后,我们可以看到(使用Eclipse Memory Analyzer)由于那些隐式的引用,每一个Activity对象其实都留存在内存中: 图1.在10次配置发生变化后,存留在内存中的Activity实例 每一次配置发生变化后,Android系统都会创建一个新的Activity并让旧的Activity可以被回收。然而,隐式持有旧Activity引用的线程,阻止他们被回收。所以每次泄露一个新的Activity,都会导致所有跟他们关联的资源都没有办法被回收。 解决方法也很简单,在我们确定了问题的根源,那么只要将线程定义为private static内部类,如下所示: 1 /** 2 * 这个例子通过将线程实例声明为private static型的内部 类,从而避免导致Activity泄 3 * 露,但是这个线程依旧会跨越配置变化存活下来。DVM有一个指向所有运行中线程的 4 * 引用(无论这些线程是否 可以被垃圾回收),而线程能存活多长时间以及什么时候可 5 * 以被回收跟Activity的生命周期没有任何关系。 6 * 活动线程会一直运行下去,直到系统将你的应用程序销毁。 7 */ 8 public class MainActivity extends Activity { 9 10 @Override 11 protected void onCreate(Bundle savedInstanceState) { 12 super.onCreate(savedInstanceState); 13 exampleTwo(); 14 } 15 16 private void exampleTwo() { 17 new MyThread().start(); 18 } 19 20 private static class MyThread extends Thread { 21 @Override 22 public void run() { 23 while (true) { 24 SystemClock.sleep(1000); 25 } 26 } 27 } 28 } 新的线程不会隐式地持有Activity的引用,并且Activity在配置发生变化后都会变得可以被回收。 怎么使一个Thread泄露 第二个问题是每当创建了一个新Activity,就会导致一个thread泄露并且不会被回收。在Java中,thread是GC Root也就是说在系统中的Dalvik Virtual Machine (DVM)保存对所有活动 中线程的强引用,这就导致了这些线程留存下来继续运行并且不会达到可以被回收的条件。因此你必须要考虑怎样停止后台线程。下面是一个例子: 1 /** 2 * 跟例子2一样,除了这次我们实现了取消线程的机制,从而保证它不会泄露。 3 * onDestroy()常常被用来在Activity推出前取消线程。 4 */ 5 public class MainActivity extends Activity { 6 private MyThread mThread; 7 8 @Override 9 protected void onCreate(Bundle savedInstanceState) { 10 super.onCreate(savedInstanceState); 11 exampleThree(); 12 } 13 14 private void exampleThree() { 15 mThread = new MyThread(); 16 mThread.start(); 17 } 18 19 /** 20 * 静态内部类不会隐式地持有他们外部类的引用,所以Activity实例不会在配置变化 21 * 中被泄露 22 */ 23 private static class MyThread extends Thread { 24 private boolean mRunning = false; 25 26 @Override 27 public void run() { 28 mRunning = true; 29 while (mRunning) { 30 SystemClock.sleep(1000); 31 } 32 } 33 34 public void close() { 35 mRunning = false; 36 } 37 } 38 39 @Override 40 protected void onDestroy() { 41 super.onDestroy(); 42 mThread.close(); 43 } 44 } 在上面的代码中,我们在onDestroy()中关闭线程保证了线程不会意外泄露。如果你想要在配置变化的时候保存线程的状态(而不是每次都要关闭并重新创建一个新的线程)。考虑使用可留存(在配置变化中不会被销毁)、没有UI的fragment来执行长时间任务。看看我的博客,叫做《用Fragment解决屏幕旋转(状态发生变化)状态不能保持的问题》,里面有一个例子说明实现这点。API Demo中也一个全面的例子。 总结 在Android中处理Activity生命周期与长时间运行的任务的关系可能很困难并且可能导致内存泄露。下面有一些值得考虑的通用建议: 优先使用静态内部类而不是非静态的。非静态内部类的每个实例都会有一个对它外部Activity实例的引用。当Activity可以被GC回收时,存储在非静态内部类中的外部Activity引用可能导致垃圾回收失败。如果你的静态内部类需要宿主Activity的引用来执行某些东西,你要将这个引用封装在一个WeakReference中,避免意外导致Activity泄露。 不要假定Java最后总会为你清理运行中的线程。在上面的例子中,很容易错误地认为用户退出Activity后,Activity就会被回收,任何跟这个Activity关联的线程也都将一并被回收。事实上不是这样的。Java线程会继续运行下去,直到他们被显式地关闭或者整个process被Android系统杀掉。因此,一定要记得记得为后台线程实现对应的取消策略,并且在Activity生命周期事件发生的时候使用合理的措施。 考虑你是否真的应该使用线程。Android Framework提供了很多旨在为开发者简化后台线程开发的类。比如,考虑使用Loader而不是线程当你需要配合Activity生命周期做一些短时间的异步后台任务查询类任务。考虑使用使用Service,然后向使用BrocastReceiver向UI反馈进度、结果。最后,记住本篇文章中一切关于线程的讨论也适用于AsyncTask(因为Asynctask类使用ExecutorService来执行它的任务)。然而,鉴于AsyncTask只应该用于短时间的操作(最多几秒钟,参照文档),它倒不至于会导致像Activity或线程泄露那么大的问题。 这篇文章中的源代码都可以从github下载。文章中的示例程序可以从Google play下载。 译文链接:Activitys, Threads, & Memory Leaks 本文转自陈哈哈博客园博客,原文链接http://www.cnblogs.com/kissazi2/p/4125356.html如需转载请自行联系原作者 kissazi2
reference to : http://blog.sina.com.cn/s/blog_4e1e357d0101i486.html static也是各个业务方可以去全局修改; volatile是处理多线程锁的替代方案,对应有时需要实时的修改共享资源的变量,被volatile修复的变量的值可以立刻被业务方取得最新的值。 不过,猛地感觉,nnd,这不是一样么,static是静态的,所以理论上也可以在不同线程去访问,能访问也就是能修改,所以这里老穆在网上搜了搜 相关的资料,把这个知识点在加强下: 变量放在主存区上,使用该变量的每个线程,都将从主存区拷贝一份到自己的工作区上进行操作。 volatile, 声明这个字段易变(可能被多个线程使用),Java内存模型负责各个线程的工作区与主存区的该字段的值保持同步,即一致性。 static, 声明这个字段是静态的(可能被多个实例共享),在主存区上该类的所有实例的该字段为同一个变量,即唯一性。 volatile, 声明变量值的一致性;static,声明变量的唯一性。 此外,volatile同步机制不同于synchronized, 前者是内存同步,后者不仅包含内存同步(一致性),且保证线程互斥(互斥性)。 static 只是声明变量在主存上的唯一性,不能保证工作区与主存区变量值的一致性;除非变量的值是不可变的,即再加上final的修饰符,否则static声明的变量,不是线程安全的。 下面摘自Java语言规范(Java Language Specification)的官方解释: 1) If a field is declared static, there exists exactly one incarnation of the field, no matter how many instances (possibly zero) of the class may eventually be created. 2) A field may be declared volatile, in which case the Java Memory Model ensures that all threads see a consistent value for the variable。 分类: Android Pro 本文转自demoblog博客园博客,原文链接http://www.cnblogs.com/0616--ataozhijia/p/5316523.html如需转载请自行联系原作者 demoblog
reference : http://blog.csdn.net/zhangyong7112/article/details/54574214 最近一个关于流量的项目在Android7.0系统的手机上运行,一直获取不到流量的使用数据,查看源码然后发现TrafficStats.getUidRxBytes(uid)和TrafficStats.getUidTxBytes(uid)一直都是返回的-1, // 获取某个网络UID接收和发送字节的总和 long total = TrafficStats.getUidRxBytes(uid) + TrafficStats.getUidTxBytes(uid); 这就纳闷了!!!然后多方查找资料,问了公司的大神,给我说可以在“/proc/uid_stat/"文件夹中读取数据。后来总算是出来了。。。 不说了 贴代码 if (total == 0 || (TrafficStats.getUidRxBytes(uid) == -1) && (TrafficStats.getUidTxBytes(uid) == -1)) { total = getTotalBytesManual(uid); } /** * 通过uid查询文件夹中的数据 * @param localUid * @return */ private Long getTotalBytesManual(int localUid) { // Log.e("BytesManual*****", "localUid:" + localUid); File dir = new File("/proc/uid_stat/"); String[] children = dir.list(); StringBuffer stringBuffer = new StringBuffer(); for (int i = 0; i < children.length; i++) { stringBuffer.append(children[i]); stringBuffer.append(" "); } // Log.e("children*****", children.length + ""); // Log.e("children22*****", stringBuffer.toString()); if (!Arrays.asList(children).contains(String.valueOf(localUid))) { return 0L; } File uidFileDir = new File("/proc/uid_stat/" + String.valueOf(localUid)); File uidActualFileReceived = new File(uidFileDir, "tcp_rcv"); File uidActualFileSent = new File(uidFileDir, "tcp_snd"); String textReceived = "0"; String textSent = "0"; try { BufferedReader brReceived = new BufferedReader(new FileReader(uidActualFileReceived)); BufferedReader brSent = new BufferedReader(new FileReader(uidActualFileSent)); String receivedLine; String sentLine; if ((receivedLine = brReceived.readLine()) != null) { textReceived = receivedLine; // Log.e("receivedLine*****", "receivedLine:" + receivedLine); } if ((sentLine = brSent.readLine()) != null) { textSent = sentLine; // Log.e("sentLine*****", "sentLine:" + sentLine); } } catch (IOException e) { e.printStackTrace(); // Log.e("IOException*****", e.toString()); } // Log.e("BytesManualEnd*****", "localUid:" + localUid); return Long.valueOf(textReceived).longValue() + Long.valueOf(textSent).longValue(); } 分类: Android Pro 本文转自demoblog博客园博客,原文链接http://www.cnblogs.com/0616--ataozhijia/p/6628462.html如需转载请自行联系原作者 demoblog
在之前的文章,我们重点讲了Android-Universal-Image-Loader的三个主要组件,现在我们终于可以开始使用它了。 Android-Universal-Image-Loader有四个重载方法 void displayImage(String url, ImageView view) void displayImage(String url, ImageView view, DisplayImageOptions options) void displayImage(String url, ImageView view, ImageLoadingListener listener) void displayImage(String url, ImageView view, DisplayImageOptions options, ImageLoadingListener listener) 第一个重载方法 所有东西都很简单。url就是图片的下载地址,ImageView就是需要显示它的imageView控件。这个ViewOption(DisplayOptions)将使用默认配置option(defaultDisplayImageOptions(…)) 第二个重载方法 我们可以针对特定的任务做一些特定的option。首先,我会先给一个使用特定操作的例子: DisplayImageOptions options = new DisplayImageOptions.Builder() .showStubImage(R.drawable.stub_image) .showImageForEmptyUrl(R.drawable.image_for_empty_url) .cacheInMemory() .cacheOnDisc() .decodingType(DecodingType.MEMORY_SAVING) .build(); • 当真正的图片正在下载,是否需要在ImageView中显示另一张图片,显示什么图片; • 当遇到空的Image URL时,是否需要在ImageView中显示另一张图片,显示什么图片; • 是否在内存中缓存已加载的image; • 是否在磁盘中(file system)缓存已下载的图片; • 要尽可能快(DecodingType.FAST)还是尽可能节约地使用RAM(DecodingType.MEMORY_SAVING) 所以,我们可以在每次调用displayImage()方法的时候将这些option传递过去,或者在初始化中的configuration中定义默认的option,然后程序中就会使用这些特定的options不管你有没有明确地传递DisplayImageOptions。 第三个重载方法 除此之外,我们可以使用ImageLoadingListener监听图片的下载和显示过程: public interface ImageLoadingListener { void onLoadingStarted(); void onLoadingFailed(); void onLoadingComplete(); } 第四个重载方法是最强大的。你可以定制option和监听那些过程 1、为了正常运行,你需要传递给ImageLoader正确的参数。重点是ImageView而不是Image URL。如果你在代码里面创建一个ImageView(而不是使用LayoutInflater),然后将当前的Activity传递给构造函数,而不是application context。 ImageView imageView = new ImageView(getApplicationContext()); // Wrong! ImageView imageView = new ImageView(MyActivity.this); // Correctly ImageView imageView = new ImageView(getActivity()); // Correctly (for Fragments) 2、只有当你想加载ImageView图片比设备的屏幕尺寸更大(例如,对于后续的缩放操作),那么你才需要在configuration配置maxImageWidthForMemoryCache(…)和maxImageHeightForMemoryCache(…)参数。在其他情况下,你不需要特殊的配置:这些参数是因为需要考虑缓存bitmap时屏幕大小和内存大小。 3、明智地在configuration中设置线程池大小:大池(线程数>10)允许多线程同时运行,这将极大地影响UI响应的速度。但是它可以通过将这些线程的优先级设置为更低解决:当ImageLoader运行以及更多的图片加载时,低优先级的线程会让UI更具响应性。UI的相应能力对列表视图(如ListView、GridView)来说至关重要(如平滑滚动时),所以你应该配置threadPoolSize(...) and threadPriority(...)参数为你的应用程序选一个最优的配置。 4、memoryCacheSize(...) and memoryCache(...)设置有相互重叠的地方。在一个configuration对象中只使用其中一个。 5、discCacheSize(…),discCacheFileCount(…)和discCache(…)设置有相互重叠的地方。在一个configuration对象中只使用其中一个。 6、如果在App中使用ImageLoader你总是或几乎总要传递相同的加载option(DisplayImageOptions)给displayImage(…)方法,然而一个合理的解决方案时在ImageLoader配置中将这些相同的加载选项设置为默认配置(defaultDisplayImageOptions(...) method)。然后在调用displayImage(…)时你就可以不指明这些option。如果options没有明确传递给这些方法,那么这个任务就会使用默认的options。 7、对于FAST 和 MEMORY_SAVING两种解析类型没有特别的重大的区别,但是推荐对所有种类的列表视图(GridView、ListView)使用FAST(当你需要显示许多小图片)。为图片查看器使用MEMORY_SAVING(当你需要显示大尺寸的图片时) 参考链接: UNIVERSAL IMAGE LOADER. PART 3 本文转自陈哈哈博客园博客,原文链接http://www.cnblogs.com/kissazi2/p/3901235.html如需转载请自行联系原作者 kissazi2
在手机上尤其需要考虑网络对图片下载的影响,常见的情况是在2G网络、在3G网络需要不同的下载策略,也就是说在慢速网络与快速网络中下载需要考虑不同的策略。一种常见的策略就是Android客户端和服务端相配合的方式,针对慢速网络对图片进行优化(让图片的质量低一点,保证能下载),但是这种情况不在本文讨论的范围中。在本文中主要讨论针对不能改变的服务器图片质量(图片的大小 xx KB),Android-Universal-Image-Loader所采取的下载策略。 需要具体考虑网络情况有:快速、慢速、无网络权限。针对这三种情况,在UIL中分别定义了三种策略。还是让我们从代码入手看看。在《从代码分析Android-Universal-Image-Loader的图片加载、显示流程》我们分析了图片的下载是从LoadAndDisplayImageTask.decodeImage(…)中开始的,其中函数内部调用了getDownloader(),然后在ImageDecoder接口的实现类(BaseImageDecoder)中获取InputStream实现图片的下载和解析。跟进去getDownloader()中看看。 private ImageDownloader getDownloader() { ImageDownloader d; if (engine.isNetworkDenied()) { d = networkDeniedDownloader; } else if (engine.isSlowNetwork()) { d = slowNetworkDownloader; } else { d = downloader; } return d; } networkDeniedDownloader、slowNetworkDownloader、downloader究竟是什么?在LoadAndDisplayImageTask的构造函数中我们看到他们实际是来源于ImageLoaderConfiguration类中对应的networkDeniedDownloader、slowNetworkDownloader、downloader。在ImageLoaderConfiguration的构造函数总,我们发现downloader来源于ImageLoaderConfiguration.Builder,分析后发现它就是一个BaseImageDownloader对象(最后在DefaultConfigurationFactory.createImageDownloade(…)中被初始化)。回到ImageLoaderConfiguration类的构造函数中(如下所示) private ImageLoaderConfiguration(final Builder builder) { resources = builder.context.getResources(); maxImageWidthForMemoryCache = builder.maxImageWidthForMemoryCache; maxImageHeightForMemoryCache = builder.maxImageHeightForMemoryCache; maxImageWidthForDiskCache = builder.maxImageWidthForDiskCache; maxImageHeightForDiskCache = builder.maxImageHeightForDiskCache; processorForDiskCache = builder.processorForDiskCache; taskExecutor = builder.taskExecutor; taskExecutorForCachedImages = builder.taskExecutorForCachedImages; threadPoolSize = builder.threadPoolSize; threadPriority = builder.threadPriority; tasksProcessingType = builder.tasksProcessingType; diskCache = builder.diskCache; memoryCache = builder.memoryCache; defaultDisplayImageOptions = builder.defaultDisplayImageOptions; downloader = builder.downloader; decoder = builder.decoder; customExecutor = builder.customExecutor; customExecutorForCachedImages = builder.customExecutorForCachedImages; networkDeniedDownloader = new NetworkDeniedImageDownloader(downloader); slowNetworkDownloader = new SlowNetworkImageDownloader(downloader); L.writeDebugLogs(builder.writeLogs); } 我们发现networkDeniedDownloader、slowNetworkDownloader都依赖与downloader对象,猜想这两个类应该是对BaseImageDownloader的一个包装。下面我们贴出NetworkDeniedImageDownloader、SlowNetworkImageDownloader的代码(它们在com.nostra13.universalimageloader.core.ImageLoaderConfiguration类中) /** * Decorator. Prevents downloads from network (throws {@link IllegalStateException exception}).<br /> * In most cases this downloader shouldn't be used directly. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.8.0 */ private static class NetworkDeniedImageDownloader implements ImageDownloader { private final ImageDownloader wrappedDownloader; public NetworkDeniedImageDownloader(ImageDownloader wrappedDownloader) { this.wrappedDownloader = wrappedDownloader; } @Override public InputStream getStream(String imageUri, Object extra) throws IOException { switch (Scheme.ofUri(imageUri)) { case HTTP: case HTTPS: throw new IllegalStateException(); default: return wrappedDownloader.getStream(imageUri, extra); } } } /** * Decorator. Handles <a href="http://code.google.com/p/android/issues/detail?id=6066">this problem</a> on slow networks * using {@link com.nostra13.universalimageloader.core.assist.FlushedInputStream}. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.8.1 */ private static class SlowNetworkImageDownloader implements ImageDownloader { private final ImageDownloader wrappedDownloader; public SlowNetworkImageDownloader(ImageDownloader wrappedDownloader) { this.wrappedDownloader = wrappedDownloader; } @Override public InputStream getStream(String imageUri, Object extra) throws IOException { InputStream imageStream = wrappedDownloader.getStream(imageUri, extra); switch (Scheme.ofUri(imageUri)) { case HTTP: case HTTPS: return new FlushedInputStream(imageStream); default: return imageStream; } } } 先看到NetworkDeniedImageDownloader类,这个类中由于对应的是没有网络访问权限(android.permission.INTERNET)的情况,这种情况下Http和Https自然就不能使用了,其他情况(如从本地资源中获取图片)还是可以的。NetworkDeniedImageDownloader.wrappedDownloader对象是什么呢?其实就是我们刚刚ImageLoaderConfiguration构造函数中传入的BaseImageDownloader对象。在看看这个类中的getStream(…)方法。 @Override public InputStream getStream(String imageUri, Object extra) throws IOException { switch (Scheme.ofUri(imageUri)) { case HTTP: case HTTPS: return getStreamFromNetwork(imageUri, extra); case FILE: return getStreamFromFile(imageUri, extra); case CONTENT: return getStreamFromContent(imageUri, extra); case ASSETS: return getStreamFromAssets(imageUri, extra); case DRAWABLE: return getStreamFromDrawable(imageUri, extra); case UNKNOWN: default: return getStreamFromOtherSource(imageUri, extra); } } 从这个函数中,我们可以看到UIL通过Scheme.ofUri(…)分析imageUri,根据ImageUri的类型选择对应的方法进行处理。通过分析Scheme类,我们发现UIL支持以下几种图片获取方式HTTP, HTTPS, FILE, CONTENT, ASSETS, DRAWABLE。 接下来,我们分析一下SlowNetworkImageDownloader.getStream(…)方法,每一次图片的下载最终都会通过BitmapFactory.decodeStream解析成Bitmap,供ImageView显示。我们可以发现这个方法针对慢速网络使用FlushedInputStream来处理。使用这个类的原因是因为在慢速网络中,BitmapFactory.decodeStream无法正确解析完整的图片。具体的可以参考StackOverFlow上的帖子《BitmapFactory.decodeStream always returns null and skia decoder shows decode returned false》和一个Google上的Bug 报告《BitmapFactory.decodeStream() fails if InputStream.skip() does not skip fully》。 网速不慢的下载就直接使用BaseImageDownloader.getStream(…)方法了。 至此,我们已经分析了UIL中图片下载技巧,最后梳理一下。为了应对慢速、正常、访问受限网络,UIL分别 使用了SlowNetworkDownloader、BaseImageLoader、NetworkDeniedDownloader来应对这些策略,在LoadAndDisplayImageTask.getDownloader(…)中通过获取对应的downloader,最后通过LoadAndDisplayImageTask.decodeImage(…)将图片解析出来。 本文转自陈哈哈博客园博客,原文链接http://www.cnblogs.com/kissazi2/p/3911472.html如需转载请自行联系原作者 kissazi2
很经常当我们动态创建某些View时,需要通过获取他们的width和height来确定别的view的布局,但是在onCreate()获取view的width和height会得到0.view.getWidth()和view.getHeight()为0的根本原因是控件还没有完成绘制,你必须等待系统将绘制完View时,才能获得。这种情况当你需要使用动态布局(使用wrap_content或match_parent)就会出现。一般来讲在Activity.onCreate(...)、onResume()方法中都没有办法获取到View的实际宽高。所以,我们必须用一种变通的方法,等到View绘制完成后去获取width和Height。下面有一些可行的解决方案。 1、监听Draw/Layout事件:ViewTreeObserver ViewTreeObserver监听很多不同的界面绘制事件。一般来说OnGlobalLayoutListener就是可以让我们获得到view的width和height的地方.下面onGlobalLayout内的代码会在View完成Layout过程后调用。 1 view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 2 @Override 3 public void onGlobalLayout() { 4 mScrollView.post(new Runnable() { 5 public void run() { 6 view.getHeight(); //height is ready 7 } 8 }); 9 } 10 }); 但是要注意这个方法在每次有些view的Layout发生变化的时候被调用(比如某个View被设置为Invisible),所以在得到你想要的宽高后,记得移除onGlobleLayoutListener: 在 SDK Lvl < 16时使用 public void removeGlobalOnLayoutListener (ViewTreeObserver.OnGlobalLayoutListener victim) 在 SDK Lvl >= 16时使用 public void removeOnGlobalLayoutListener (ViewTreeObserver.OnGlobalLayoutListener victim) 2、将一个runnable添加到Layout队列中:View.post() 这个解决方案是我最喜欢的,但是几乎没人知道有这个方法。简单地说,只要用View.post()一个runnable就可以了。runnable对象中的方法会在View的measure、layout等事件后触发,具体的参考Romain Guy: UI事件队列会按顺序处理事件。在setContentView()被调用后,事件队列中会包含一个要求重新layout的message,所以任何你post到队列中的东西都会在Layout发生变化后执行。 1 final View view=//smth; 2 ... 3 view.post(new Runnable() { 4 @Override 5 public void run() { 6 view.getHeight(); //height is ready 7 } 8 }); 这个方法比ViewTreeObserver好: 1、你的代码只会执行一次,而且你不用在在每次执行后将Observer禁用,省心多了。 2、语法很简单 参考:http://stackoverflow.com/a/3602144/774398http://stackoverflow.com/a/3948036/774398 3、重写View的onLayout方法 这个方法只在某些场景中实用,比如当你所要执行的东西应该作为他的内在逻辑被内聚、模块化在view中,否者这个解决方案就显得十分冗长和笨重。 1 view = new View(this) { 2 @Override 3 protected void onLayout(boolean changed, int l, int t, int r, int b) { 4 super.onLayout(changed, l, t, r, b); 5 view.getHeight(); //height is ready 6 } 7 }; 需要注意的是onLayout方法会调用很多次,所以要考虑好在这个方法中要做什么,或者在第一次执行后禁用掉你的代码。 附加:获取固定宽高 如果你要获取的view的width和height是固定的,那么你可以直接使用: 1 View.getMeasureWidth() 2 View.getMeasureHeight() 但是要注意,这两个方法所获取的width和height可能跟实际draw后的不一样。官方文档解释了不同的原因: View的大小由width和height决定。一个View实际上同时有两种width和height值。 第一种是measure width和measure height。他们定义了view想要在父View中占用多少width和height(详情见Layout)。measured height和width可以通过getMeasuredWidth() 和 getMeasuredHeight()获得。 第二种是width和height,有时候也叫做drawing width和drawing height。这些值定义了view在屏幕上绘制和Layout完成后的实际大小。这些值有可能跟measure width和height不同。width和height可以通过getWidth()和getHeight获得。 参考链接 https://stackoverflow.com/questions/3591784/getwidth-and-getheight-of-view-returns-0/24035591#24035591 本文转自陈哈哈博客园博客,原文链接http://www.cnblogs.com/kissazi2/p/4133927.html如需转载请自行联系原作者 kissazi2
一、程序的基本结构 程序的控制核心是Context类,它持有: ·类型管理器TypeManager,管理该运用程序域加载的命名空间及类型的树,树结构如下: TypeDictionary(Root) |--TypeDictionary | |--TypeDictionary | |--TypeDictionary | | | |--Type | |--Type | | | |--TypeDictionary | |--Type |--Type | 其中TypeDictionary对应的是命名空间,Type对应的是类型。TypeManager还管理一个名为Now的TypeDictionary,表示当前所在的TypeDictionary。 ·AliasCmds ,命令缩写字典。 ·Instances,用户变量字典。 ·CmdDispatcher是命令指派器。控制台获取指令后传给Context。代码: while ((cmd = Console.ReadLine().Trim()) != "exit") { if (!String.IsNullOrEmpty(cmd)) { cxt.Invoke(cmd); } Console.Write(">> "); } Context又传给CmdDispatcher处理。CmdDispatcher解析命令,根据命令的特征选择不同的CmdHandler来处理。目前编写了5个CmdDispatcher: CdClassCmdHandler:进出命名空间的处理,针对cdc指令; ListClassCmdHandler:列出命名空间和类型,针对lsc,dirc指令; ListInstanceCmdHandler:列出用户变量,针对 my 指令; ListAliasCmdHandler:列出指令缩写,针对 alias 指令; CscCmdHandler:编译并运行代码,其它CmdDispatcher 处理不了的都交给它。 CmdDispatcher.Dispatch()方法代码: public void Dispatch() { String[] results = InputCmdString.Split(SPLITS, StringSplitOptions.None); if(results.Length == 0) return; String cmd = results[0]; String mark = String.Empty; IList<String> args = new List<String>(); Int32 argIndex = 1; if (results.Length > 1 && results[1].StartsWith("-")) { argIndex ++; mark = results[1]; } for(;argIndex < results.Length;argIndex++) { args.Add(results[argIndex]); } switch (cmd.ToLower()) { case "debug": // 开启debug开关 Context.Debug = true; break; case "undebug": // 关闭debug开关 Context.Debug = false; break; case "cdc": // 改变命名空间 new CdClassCmdHandler(Context, InputCmdString, mark, args).Run(); break; case "lsc": // 列出命名空间的内容 case "dirc": new ListClassCmdHandler(Context, InputCmdString, mark, args).Run(); break; case "my": // 列出用户变量 new ListInstanceCmdHandler(Context, InputCmdString, mark, args).Run(); break; case "alias": // 列出alias列表 new ListAliasCmdHandler(Context, InputCmdString, mark, args).Run(); break; default: String fullCmd = Context.GetFullCmd(cmd); if (fullCmd != null) // 处理 alias { if (mark != null) fullCmd += " " + mark; if (args != null && args.Count > 0) { foreach(String s in args) { fullCmd += " " + s; } } Context.Invoke(fullCmd); } else // 编译代码并运行 { new CscCmdHandler(Context, InputCmdString).Run(); } break; } return; } 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2008/02/29/1085815.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
我们玩技术,不是被技术玩。Coding是快乐的,而非苦逼的。.Net/C# 这个神器竟然天天有人吐槽。看不下去鸟。 在top10语言中,C#是最优美的语言,没有之一。在top10语言中,C#所可用的标准库及可获得其它库是最强大的之一,这个必须带之一,因为有java在。在top10语言中,C#语言是性能最高的语言之一(开unsafe指针后可以和C一较高下,结果100%是C#败,不过性能差距并不大,C# 非托管程序性能约为C程序的四分之三),在top10语言中,C#是内存管理机制最完善的语言(即可用GC,又可不用GC,虽然C++也能做到,但是要靠第三方库,且使用起来不直观不方便)。此外,C#的类库极其强大,C#调用Native API 极其简单。C#和函数式语言的互动(F#)也是最流畅的,C#与脚本的互动也很简单(Powershell,IronXXX)。 一旦你掌握了.Net和C#。等于手中握了一把神器。当然,这个掌握要求很高,不是那些什么必知啊、精通啊、面试必考的那些东西。而是一些更简单更精髓的东东。 下面,以最近做的两个项目来展示下,神器应该怎么玩。 (1)高性能开发 这个项目是一个对性能要求非常高非常高非常高非常高的实时视频分析项目。因为商业原因,具体细节不方便透露。本项目最开始的版本我是用的纯C#开发,因为是图像与视频项目,我打开了unsafe,用上了指针和非托管内存。项目中有一个核心算法,大部分运行时间耗在这个算法上面,写完之后,我发现,我写的这个C#版算法比国内外同行用C++实现的快几十倍。分辨率差不多的话,他们处理一帧约需要1秒左右,而我只需要几十毫秒。 举这个例子不是想说C#比C++快。实际上根据我这两年对unsafe C# 指针的应用来看,一个写的好的非托管C#代码,性能约是C代码的四分之三。可为什么在实战中有这么好的性能呢,C#下我们可以对开发任务做如下分解:对于性能不关键的地方使用托管程序,对于性能关键的地方使用非托管程序,即使是使用指针的非托管编程,C#下的开发效率也比C和C++快得多。这样一来,我的精力全部集中于关键地方的实现了,我就有更好的心情和更多的时间来进行优化,优化的结果就是,C#版的算法反而比C/C++的快。 也就是说,理论上,C/C++比C# unsafe快30%,但是在实践上,加上时间和资源的约束,反而可能是C#的代码更快。 当然,我这个算法的性能还不够高,达不到实时的目的。最终版本,核心算法用CUDA实现,外围算法用C# unsafe实现的。 如果我在Win7下开发,我不认为我有需要C++的地方。当然,C还是需要的,很多时候,要和硬件打交道。 一般搞图像和视频的都用matlab或C++,或者两个都用。其中,matlab设计算法,C++来重写以提高性能。而用C# unsafe,一个顶这两个,用它来设计算法,工作效率堪比matlab,运行性能堪比C++,且可以直接上线运行。 (2)快速原型开发 .Net 做原型开发是非常爽的。刷刷刷,很快就搞出来了。 这是一个数字版权管理的项目,项目整体方案是我设计的,我要验证这个方案具有可行性,因此,需要快速开发出一个原型出来。一二三四五,五个程序,一天一夜就搞出来了,项目的主要风险全部消灭。钱到手。 其实C#还有些新玩法。因为C# unsafe代码和C代码很像,我在想,能不能写个工具直接将C#算法代码翻译为C代码,为C带来强大的生产力。C是万金油,哪里都能用。 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2012/06/12/2546009.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
一个诡异的问题。服务端控件绑定后台变量值无效。 这是一段Asp.Net代码 <div> <input type="text" name="name" runat="Server" value="<%=IMaxPage %>" /> </div> 后台代码 public partial class _Default : System.Web.UI.Page { protected int IMaxPage = 0; protected void Page_Load(object sender, EventArgs e) { } } 没有显示出期待的值 0.而是直接显示出调用后台的代码。 而当我将控件改成非服务端控件后,可以正常绑定。 代码如下: <div> <input type="text" name="name" runat="Server" value="<%=IMaxPage %>" /> </div> 问题是已经解决了,但是有人知道具体为什么加上runat="Server" 后绑定后台数据<%= **** %>无效。请麻烦告知我一下。 本文转自陈哈哈博客园博客,原文链接http://www.cnblogs.com/kissazi2/archive/2012/09/19/2693434.html如需转载请自行联系原作者 kissazi2
同样的算法,为什么会有数量级的性能差异?问题起源于几个月前与一位网友的探讨。这位网友在写婚纱抠图程序。一般来说,婚纱摄影的图像都较大,甚至大至几千万像素。这位网友使用C#开发,他的问题就在于性能。当时建议他使用XNA开发,但问题又出来了:加载一副图像的时间竟需要好几秒!而我自己写的C#加载几千万像素图像及图像转换操作,都是瞬间完成。为什么会有如此大的差别呢?这就是本文要说的。 问题主要处在程序的局部性和缓存命中上。我们把图像类抽象一下: Bitmap { Width,Height; Data; } 一般来说,它在内存中被分为两块存放: 程序会分配一大块内存,存储具体的图像数据,然后再为Bitmap分配小块内存,储存Width、Height及对图像数据的引用。 这样一来:对于图像的操作的两种写法,性能上就会存在差异: 写法A: for(int y = 0; y<xxx.Height; y++) { for(int x = 0; x < xxx.Width; x++) { Data[x,y] = .... } } 写法B: int width = xxx.Width; int height = xxx.Height; for(int y = 0; y<height ; y++) { for(int x = 0; x <width ; x++) { Data[x,y] = .... } } 写法B中,使用的是在栈中的变量width和height,因此较写法A具有更好的局部性。下面,用实验测试下这两种写法究竟有多大的性能差异。 ==== 实验1:对托管内存中3000万像素图像的赋值操作 public class Image { public int Width { get; set; } public int Height { get; set; } } public class Bitmap : Image { public int[] Data; public Bitmap(int width, int height) { this.Width = width; this.Height = height; Data = new int[width*height]; } public void Fill(int value) { int height = Height; int width = Width; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { Data[y * width + x] = value; } } } public void FillEx(int value) { for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { Data[y * Width + x] = value; } } } public static void Test() { Bitmap img = new Bitmap(5000, 6000); CodeTimer.Time("Fill", 1, () => { img.Fill(1); }); CodeTimer.Time("FillEx", 1, () => { img.FillEx(2); }); CodeTimer.Time("Fill", 1, () => { img.Fill(1); }); CodeTimer.Time("FillEx", 1, () => { img.FillEx(2); }); CodeTimer.Time("Fill", 1, () => { img.Fill(1); }); CodeTimer.Time("FillEx", 1, () => { img.FillEx(2); }); CodeTimer.Time("Fill", 1, () => { img.Fill(1); }); CodeTimer.Time("FillEx", 1, () => { img.FillEx(2); }); } } 结果见下表(单位ms)。 1 2 3 4 Fill 126(82) 83 84 85 FillEx 100(141) 99 100 99 “1”列结果中括号数据是将Fill和FillEx执行顺序互换后的测试结果。可以发现,同一个类中,首次执行的方法吃点亏。对于这个测试来说,吃 40 ms 的亏。比较2,3,4可以看出,FillEx要比Fill慢一点。 ==== 实验2:对非托管内存中3000万像素图像的赋值操作 public class UnmanagedBitmap : Image { public IntPtr Data; public UnmanagedBitmap(int width, int height) { this.Width = width; this.Height = height; Data = Marshal.AllocHGlobal(sizeof(int) * width * height); } public unsafe void Fill(int value) { int height = Height; int width = Width; int* p = (int*)Data; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { *p = value; p++; } } } public unsafe void FillEx(int value) { int* p = (int*)Data; for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { *p = value; p++; } } } public static void Test() { UnmanagedBitmap img = new UnmanagedBitmap(5000, 6000); CodeTimer.Time("Fill", 1, () => { img.Fill(1); }); CodeTimer.Time("FillEx", 1, () => { img.FillEx(2); }); CodeTimer.Time("Fill", 1, () => { img.Fill(1); }); CodeTimer.Time("FillEx", 1, () => { img.FillEx(2); }); CodeTimer.Time("Fill", 1, () => { img.Fill(1); }); CodeTimer.Time("FillEx", 1, () => { img.FillEx(2); }); CodeTimer.Time("Fill", 1, () => { img.Fill(1); }); CodeTimer.Time("FillEx", 1, () => { img.FillEx(2); }); } } 测试结果: 1 2 3 4 Fill 128(83) 93 84 84 FillEx 88(123) 90 84 84 可看出,Fill和FillEx几乎没有差别。 ==== 实验1中,FillEx的循环为: for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { Data[y * Width + x] = value; } } 实验2中,FillEx的循环为: public unsafe void FillEx(int value) { int* p = (int*)Data; for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { *p = value; p++; } } } 对比这两段代码,参照实验结果可以看出,在for循环条件中的Width和Height属性,jit做了特殊的处理,应该是缓存起来了,因此,实验2中的Fill和FillEx才没出现性能差别。而在循环体中所使用的Width属性,没有缓存起来,导致实验1中,Fill和FillEx性能有可见的差异。 尽管如此,实验1所显示的两种写法的性能差别并不大。下面,请看实验3。 ==== 实验3:两种写法产生数量级性能差异的实验 本实验中所使用的核心类见《发布我的高性能纯C#图像处理基本类,顺便也挑战一下极限。:)》。具体代码可在http://smartimage.googlecode.com/svn/trunk/ 下载。 测试的两个方法如下,两个方法的不同之处我用红色显著标示出来: public unsafe void ToBitmap(Bitmap map) { if (map == null) throw new ArgumentNullException("map"); if (map.Width != this.Width || map.Height != this.Height) { throw new ArgumentException("尺寸不匹配."); } if (map.PixelFormat != PixelFormat.Format32bppArgb) { throw new ArgumentException("只支持 Format32bppArgb 格式。 "); } Int32 step = SizeOfT(); Byte* t = (Byte*)StartIntPtr; BitmapData data = map.LockBits(new Rectangle(0, 0, map.Width, map.Height), ImageLockMode.ReadWrite, map.PixelFormat); try { int width = map.Width; int height = map.Height; Byte* line = (Byte*)data.Scan0; for (int h = 0; h < height; h++) { Argb32* c = (Argb32*)line; for (int w = 0; w < width; w++) { m_converter.Copy(t, c); t += step; c++; } line += data.Stride; } } finally { map.UnlockBits(data); } } public unsafe void ToBitmapEx(Bitmap map) { if (map == null) throw new ArgumentNullException("map"); if (map.Width != this.Width || map.Height != this.Height) { throw new ArgumentException("尺寸不匹配."); } if (map.PixelFormat != PixelFormat.Format32bppArgb) { throw new ArgumentException("只支持 Format32bppArgb 格式。 "); } Int32 step = SizeOfT(); Byte* t = (Byte*)StartIntPtr; BitmapData data = map.LockBits(new Rectangle(0, 0, map.Width, map.Height), ImageLockMode.ReadWrite, map.PixelFormat); try { Byte* line = (Byte*)data.Scan0; for (int h = 0; h < map.Height; h++) { Argb32* c = (Argb32*)line; for (int w = 0; w < map.Width; w++) { m_converter.Copy(t, c); t += step; c++; } line += data.Stride; } } finally { map.UnlockBits(data); } } 测试代码: public static void Test() { ImageArgb32 src = new ImageArgb32(5000, 6000); System.Drawing.Bitmap dst = new System.Drawing.Bitmap(5000, 6000, System.Drawing.Imaging.PixelFormat.Format32bppArgb); CodeTimer.Time("ToBitmap",1,()=>{ src.ToBitmap(dst);}); CodeTimer.Time("ToBitmapEx", 1, () => { src.ToBitmapEx(dst); }); CodeTimer.Time("ToBitmap", 1, () => { src.ToBitmap(dst); }); CodeTimer.Time("ToBitmapEx", 1, () => { src.ToBitmapEx(dst); }); CodeTimer.Time("ToBitmap", 1, () => { src.ToBitmap(dst); }); CodeTimer.Time("ToBitmapEx", 1, () => { src.ToBitmapEx(dst); }); CodeTimer.Time("ToBitmap", 1, () => { src.ToBitmap(dst); }); CodeTimer.Time("ToBitmapEx", 1, () => { src.ToBitmapEx(dst); }); } 测试结果: 1 2 3 4 ToBitmap 354 259 261 260 ToBitmapEx 7451 7441 7440 7445 由于区别实在太显著,我就没有交换执行次序重复实验了。从结果看出,ToBitmap的写法比ToBitmapEx要快近30倍。到这里可以知道,为什么我在前面提到的那个哥们写的程序加载图像耗时数秒? 下面,我们对ToBitmapEx做一个小小的变动,变动内容标红: public unsafe void ToBitmapEx(Bitmap map) { if (map == null) throw new ArgumentNullException("map"); if (map.Width != this.Width || map.Height != this.Height) { throw new ArgumentException("尺寸不匹配."); } if (map.PixelFormat != PixelFormat.Format32bppArgb) { throw new ArgumentException("只支持 Format32bppArgb 格式。 "); } Int32 step = SizeOfT(); Byte* t = (Byte*)StartIntPtr; BitmapData data = map.LockBits(new Rectangle(0, 0, map.Width, map.Height), ImageLockMode.ReadWrite, map.PixelFormat); try { Byte* line = (Byte*)data.Scan0; int width = map.Width; for (int h = 0; h < map.Height; h++) { Argb32* c = (Argb32*)line; for (int w = 0; w < width; w++) { m_converter.Copy(t, c); t += step; c++; } line += data.Stride; } } finally { map.UnlockBits(data); } } 测试结果: 1 2 3 4 ToBitmap 313(263) 261 261 260 ToBitmapEx 268(313) 261 264 261 可以看出两者结果已经几乎一样了。 ==== 总结: (1)部分情况(实验2),jit可以对程序的局部性做完全优化。 (2)部分情况(实验1),jit可以对程序的局部性做部分优化。 (3)部分情况(实验3),jit对程序的局部性不做优化。 编译优化的原则比较保守,它首先需要保证正确性。比如下面这段代码: for(int i=0; i< xxx.Width; i++) { xxx.Width = 3; } jit就不能简单优化成: int w = xxx.Width; for(int i=0; i< w; i++) { xxx.Width = 3; } 而实际情况可能比这种情况更复杂,jit优化会非常谨慎,很难达到最优效果。在写高性能程序时,不应该依托于jit的优化。对于实验3这种情况,直接把需要使用的分散在内存中各处的数据缓存到栈中即可。 ==== 多说几句。C#程序中出现的性能问题一般来说和语言与底层机制关系不大。UI性能低下的根源应该在于过度封装,如果有第三方轻量级UI库,那性能肯定是棒棒的。其它方面的性能问题主要还是和设计有关。C#程序和C/C++的性能差异最主要的区别是关注点不一样,C/C++的公用的库设计的一个很重要的目标就是性能,而C#目前的主要的库在设计时偏好是其它方面,C#程序员在写程序时偏好也是其它方面。一个良好设计的C#程序的性能应该不低于C/C++程序的50%。对于复杂程序,由于C/C++的设计复杂度较高,在同等时间内,C#程序的设计应该优秀于C/C++程序,因此在性能上应该达到C/C++的70%才对。 《大唐双龙传》中,寇仲、徐子陵的井中月至境,师仙子的剑心通明,石之轩的入微,伏难陀的梵我不二(tmd黄易这个老小子竟然没给我家可爱的婠婠的天魔功十八层取一个类似井中月至境、剑心通明、入微、梵我不二这样响亮的名字!),都是相通的。 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2010/07/02/1769616.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
1、简介 能够动态执行 C# 代码是一件很酷的功能,比如,我们可以在控制台中输入一行 C# 代码,然后程序自动编译并执行这一行代码,将结果显示给我们。这差不多就是一个最简单的 C# 代码解释器了。 动态执行 C# 代码又是一件很有用的功能,比如,我们可以将某些代码写在某个文件之中,由程序集在执行时进行加载,改变这些代码不用中止程序,当程序再次加载这些代码时,就自动执行的是新代码了。 下面,我将在写一个简单C# 代码解释器,然后将在 C# 代码解释器之中加入动态代码与解释器环境间的动态交互机制,来演示一个很好很强大的应用。 2、简单的 C# 代码解释器 关于如何动态执行 C# 代码在 Jailu.Net 的《如何用C#动态编译、执行代码》一文中讲述的很清晰。采用该文所述方式写一个 C# 代码解释器: using System;using System.Collections.Generic;using System.Reflection;using System.Globalization;using Microsoft.CSharp;using System.CodeDom;using System.CodeDom.Compiler;using System.Text;using System.IO;using System.Xml;namespace Test{ class Program { static void Main(string[] args) { Console.Write(">> "); String cmd; Context cxt = new Context(); while ((cmd = Console.ReadLine().Trim()) != "exit") { if (!String.IsNullOrEmpty(cmd)) { Console.WriteLine(); cxt.Invoke(cmd); } Console.Write("\n>> "); } } } public class Context { public CSharpCodeProvider CodeProvider { get; set; } public IDictionary<String, Assembly> Assemblys { get; set; } public Context() { CodeProvider = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } }); Assemblys = new Dictionary<String, Assembly>(); Assembly[] al = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly a in al) { AddAssembly(a); } AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(CurrentDomain_AssemblyLoad); } private void AddAssembly(Assembly a) { if (a != null) { Assemblys.Add(a.FullName, a); } } void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args) { Assembly a = args.LoadedAssembly; if (!Assemblys.ContainsKey(a.FullName)) { AddAssembly(a); } } public CompilerParameters CreateCompilerParameters() { CompilerParameters cp = new CompilerParameters(); cp.GenerateExecutable = false; cp.GenerateInMemory = true; if (Assemblys != null) { foreach (Assembly a in Assemblys.Values) { cp.ReferencedAssemblies.Add(a.Location); } } return cp; } public void Invoke(String cmd) { String inputCmdString = cmd.Trim(); if (String.IsNullOrEmpty(inputCmdString)) return; String fullCmd = BuildFullCmd(inputCmdString); CompilerResults cr = CodeProvider.CompileAssemblyFromSource(CreateCompilerParameters(), fullCmd); if (cr.Errors.HasErrors) { Boolean recompileSwitch = true; foreach (CompilerError err in cr.Errors) { //CS0201 : Only assignment, call, increment, decrement, and new object expressions can be //used as a statement if (!err.ErrorNumber.Equals("CS0201")) { recompileSwitch = false; break; } } // 重新编译 if (recompileSwitch) { String dynaName = "TempArg_Dynamic_" + DateTime.Now.Ticks.ToString(); inputCmdString = String.Format(" var {0} = ", dynaName) + inputCmdString; inputCmdString += ";\n System.Console.WriteLine(" + dynaName + ");"; fullCmd = BuildFullCmd(inputCmdString); cr = CodeProvider.CompileAssemblyFromSource(CreateCompilerParameters(), fullCmd); } if (cr.Errors.HasErrors) { Console.WriteLine("编译错误:"); foreach (CompilerError err in cr.Errors) { Console.WriteLine(err.ErrorNumber); Console.WriteLine(err.ErrorText); } return; } } Assembly assem = cr.CompiledAssembly; Object dynamicObject = assem.CreateInstance("Test.DynamicClass"); Type t = assem.GetType("Test.DynamicClass"); MethodInfo minfo = t.GetMethod("MethodInstance"); minfo.Invoke(dynamicObject, null); } private String BuildFullCmd(String inputCmdString) { String fullCmd = String.Empty; fullCmd += @" namespace Test { public class DynamicClass { public void MethodInstance() { " + inputCmdString + @"; } } }"; return fullCmd; } }} 编译执行后就得到一个傻傻的 C# 代码解析器,也可以当一个简单的计算器用:3、解释器与所解释的代码之间进行变量交互 如果将所解释的代码中的某些变量储存下来,供给以后的代码用,这一解释器的功能又会强大很多。假设这类变量名称以$打头,如: $myblogname = “http://xiaotie.cnblogs.com” 将在解释器环境中定义(如果该变量未存在)或赋值于(如果该变量已存在)一个名为 myblogname 的字符串变量,指向字符串“http://xiaotie.cnblogs.com”。而,System.Console.WriteLine($myblogname)则取出并打印出字符串该变量所引用的。 简单说来,也就是让所解释的代码中能够初始化并引用解释器中的变量。 如何实现呢?这是本文的重点。 首先,在 Context 类中定义一个SortedDictionary储存变量,并提供索引访问: public SortedDictionary<String, Object> Instances { get; set; } public Object this[String instanceName] { get { if (Instances.ContainsKey(instanceName)) { return Instances[instanceName]; } else { return null; } } set { if (Instances.ContainsKey(instanceName)) { Instances.Remove(instanceName); } Instances.Add(instanceName, value); } } BuildFullCmd方法改变为: private String BuildFullCmd(String inputCmdString) { String fullCmd = String.Empty; fullCmd += @" using Test; public class DynamicClass { private Context m_context; public void MethodInstance(Context context) { m_context = context; " + inputCmdString + @"; } }"; return fullCmd; } 这样,在动态生成的对象中,便可以引用Context对象。 对于inputCmdString 中未定义的外部变量,在第一次遇见时将$argname替换为一个随机生成的内部变量,在代码的最后,将这个内部变量储存在 Context 中。 虽然通过 (Context[argname].GetType())(Context[argname]) 便可引用外部变量 $argname,但是这样引用赋值时,编译器会报错。解决这个问题需要一个新的类: public class ObjectHelper<T> { private String m_objName; public Context Context { get; private set; } public T Obj { get { Object obj = Context[m_objName]; return (T)obj; } set { Context[m_objName] = value; } } public ObjectHelper(Context cxt, String objName) { m_objName = objName; Context = cxt; } } 将inputCmdString中的外部变量$argname统一替换为(new ObjectHelper <m_context[“argname”].GetType()> (m_context, “argname”)).Obj" 即可实现在动态代码中对已定义外部变量的引用。 上述对inputCmdString的预处理代码为: Regex re; // 处理未初始化的环境变量 re = new Regex(@"^(\$)(\w)+"); if (inputCmdString != null) { Match m = re.Match(inputCmdString); if (m != null && m.Length > 1) { String outArgName = inputCmdString.Substring(m.Index, m.Length).Substring(1); if (this[outArgName] == null) { String innerArgName = "TempArg_" + outArgName; inputCmdString = "var " + inputCmdString.Replace("$" + outArgName, innerArgName); inputCmdString += ";m_context[\"" + outArgName + "\"]=" + innerArgName + ";"; } } } // 处理其它环境变量 re = new Regex(@"(\$)(\w)+"); IDictionary<String, String> ArgsList = new Dictionary<String, String>(); if (inputCmdString != null) { MatchCollection mc = re.Matches(inputCmdString); if (mc != null) { foreach (Match m in mc) { if (m.Length > 1) { String outArgName = inputCmdString.Substring(m.Index, m.Length).Substring(1); if (!ArgsList.ContainsKey(outArgName)) { Object obj = this[outArgName]; if (obj == null) throw new Exception("不存在环境变量" + outArgName); String innerArgName = String.Format(@"(new ObjectHelper<{0}>(m_context,""{1}"")).Obj", obj.GetType(), outArgName); ArgsList.Add(outArgName, innerArgName); } } } } foreach (String outArg in ArgsList.Keys) { inputCmdString = inputCmdString.Replace("$" + outArg, ArgsList[outArg]); } } 这里做了个简化,即定义外部变量的格式必须为 $argname = value,其中 $argname 必须在行首。 这样,对于:$myblogname = "http://xiaotie.cnblogs.com". 因为 myblogname 变量不存在,被解析为: var TempArg_myblogname = "http://xiaotie.cnblogs.com"; m_context["myblogname"]=TempArg_myblogname;; 定义后,当再出现 $myblogname,则被解析为 (new ObjectHelper<System.String>(m_context,"myblogname")).Obj; 看看实际执行情况: 完整代码于此下载。 4、一个很好很强大的应用—---打入.Net 程序内部,看看其执行情况。 采用上面的方法改进了 OrcShell(OrcShell详情见我前面的随笔: 实现简单的CSharpShell -- OrcShell )。新版 OrcShell 程序于此下载(需要.Net 3.5)。基本上是一个可用的 小型 .Net Framework Shell 了,可以动态的查看、创建、执行 .Net 的类型了。不过,自动提示与完成功能还没有做,使用起来还是较不方便的。 help 指令可以查看常用指令列表: lsc 列出当前命名空间中的类型和下属命名空间。格式: lsc [name] dirc 同 lsc cdc 改变当前的命名空间,格式: cdc [.|..|name] my 查看全部变量。格式:my。可通过$ArgName来引用变量。 alias 查看全部别名。格式:alias use 添加命名空间。格式: use [namespace] unuse 移除命名空间。格式:unuse [namespace] import 导入程序集,有两种导入方式: "import -f [fullpath]","import [partname]" 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2008/03/01/1087448.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)
软件名称:RefNavigator 软件主页:http://www.refnavigator.com/index.html 授权方式:收费 技术领域:.Net Framework 2.0; Word Addin; Web Scraper; Rule Design 功能: (1)基本的文献管理功能 (2)可以搜索几个常用的web站点:ACM,Arxiv,CiteSeerX,GoogleScholar,IEEExplore,Pubmed,Scidirect,Scirus (3)可以搜索Z3950站点(Z3950站点太多了,就没加进去。可以自己添加) (4)支持导入自己本机的文件;内嵌PDF浏览器 (5)虚拟目录系统 (6)有Tag管理功能,方便将文献按不同的方式来显示 (7)支持导入BibTex,EndNote,Ris,RefWork等常见格式的文献 (8)支持Word插件:Word 2003与Word2007。插入Citation和生成Bibliography。 (9)支持1800多种Style (10)支持Windows 2000,XP,2003,Vista 截屏: 本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2009/01/13/1374577.html如需转载请自行联系原作者 xiaotie 集异璧实验室(GEBLAB)