PHP error_reporting() 函数功能详解

定义和用法:
error_reporting() 设置 PHP 的报错级别并返回当前级别。

函数语法:
error_reporting(report_level)

如果参数 level 未指定,当前报错级别将被返回。下面几项是 level 可能的值:
值 常量 描述
1 E_ERROR 致命的运行错误。错误无法恢复,暂停执行脚本。
2 E_WARNING 运行时警告(非致命性错误)。非致命的运行错误,脚本执行不会停止。
4 E_PARSE 编译时解析错误。解析错误只由分析器产生。
8 E_NOTICE 运行时提醒(这些经常是你代码中的bug引起的,也可能是有意的行为造成的。)
16 E_CORE_ERROR PHP启动时初始化过程中的致命错误。
32 E_CORE_WARNING PHP启动时初始化过程中的警告(非致命性错)。
64 E_COMPILE_ERROR 编译时致命性错。这就像由Zend脚本引擎生成了一个E_ERROR。
128 E_COMPILE_WARNING 编译时警告(非致命性错)。这就像由Zend脚本引擎生成了一个E_WARNING警告。
256 E_USER_ERROR 用户自定义的错误消息。这就像由使用PHP函数trigger_error(程序员设置E_ERROR)
512 E_USER_WARNING 用户自定义的警告消息。这就像由使用PHP函数trigger_error(程序员设定的一个E_WARNING警告)
1024 E_USER_NOTICE 用户自定义的提醒消息。这就像一个由使用PHP函数trigger_error(程序员一个E_NOTICE集)
2048 E_STRICT 编码标准化警告。允许PHP建议如何修改代码以确保最佳的互操作性向前兼容性。
4096 E_RECOVERABLE_ERROR 开捕致命错误。这就像一个E_ERROR,但可以通过用户定义的处理捕获(又见set_error_handler())
8191 E_ALL 所有的错误和警告(不包括 E_STRICT) (E_STRICT will be part of E_ALL as of PHP 6.0)
例子:
任意数目的以上选项都可以用“或”来连接(用 OR 或 |),这样可以报告所有需要的各级别错误。
例如,下面的代码关闭了用户自定义的错误和警告,执行了某些操作,然后恢复到原始的报错级别:
<?php
//禁用错误报告
error_reporting(0);

//报告运行时错误
error_reporting(E_ERROR | E_WARNING | E_PARSE);

//报告所有错误
error_reporting(E_ALL);
?>

Android Activity的生命周期

activity类处于android.app包中,继承体系如下:

1.java.lang.Object

2.android.content.Context

3.android.app.ApplicationContext

4.android.app.Activity

activity是单独的,用于处理用户操作。几乎所有的activity都要和用户打交道,所以activity类创建了一个窗口,开发人员可以通过setContentView(View)接口把UI放到activity创建的窗口上,当 activity指向全屏窗口时,也可以用其他方式实现:作为漂浮窗口(通过windowIsFloating的主题集合),或者嵌入到其他的 activity(使用ActivityGroup)。大部分的Activity子类都需要实现以下两个接口:

  • onCreate(Bundle)接口是初始化activity的地方. 在这儿通常可以调用setContentView(int)设置在资源文件中定义的UI, 使用findViewById(int) 可以获得UI中定义的窗口.
  • onPause()接口是使用者准备离开activity的地方,在这儿,任何的修改都应该被提交(通常用于ContentProvider保存数据).

为了能够使用Context.startActivity(),所有的activity类都必须在AndroidManifest.xml文件中定义有相关的“activity”项。

activity类是Android 应用生命周期的重要部分。

Activity生命周期

在系统中的Activity被一个Activity栈所管理。当一个新的Activity启动时,将被放置到栈顶,成为运行中的Activity,前一个Activity保留在栈中,不再放到前台,直到新的Activity退出为止。

Activity有四种本质区别的状态:

  1. 在屏幕的前台(Activity栈顶),叫做活动状态或者运行状态(active or running)
  2. 如果一个Activity失去焦点,但是依然可见(一个新的非全屏的Activity 或者一个透明的Activity 被放置在栈顶),叫做暂停状态(Paused)。一个暂停状态的Activity依然保持活力(保持所有的状态,成员信息,和窗口管理器保持连接),但是在系统内存极端低下的时候将被杀掉。
  3. 如果一个Activity被另外的Activity完全覆盖掉,叫做停止状态(Stopped)。它依然保持所有状态和成员信息,但是它不再可见,所以它的窗口被隐藏,当系统内存需要被用在其他地方的时候,Stopped的Activity将被杀掉。
  4. 如果一个Activity是Paused或者Stopped状态,系统可以将该Activity从内存中删除,Android系统采用两种方式进行删除,要么要求该Activity结束,要么直接杀掉它的进程。当该Activity再次显示给用户时,它必须重新开始和重置前面的状态。

下面的图显示了Activity的重要状态转换,矩形框表明Activity在状态转换之间的回调接口,开发人员可以重载实现以便执行相关代码,带有颜色的椭圆形表明Activity所处的状态。

activity

在上图中,Activity有三个关键的循环:

  1. 整个的生命周期,从onCreate(Bundle)开始到onDestroy()结束。Activity在onCreate()设置所有的“全局”状态,在onDestory()释放所有的资源。例如:某个Activity有一个在后台运行的线程,用于从网络下载数据,则该Activity可以在onCreate()中创建线程,在onDestory()中停止线程。
  2. 可见的生命周期,从onStart()开始到onStop()结束。在这段时间,可以看到Activity在屏幕上,尽管有可能不在前台,不能和用户交互。在这两个接口之间,需要保持显示给用户的UI数据和资源等,例如:可以在onStart中注册一个IntentReceiver来监听数据变化导致UI的变动,当不再需要显示时候,可以在onStop()中注销它。onStart(),onStop()都可以被多次调用,因为Activity随时可以在可见和隐藏之间转换。
  3. 前台的生命周期,从onResume()开始到onPause()结束。在这段时间里,该Activity处于所有 Activity的最前面,和用户进行交互。Activity可以经常性地在resumed和paused状态之间切换,例如:当设备准备休眠时,当一个 Activity处理结果被分发时,当一个新的Intent被分发时。所以在这些接口方法中的代码应该属于非常轻量级的。

Activity的整个生命周期都定义在下面的接口方法中,所有方法都可以被重载。所有的Activity都需要实现 onCreate(Bundle)去初始化设置,大部分Activity需要实现onPause()去提交更改过的数据,当前大部分的Activity也需要实现onFreeze()接口,以便恢复在onCreate(Bundle)里面设置的状态。

public class Activity extends ApplicationContext {
	protected void onCreate(Bundle icicle);
	protected void onStart();
	protected void onRestart();
	protected void onResume();
	protected void onFreeze(Bundle outIcicle);
	protected void onPause();
	protected void onStop();
	protected void onDestroy();

}

《三体1》10.大史

大史在汪淼身边坐下了,将一把车钥匙递给他,“东单口儿上就随便停车,我晚一步就让交警拖走了。”

大史啊,要知道你一直跟在我后面,我至少会有些安慰的。汪淼心里说,但自尊使他没将这话说出口。他接过大史递过来的一枝烟,点上后,抽了戒烟几年后的第一口。

“怎么样老弟,扛不住了吧?我说你不成吧,你还硬充六根脚指头。”

“你不会明白的。”汪淼猛抽几口烟说。

“我是太明白了……那好,去吃饭吧。”

“我不想吃。”

“那去喝酒,我请你!”

汪淼于是上了大史的车,开到附近一家小饭店,天还早,店里没什么人。

“二斤爆肚,一瓶二锅头!”大史喊道,头也不抬,显然对这儿很熟了。

看到端上来的两大盘黑乎乎的东西,汪淼空空的胃翻腾起来,差点吐出来。大史又给他要豆浆和油条,汪淼强迫自己吃了点儿,然后和大史一杯接一杯地喝了起来。他感觉自己轻飘飘的,话也多了起来,将这三天的事情全部向大史说了,虽然他清楚,大史可能都知道,甚至知道的比他还多。

“你是说,宇宙在冲你眨巴眼儿?”大史像吃面条似的吞下半盘爆肚,抬头问。

“这比喻很到位。”

“扯淡。”

“你的无畏来源于无知。”

“还是扯淡,来,干!”

汪淼干了这杯后,感觉世界围绕着自己旋转,只有对面吃爆肚的大史很稳定,他说:“大史啊,你——考虑过一些终极的哲学问题吗?哦,比如说,人类从哪里来,要到哪里去;宇宙从哪里来,要到哪里去之类的。”

“没有。”

“从来没有?”

“从来没有。”

“你总看到过星空吧,难道没有产生过一点敬畏和好奇?”

“我夜里从不看天。”

“怎么可能呢?你们不是常上夜班吗?”

“老弟,我夜里蹲点时要是仰头看天,那监视对象溜了怎么办?”

“我们真没的谈,干!”

“其实啊,我就是看天上的星星也不会去想你那些终极哲学,我要操心的事儿多着呢,要供房子,孩子还要上大学,更不要提那没完没了的案子……我是个一眼能从嘴巴看到屁眼的直肠子,自然讨不得领导欢心,退伍后混了多少年还是这么个熊样儿,要不是能干活,早让人踹出去了……这些还不够我想的,我还有心思看星星想哲学?”

“那倒也是,来,干!”

“不过啊,我倒还真发明了一条终极定理。”

“说说。”

“邪乎到家必有鬼。”

“你这是……什么狗屁定理!”

“我说的‘有鬼’是指没有鬼,是有人在捣鬼。”

“如果你有些起码的科学常识,就无法想象是怎样的力量才能做成这两件事,特别是后一件,在整个宇宙的尺度上,不但用人类现有的科学无法解释,甚至在科学之外我都无法想象。这连超自然都不是,我都不知道是超什么了……”

“还是那句话:扯淡!邪乎事儿我见多了。”

“那你给个建议,下一步我该怎么办?”

“继续喝,喝完了睡觉。”

“好吧。”

……

汪淼不知道自己是怎么回到自己的车上,躺在后座上陷入了无梦的沉睡,感觉时间并不长,但睁开眼睛后,看到太阳已在城市的西边快要落下去了。他走下车,虽然早上喝的酒让他浑身发软,但感觉好多了。他看到,自己正在紫禁城的一角,夕阳照在古老的皇宫上,在护城河中泛起碎金,在他眼中,世界又恢复了古典和稳定。汪淼就这样享受着久违的宁静,直到天色暗下来,那辆他熟悉的黑色桑塔纳从街道上车流中钻出来,径直开过来刹住,大史走了下来。

“睡好了?”大史瓮声瓮气地问。

“是,下一步该怎么办?”

“谁,你吗?去吃晚饭,再喝点儿,喝完接着睡。”

“然后呢?”

“然后?明天你总得去上班吧。”

“倒计时已减到……1091小时了。”

“去他妈的倒计时,你现在首先要保证站直了别趴下,然后才能说别的。”

“大史,你就不能告诉我一些真相吗?就算我求你了。”

大史盯着汪淼看了一会儿,然后仰天一笑,“这话我也对常伟思说过几次,咱俩是难兄难弟。实话告诉你,我他妈的什么也不知道,级别低,他们不告诉我,有时真像在做噩梦。”

“可你知道的总比我多。”

“那好,我现在就把多出来的都告诉你。”大史指了指护城河的河沿,两人在那里找了个地方坐下来。天已经黑了下来,身后是车灯的河流,他们看着自己的影子在河面上长长短短地变幻着。

“干我们这行的,其实就是把好多看上去不相关的事情串联起来,串对了,真相就出来了。前一阵发生过好多事儿,针对科研机构和学术界的犯罪急剧增多,这是从未有过的事儿。你当然知道良湘加速器工地的那起爆炸案,还有那名获诺贝尔的学者被杀的案子……犯罪的动机都很怪,不为钱,不为报复,也没什么政治背景,单纯地搞破坏。还有其他一些犯罪之外的事,比如‘科学边界’和那些学者的自杀等等。环保分子最近的活动也过分活跃,一会儿在工地集会阻止水库和核电站的建设,一会儿又搞什么回归自然的试验社会……还有其他一些看上去是鸡毛蒜皮的事儿——你最近看电影吗?”

“基本不看。”

“最近的几部大片,全土的掉渣,上面青山绿水的,不知哪个年代的帅哥靓妹在里面男耕女织过得挺舒服,用导演的话说,是表现被科技强奸之前的美好生活。比如那部《桃花源》,明摆着拍出来没人看,可就有人硬把几个亿砸进去。还有一个科幻小说征文大赛,最高奖五百万,谁把未来写的最恶心谁就能得奖,然后又砸进去几个亿把那几篇小说拍成电影……奇奇怪怪的邪教也都冒出来,每一个教主都财大气粗……”

“这些与你前面说的有什么关系?”

“得把它们串起来看,当然我以前用不着操这份闲心,但从重案组调到作战中心后,这就是我份内的事儿了。我能把它们串起来,这就是我的天分,连常伟思也不得不服。”

“得出的结论呢?”

“所有这一切,都有且只有一个后台,它想把科学研究彻底搞垮。”

“谁?”

“不知道,真的不知道,但能感觉到它的计划,很气派很全面的一个计划:破坏科研设施,杀害科学家;或让你们自杀,让你们发疯……但主要还是让你们往歪处想,这样你们就变得比一般人还蠢。”

“你最后这句真精辟!”

“同时,还要在社会上把科学搞臭,当然以前也一直有人干这个,但这次绝对是有组织的。”

“我相信你说的。”

“哼,也就是现在吧。你们这些科学精英都看不出来的事,居然被我这个专科毕业的大老粗看出来了?我说出这个想法后,没少被领导和学者们笑话。”

“就是当时你对我说的这些,我也肯定不会笑话你。你知道一些伪科学的事吧,知道那些搞伪科学的最怕什么人吗?”

“科学家呗。”

“错了,世界上有许多一流学者被伪科学骗得团团转,最后还为之摇旗呐喊。但伪科学最怕另一种人,他们很难被骗:魔术师。事实上,大量的伪科学骗局,都是被魔术师揭穿的。比起科学界的书呆子来,你多年的警务和社会经验显然更有能力觉察这种大规模犯罪。”

“其实比我聪明的人还是有的,这种事早就被上面觉察了,我开始时还被笑话是没找对地方,再后来就被老连长招到了这儿,不过也只是干些跑腿的事儿……好了,这就是我比你多知道的那点儿。”

“有个疑问:这些与军方有什么关系呢?”

“我也纳闷,问他们,他们就说战争爆发了,战争当然是军队的事儿。我和你一样,开始以为他们是在说梦话。可他们真没开玩笑,现在部队确实处于临战状态。我们这样的作战中心,在全球有二十多个,上面还有一级,但谁都不知道是什么。”

“敌人是谁?”

“不知道。北约军官进驻总参的作战室了,五角大楼里也有一大帮子解放军,谁他妈知道谁是敌人?”

“这也太离奇了,你说得这都是真的?!”

“我在部队的好几个老战友现在都混成将军了,所以知道一些。”

“这么大的事,新闻媒体居然没有一点儿反应?”

“这又是一个了不得的现象:所有国家同时保密,而且做的这么严实。我现在可以肯定一点:敌人是个狠角色,上面害怕了!我太熟悉常伟思了,从他那里就能看出来,他是天塌下来都不怕的人,但现在塌下来的可能不止是天了。他们被吓得够呛,他们根本没有信心战胜那个敌人。”

“要这样,那太可怕了。”

“不过谁都有怕的东西,那个狠角色也有;越厉害的角色,它怕的东西对它就越致命。”

“那它怕什么?”

“怕你们,怕科学家。而且奇怪的是,你们研究的东西越是没有实际用处,越是天马行空不着边际,像杨冬那号的,它就越怕,比你怕宇宙眨眼更怕,所以才出手这么狠。要是杀你们有用,它早就把你们杀光了,但最有效的办法还是扰乱你们的思想,人死了还会有别人,但思想乱了,科学就完了。”

“你是说它怕基础科学?”

“是,基础科学。”

“我和杨冬的研究差别很大,纳米材料不是基础科学,只是一个高强度材料,能威胁到那种力量?!”

“你还真是个特例,像你这种搞应用研究的,它现在一般还不打扰,也许你那些材料中真有让它怕的东西。”

“那我该怎么办?”

“去上班,研究下去,这就是对它最大的打击,别管什么鸡巴倒计时。如果下了班想放松,也可以玩玩那个游戏,能打通它最好。”

“游戏?《三体》?难道它与这些也有关系?!”

“有关系,我看作战中心的好几个专家也在玩儿,那玩意儿不是一般的游戏,我这样无知无畏的人玩不了,还真得你这样有知识的才行。”

“哦,没别的了?”

“没了,有的时候我再告诉你,手机要一直开着。老弟,可得站直啰!害怕的时候就想想我那条终极定理。”

汪淼连谢谢都没来的及说,大史就上车走了。

定时删除日志脚本

对于web应用服务器,tomcat下或者其他的服务器,每天都有大量的日志,有时候不注意就会爆满磁盘导致系统崩溃,所以需要一个定时任务将几天前的日志给清除掉,linux下可写个脚本完成这样的简单任务;

1.删除7天前的日志脚本如下

#!/bin/sh
find /usr/local/tomcat7/logs/* -mtime +7 -exec rm {} \;

将上面的脚本保存在一个脚本文件里比如/usr/task/rm_log.sh

2.分配可执行的权限

chmod +x /usr/task/rm_log.sh

3.加入定时任务

编辑文件

vi /etc/crontab

加入下面一行定时任务,每天23点1分执行

1 23 * * * root /usr/task/rm_log.sh

保存之后执行下面的命令让定时任务开始生效

service crond restart

这样所有事情就已经完成了.

4.oracle备份脚本

#!/bin/sh
#. /home/oracle/.bash_profile
filename=/usr/backup_data/db_full_$(date +%Y%m%d%H%M%S)
exp username/password@dbname owner=ownername compress=y log=$filename.log file=$filename.dmp
/bin/gzip -c $filename.dmp $filename.log > $filename.dmp.gz
rm -f $filename.dmp $filename.log

以上脚本是备份数据库,并且使用gzip进行压缩以减少存储空间,如果只保留近几天的备份文件,可以加上上面删除的脚本,这样就成了只备份几天的数据了。

php引用(&)详解及注意事项

php的引用(就是在变量或者函数、对象等前面加上&符号)

在PHP 中引用的意思是:不同的名字访问同一个变量内容。
与C语言中的指针是有差别的.C语言中的指针里面存储的是变量的内容,在内存中存放的地址。

1.变量的引用

PHP 的引用允许你用两个变量来指向同一个内容

<?
$a=”ABC”;
$b =&$a;
echo $a;//这里输出:ABC
echo $b;//这里输出:ABC
$b=”EFG”;
echo $a;//这里$a的值变为EFG 所以输出EFG
echo $b;//这里输出EFG
?>

2.函数的引用传递(传址调用

<?php
function test(&$a)
{
$a=$a+100;
}
$b=1;
echo $b;//输出1
test($b);   //这里$b传递给函数的其实是$b的变量内容所处的内存地址,通过在函数里改变$a的值 就可以改变$b的值了
echo “<br>”;
echo $b;//输出101
?>

要注意的是,在这里test(1);的话就会出错。

 

注意:

上面的“ test($b); ” 中的$b前面不要加 & 符号,但是在函数“call_user_func_array”中,若要引用传参,就得需要 & 符号,如下代码所示:

<?php

function a(&$b){
$b++;
}
$c=0;

call_user_func_array(‘a’,array(&$c));

echo $c;

//输出 1

?>

3.函数的引用返回

 

先看代码

<?php
function &test()
{
static $b=0;//申明一个静态变量
$b=$b+1;
echo $b;
return $b;
}

$a=test();//这条语句会输出 $b的值 为1
$a=5;
$a=test();//这条语句会输出 $b的值 为2

$a=&test();//这条语句会输出 $b的值 为3
$a=5;
$a=test();//这条语句会输出 $b的值 为6
?>

下面解释下:
通过这种方式$a=test();得到的其实不是函数的引用返回,这跟普通的函数调用没有区别 至于原因: 这是PHP的规定
PHP规定通过$a=&test(); 方式得到的才是函数的引用返回

用上面的例子来解释就是
$a=test()方式调用函数,只是将函数的值赋给$a而已, 而$a做任何改变 都不会影响到函数中的$b
而通过$a=&test()方式调用函数呢, 他的作用是 将return $b中的 $b变量的内存地址与$a变量的内存地址 指向了同一个地方
即产生了相当于这样的效果($a=&$b;) 所以改变$a的值 也同时改变了$b的值 所以在执行了
$a=&test();
$a=5;
以后,$b的值变为了5

这里是为了让大家理解函数的引用返回才使用静态变量的,其实函数的引用返回多用在对象中
4.对象的引用

<?php
class a{
var $abc=”ABC”;
}
$b=new a;
$c=$b;
echo $b->abc;//这里输出ABC
echo $c->abc;//这里输出ABC
$b->abc=”DEF”;
echo $c->abc;//这里输出DEF
?>

以上代码是在PHP5中的运行效果

 

在PHP5中 对象的赋值是个引用的过程。上列中$b=new a; $c=$b; 其实等效于$b=new a; $c=&$b;
PHP5中默认就是通过引用来调用对象, 但有时你可能想建立一个对象的副本,并希望原来的对象的改变不影响到副本 . 为了这样的目的,PHP5定义了一个特殊的方法,称为__clone。

自 PHP 5 起,new 自动返回引用,因此在此使用 =& 已经过时了并且会产生 E_STRICT 级别的消息。

 

 

在php4中,对象的赋值是个拷贝过程,

如:$b=new a,其中new a产生的是一个匿名的a对象实例,而此时的$b是对这个匿名对象的拷贝。同理$c=$b,也是对$b内容的一个拷贝。所以在php4中,为了节省内存空间,$b=new a 一般会改成引用的模式,即 $b=& new a。

 

下面再来个 官方 提供的例子:

在php5中,你不需要额外添加什么东西就可到达“对象引用”的功能:

 

<?php
class foo{
protected $name;
function __construct($str){
$this->name = $str;
}
function __toString(){
return  ‘my name is “‘. $this->name .'” and I live in “‘ . __CLASS__ . ‘”.’ . “\n”;
}
function setName($str){
$this->name = $str;
}
}

class MasterOne{
protected $foo;
function __construct($f){
$this->foo = $f;
}
function __toString(){
return ‘Master: ‘ . __CLASS__ . ‘ | foo: ‘ . $this->foo . “\n”;
}
function setFooName($str){
$this->foo->setName( $str );
}
}

class MasterTwo{
protected $foo;
function __construct($f){
$this->foo = $f;
}
function __toString(){
return ‘Master: ‘ . __CLASS__ . ‘ | foo: ‘ . $this->foo . “\n”;
}
function setFooName($str){
$this->foo->setName( $str );
}
}

$bar = new foo(‘bar’);

print(“\n”);
print(“Only Created \$bar and printing \$bar\n”);
print( $bar );

print(“\n”);
print(“Now \$baz is referenced to \$bar and printing \$bar and \$baz\n”);
$baz =& $bar;
print( $bar );

print(“\n”);
print(“Now Creating MasterOne and Two and passing \$bar to both constructors\n”);
$m1 = new MasterOne( $bar );
$m2 = new MasterTwo( $bar );
print( $m1 );
print( $m2 );

print(“\n”);
print(“Now changing value of \$bar and printing \$bar and \$baz\n”);
$bar->setName(‘baz’);
print( $bar );
print( $baz );

print(“\n”);
print(“Now printing again MasterOne and Two\n”);
print( $m1 );
print( $m2 );

print(“\n”);
print(“Now changing MasterTwo’s foo name and printing again MasterOne and Two\n”);
$m2->setFooName( ‘MasterTwo\’s Foo’ );
print( $m1 );
print( $m2 );

print(“Also printing \$bar and \$baz\n”);
print( $bar );
print( $baz );
?>

 

输出:

Only Created $bar and printing $bar
my name is “bar” and I live in “foo”.

Now $baz is referenced to $bar and printing $bar and $baz
my name is “bar” and I live in “foo”.

Now Creating MasterOne and Two and passing $bar to both constructors
Master: MasterOne | foo: my name is “bar” and I live in “foo”.

Master: MasterTwo | foo: my name is “bar” and I live in “foo”.

Now changing value of $bar and printing $bar and $baz
my name is “baz” and I live in “foo”.
my name is “baz” and I live in “foo”.

Now printing again MasterOne and Two
Master: MasterOne | foo: my name is “baz” and I live in “foo”.

Master: MasterTwo | foo: my name is “baz” and I live in “foo”.

Now changing MasterTwo’s foo name and printing again MasterOne and Two
Master: MasterOne | foo: my name is “MasterTwo’s Foo” and I live in “foo”.

Master: MasterTwo | foo: my name is “MasterTwo’s Foo” and I live in “foo”.

Also printing $bar and $baz
my name is “MasterTwo’s Foo” and I live in “foo”.
my name is “MasterTwo’s Foo” and I live in “foo”.

 

上个例子解析:

$bar = new foo(‘bar’);
$m1 = new MasterOne( $bar );
$m2 = new MasterTwo( $bar );

实例对象$m1与$m2中的$bar是对实例$bar的引用,而非拷贝,这是php5中,对象引用的特点,也就是说
1.$m1或$m2内部,任何对$bar的操作都会影响外部对象实例$bar的相关值。
2.外部对象实例$bar的改变也会影响$m1和$m2内部的$bar的引用相关值。

 

在php4中,要实现如上述的 用一个对象实例去当着另外一个对象的属性时,其等价代码(即引用调用)类似如下:

class foo{
var $bar;
function setBar(&$newBar){
$this->bar =& newBar;
}
}

 

 

 

 

5.引用的作用
如果程序比较大,引用同一个对象的变量比较多,并且希望用完该对象后手工清除它,个人建议用 “&” 方式,然后用$var=null的方式清除. 其它时候还是用php5的默认方式吧. 另外, php5中对于大数组的传递,建议用 “&” 方式, 毕竟节省内存空间使用。

6.取消引用
当你 unset 一个引用,只是断开了变量名和变量内容之间的绑定。这并不意味着变量内容被销毁了。例如:

<?php
$a = 1;
$b =& $a;
unset ($a);
?>

不会 unset $b,只是 $a。

7.global 引用
当用 global $var 声明一个变量时实际上建立了一个到全局变量的引用。也就是说和这样做是相同的:

<?php
$var =& $GLOBALS[“var”];
?>

这意味着,例如,unset $var 不会 unset 全局变量。

如果在一个函数内部给一个声明为 global 的变量赋于一个引用,该引用只在函数内部可见。可以通过使用 $GLOBALS 数组避免这一点。

Example  在函数内引用全局变量

<?php
$var1 = “Example variable”;
$var2 = “”;

function global_references($use_globals)
{
global $var1, $var2;
if (!$use_globals) {
$var2 =& $var1; // visible only inside the function
} else {
$GLOBALS[“var2”] =& $var1; // visible also in global context
}
}

global_references(false);
echo “var2 is set to ‘$var2’\n”; // var2 is set to ”
global_references(true);
echo “var2 is set to ‘$var2’\n”; // var2 is set to ‘Example variable’
?>

global $var; 当成是 $var =& $GLOBALS[‘var’]; 的简写。从而将其它引用赋给 $var 只改变了本地变量的引用。

8.$this
在一个对象的方法中,$this 永远是调用它的对象的引用。

//下面再来个小插曲
php中对于地址的指向(类似指针)功能不是由用户自己来实现的,是由Zend核心实现的,php中引用采用的是“写时拷贝”的原理,就是除非发生写操作,指向同一个地址的变量或者对象是不会被拷贝的。

通俗的讲
1:如果有下面的代码

<?
$a=”ABC”;
$b=&$a;
?>

其实此时 $a与$b都是指向同一内存地址 而并不是$a与$b占用不同的内存

2:如果在上面的代码基础上再加上如下代码

<?php
$a=”EFG”;
?>

由于$a与$b所指向的内存的数据要重新写一次了,此时Zend核心会自动判断 自动为$b生产一个$a的数据拷贝,重新申请一块内存进行存储
php的引用(就是在变量或者函数、对象等前面加上&符号)是个高级话题,新手多注意,正确的理解php的引用很重要,对性能有较大影响,而且理解错误可能导致程序错误!

很 多人误解php中的引用跟C当中的指针一样,事实上并非如此,而且很大差别。C语言中的指针除了在数组传递过程中不用显式申明外,其他都需要使用*进行定 义,而php中对于地址的指向(类似指针)功能不是由用户自己来实现的,是由Zend核心实现的,php中引用采用的是“写时拷贝”的原理,就是除非发生 写操作,指向同一个地址的变量或者对象是不会被拷贝的,比如下面的代码:

 

$a = array(‘a’,’c’…’n’);
$b = $a;

如 果程序仅执行到这里,$a和$b是相同的,但是并没有像C那样,$a和$b占用不同的内存空间,而是指向了同一块内存,这就是php和c的差别,并不需要 写成$b=&$a才表示$b指向$a的内存,zend就已经帮你实现了引用,并且zend会非常智能的帮你去判断什么时候该这样处理,什么时候不 该这样处理。

如果在后面继续写如下代码,增加一个函数,通过引用的方式传递参数,并打印输出数组大小。

 

function printArray(&$arr) //引用传递
{
print(count($arr));

}
printArray($a);

上面的代码中,我们通过引用把$a数组传入printArray()函数,zend引擎会认为printArray()可能会导致对$a的改变,此时就会自动为$b生产一个$a的数据拷贝,重新申请一块内存进行存储。这就是前面提到的“写时拷贝”概念。

如果我们把上面的代码改成下面这样:

 

function printArray($arr)   //值传递
{
print(count($arr));
}
printArray($a);
上面的代码直接传递$a值到printArray()中,此时并不存在引用传递,所以没有出现写时拷贝。

nginx安装配置flv流媒体服务器

apache也可以配置flv流媒体服务器,之前有文章介绍,但是用一些播放器播放的时候还是不能拖动,比如说使用VLC播放器播放的时候,拖动就不起作用,因此尝试使用nginx来配置,最后发现flowplay,或者VLC都可以完美播放并且可以任意拖动。

看看nginx的安装步奏

1.安装pcre

获取pcre编译安装包,在http://www.pcre.org/上可以获取当前最新的版本
解压缩pcre-xx.tar.gz包。
进入解压缩目录,执行./configure。
make & make install

2.安装openssl
获取openssl编译安装包,在http://www.openssl.org/source/上可以获取当前最新的版本。
解压缩openssl-xx.tar.gz包。
进入解压缩目录,执行./config。
make & make install

3.安装zlib
获取zlib编译安装包,在http://www.zlib.net/上可以获取当前最新的版本。
解压缩openssl-xx.tar.gz包。
进入解压缩目录,执行./configure。
make & make install

4.安装nginx
获取nginx,在http://nginx.org/en/download.html上可以获取当前最新的版本。
解压缩nginx-xx.tar.gz包。
进入解压缩目录,执行

./configure –with-pcre=/usr/harddisk/pcre-8.36 –with-http_flv_module –with-http_gzip_static_module –with-http_stub_status_module

make & make install

注意,可以不需要安装openssl , zlib可能操作系统就已经安装所以也不需要安装,但是pcre必须的,注意–with-pcre是指向源代码的目录而不是安装目录,不然的话编译报错。

5.配置虚拟路径

配置文件路径 vi /usr/local/nginx/conf/nginx.conf

location ^~/vedio/
        {
                alias /usr/harddisk/;
                flv;
                limit_rate 250k;
        }

以上就是配置路径/vedio/下的访问能使用flv模块并且限速,alias是对应的物理路径。

6.启动和停止

/usr/local/nginx/sbin/nginx
/usr/local/nginx/sbin/nginx -s stop

android实现拨打电话

调用系统自带的打电话功能非常简单,在应用中只需要调用对应的接口,并传入电话号码就可以了,界面会自动跳转到打电话界面,并且电话已经开始拨打,等待接通就可以了,下面就是示例的代码:

String number = "110";  
//用intent启动拨打电话  
Intent intent = new Intent(Intent.ACTION_CALL,Uri.parse("tel:"+number));  
startActivity(intent);

当然打电话是调用系统的activity,所以需要分配权限才能使用

AndroidManifest.xml中加入下面的权限即可

<!--添加可以向外拨打电话的权限  -->  
<uses-permission android:name="android.permission.CALL_PHONE"></uses-permission>

这就是拨打电话的全部,超级简单。

android中播放视频的几种方法

播放视频的方法有很多种,使用andorid自带的也可以播放大部分的视频,有些格式的视频需要专门的解码才能播放,大部分的视频网站都有自己的视频格式,同时自己提供播放软件,这就是最简单的防止盗版,同时也是根据需要压缩自己的视频文件可以减少存储空间,当然最重要的还是节省带宽;

在Android中,我们有三种方式来实现视频的播放:
1、使用其自带的播放器。指定Action为ACTION_VIEW,Data为Uri,Type为其MIME类型。
2、使用VideoView来播放。在布局文件中使用VideoView结合MediaController来实现对其控制。
3、使用MediaPlayer类和SurfaceView来实现,这种方式很灵活。

1.调用其自带的播放器:

Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath()+"/Test_Movie.m4v");     
//调用系统自带的播放器    
Intent intent = new Intent(Intent.ACTION_VIEW);    
Log.v("URI:::::::::", uri.toString());    
intent.setDataAndType(uri, "video/mp4");    
startActivity(intent);

2.使用VideoView来实现:

Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath()+"/Test_Movie.m4v");    
VideoView videoView = (VideoView)this.findViewById(R.id.video_view);    
videoView.setMediaController(new MediaController(this));    
videoView.setVideoURI(uri);    
videoView.start();    
videoView.requestFocus();

3.使用MediaPlayer:

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height="fill_parent" android:weightSum="1">
    <SurfaceView android:layout_height="220dip" android:layout_gravity="center" android:id="@+id/surface" android:layout_weight="0.25" android:layout_width="320dip"></SurfaceView>
    <LinearLayout android:id="@+id/linearLayout1" android:layout_height="wrap_content" android:layout_width="fill_parent">
        <Button android:text="播放" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
        <Button android:text="暂停" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
        <Button android:text="停止" android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
    </LinearLayout>
</LinearLayout>

主程序:

public class SurfaceActivity extends Activity implements SurfaceHolder.Callback {
    /** Called when the activity is first created. */
    MediaPlayer player;
    SurfaceView surface;
    SurfaceHolder surfaceHolder;
    Button play,pause,stop;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        play=(Button)findViewById(R.id.button1);
        pause=(Button)findViewById(R.id.button2);
        stop=(Button)findViewById(R.id.button3);
        surface=(SurfaceView)findViewById(R.id.surface);
 
        surfaceHolder=surface.getHolder();  //SurfaceHolder是SurfaceView的控制接口
        surfaceHolder.addCallback(this);   //因为这个类实现了SurfaceHolder.Callback接口,所以回调参数直接this
        surfaceHolder.setFixedSize(320, 220);  //显示的分辨率,不设置为视频默认
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);  //Surface类型
 
        play.setOnClickListener(new OnClickListener(){
             @Override
            public void onClick(View v) {
                player.start();
            }});
        pause.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View v) {
                player.pause();
            }});
        stop.setOnClickListener(new OnClickListener(){
             @Override
            public void onClick(View v) {
                player.stop();
            }});
    }
 
    @Override
    public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
    }
 
    @Override
    public void surfaceCreated(SurfaceHolder arg0) {
//必须在surface创建后才能初始化MediaPlayer,否则不会显示图像
        player=new MediaPlayer();
        player.setAudioStreamType(AudioManager.STREAM_MUSIC);
        player.setDisplay(surfaceHolder);
        //设置显示视频显示在SurfaceView上
            try {
                player.setDataSource("/sdcard/3.mp4");
                player.prepare();
            } catch (Exception e) {
                e.printStackTrace();
            }
    }
 
    @Override
    public void surfaceDestroyed(SurfaceHolder arg0) {
        // TODO Auto-generated method stub
 
    }
 
    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        if(player.isPlaying()){
        player.stop();
        }
        player.release();
        //Activity销毁时停止播放,释放资源。不做这个操作,即使退出还是能听到视频播放的声音
    }
}

总结

第三种算是比较常用的播放方式,比较灵活可以对视频进行控制,当然这毕竟是自带的播放器,支持视频格式根据系统安装的情况而定,那么可以使用一些第三方播放器来实现,比如说vlc开源播放器http://www.videolan.org/,或者其他的开源播放器都可以做的很好。

android使用ACTION_SEND分享内容

几乎是所有的app现在都有分享的功能,分享一些文本,链接和图片之类的到各大社交平台,那么这么一个简单的功能是如何实现的能,android里面有一个默认的接口,我们来看看怎么使用。

android默认的intent ACTION_SEND可以实现这个分享的功能,看看下面的示例

Intent sharingIntent = new Intent(Intent.ACTION_SEND);
sharingIntent.setType("text/html");
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, Html.fromHtml("<p>This is the text that will be shared.</p>"));
startActivity(Intent.createChooser(sharingIntent,"Share using"));

以上是分享一段html,也是最常用的分享方式,也可以分享一些图片如下

Intent sharingIntent = new Intent(Intent.ACTION_SEND);
Uri screenshotUri = Uri.parse(path);

sharingIntent.setType("image/png");
sharingIntent.putExtra(Intent.EXTRA_STREAM, screenshotUri);
startActivity(Intent.createChooser(sharingIntent, "Share image using"));

分享多个图片

ArrayList<Uri> imageUris = new ArrayList<Uri>();
imageUris.add(imageUri1); // Add your image URIs here
imageUris.add(imageUri2);

Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, imageUris);
shareIntent.setType("image/*");
startActivity(Intent.createChooser(shareIntent, "Share images to.."));

上面的分享当然是在手机上有可以分享的软件才能接受到分享的内容,以上的分享事件都会打开一个选择分享软件的列表,比如微信或者微博之类的,用户选择一个之后发送分享内容,那么下面这段xml可以让你的app可以出现在分享列表中,以实现分享的效果:

<activity android:name=".ui.MyActivity" >
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="image/*" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.SEND_MULTIPLE" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="image/*" />
    </intent-filter>
</activity>

下面这段代码是处理接收到消息之后的基本过程

void onCreate (Bundle savedInstanceState) {
    ...
    // Get intent, action and MIME type
    Intent intent = getIntent();
    String action = intent.getAction();
    String type = intent.getType();

    if (Intent.ACTION_SEND.equals(action) && type != null) {
        if ("text/plain".equals(type)) {
            handleSendText(intent); // Handle text being sent
        } else if (type.startsWith("image/")) {
            handleSendImage(intent); // Handle single image being sent
        }
    } else if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) {
        if (type.startsWith("image/")) {
            handleSendMultipleImages(intent); // Handle multiple images being sent
        }
    } else {
        // Handle other intents, such as being started from the home screen
    }
    ...
}

void handleSendText(Intent intent) {
    String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
    if (sharedText != null) {
        // Update UI to reflect text being shared
    }
}

void handleSendImage(Intent intent) {
    Uri imageUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
    if (imageUri != null) {
        // Update UI to reflect image being shared
    }
}

void handleSendMultipleImages(Intent intent) {
    ArrayList<Uri> imageUris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
    if (imageUris != null) {
        // Update UI to reflect multiple images being shared
    }
}

总结

android自带的分享只是列出接收分享内容的app列表,由用户选择启动对应的app进行分享,当系统中没有安装目标app的时候是没办法分享的,那么对一般的应用来说这就已经足够了,系统自带的功能一般都是比较基本的东西,另一个办法是使用第三方分享sdk可以实现更加复杂的分享功能。

c++使用CreateProcess启动程序

有时候需要在程序中启动第三方程序,执行某个命令行之类的事情,也可以做个简单的启动程序启动浏览器之类的传入一些参数,下面来看个简单的例子:

创建一个命令行程序,启动程序的时候可能会遇到一些权限问题,在项目的属性里面可以设置启动的权限, 使用管理员权限

image

下面是个启动浏览器的简单示例

#include "stdafx.h"
#include <windows.h>
#include <string>
#include<stdio.h>

#pragma comment( linker, "/subsystem:windows /entry:mainCRTStartup" )
int main(int argc, _TCHAR* argv[])
{
	TCHAR cmdline[] = L"C:\\Program Files\\Internet Explorer\\iexplore.exe http://www.fullstacks.cn";
	STARTUPINFO si;
	memset(&si, 0, sizeof(STARTUPINFO));  
	si.cb = sizeof(STARTUPINFO);  
	si.dwFlags = STARTF_USESHOWWINDOW;  
	si.wShowWindow = SW_SHOW;  
	PROCESS_INFORMATION pi;
	CreateProcess(NULL,cmdline,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);

	return 0;
}

其中#pragma comment …这一行的意思是启动的时候不显示命令行窗口,双击打开目标程序的时候直接就打开浏览器了;

对于启动其他程序或者启动java程序等可能需要设置一些环境变量或者获取一些环境变量

可以通过方法GetEnvironmentVariableA 获取环境变量,方法SetEnvironmentVariableA设置环境变量

char userdir[256];
memset(userdir,0,buffSize);
//获取环境变量APPDATA的值
GetEnvironmentVariableA("APPDATA", userdir, 256);
//设置环境变量
SetEnvironmentVariableA("GOOGLE_API_KEY"," ");
SetEnvironmentVariableA("GOOGLE_DEFAULT_CLIENT_ID"," ");
SetEnvironmentVariableA("GOOGLE_DEFAULT_CLIENT_SECRET"," ");

将char转换为tchar

//转换为tchar
TCHAR lastcmd[256];
memset(lastcmd,0,256);
MultiByteToWideChar(CP_ACP, 0, userdir, -1, lastcmd, strlen(userdir));

总结

做个启动快捷方式的时候如果使用系统的快捷方式,有时候会被一些软件修改掉,比如说启动浏览器并打开某个指定网站的启动快捷方式,很多应用系统需要这样的快捷方式放在桌面上,以便用户进入系统,但是往往会被一些流氓软件轻轻松松改掉打开一些广告网站,这样就影响很不好了,那么如果使用上面这种方式写一个exe启动你的浏览器然后以你的系统作为默认打开的状态,那么这个体验就非常不错而且不用担心被流氓软件修改你的启动程序。

java导出可执行jar包与启动脚本

java写桌面程序的时候如何启动有很多种办法,有些打包工具就可以直接生成一个快捷方式启动可执行的jar包,也可以直接写一个启动脚本启动,我们来看看启动脚本是怎么写的。

1.导出可执行jar包,首先需要创建一个运行配置

image

2.起个名字并选择需要导出的项目以及启动的时候的入口函数main所在的类

image

3.在项目上右键->导出,选择可执行jar包

image

4.选择上面配置好的运行配置,选择导出的路径

image

这样就导出成功了,依赖的jar包会导到一个文件夹里

5.写个脚本,并制定虚拟机路径

示例脚本如下

@cls
set JRE_HOME=%cd%\jre6
set JAVA_HOME=%cd%\jre6
PATH=%JRE_HOME%\bin;%PATH%

set CLASSPATH = %JRE_HOME%\lib\rt.jar;

@echo %JRE_HOME%
start javaw -jar myapp.jar

把依赖的jar包放到可执行jar包同级的lib目录里面就可以了。

php json_encode()问题

前2天碰到一个问题,折腾了好久才解决,应该是自己道行太浅,这里还是给大家分享一下。

php服务端返回给客户端数据,用json_encode()将array的数据通过json格式返回给页面。

页面通过ajax请求后台,dataType为json,结果在success中不能正确解析json,不过在ie下是ok的,在Firefox和Chromes不行。

后台代码

$returnarray = array(“code”=>100);
echo json_encode($returnarray);
return;

前台代码

$.ajax({
url:’?app=default&act=drop’,
dataType:’json’,
type:’POST’,
data:{cid:id},
success:function(res, textStatus){
if(res.code == 100){
alert(“删除成功”);
} else {
alert(“删除失败”);
}
}

});

在firefox中res返回的结果是

php_code2

最后解决办法是,在服务端加上头部声明

header(“Content-Type:application/json”);

 

 

PHP与MYSQL事务处理

MYSQL的事务处理主要有两种方法。
1、用begin,rollback,commit来实现
begin 开始一个事务
rollback 事务回滚
commit 事务确认
2、直接用set来改变mysql的自动提交模式
MYSQL默认是自动提交的,也就是你提交一个QUERY,它就直接执行!我们可以通过
set autocommit=0 禁止自动提交
set autocommit=1 开启自动提交
来实现事务的处理。
当你用 set autocommit=0 的时候,你以后所有的SQL都将做为事务处理,直到你用commit确认或rollback结束。
注意当你结束这个事务的同时也开启了个新的事务!按第一种方法只将当前的作为一个事务!
个人推荐使用第一种方法!
MYSQL中只有INNODB和BDB类型的数据表才能支持事务处理!其他的类型是不支持的!
***:一般MYSQL数据库默认的引擎是MyISAM,这种引擎不支持事务!如果要让MYSQL支持事务,可以自己手动修改:
方法如下:1.修改c:\appserv\mysql\my.ini文件,找到skip-InnoDB,在前面加上#,后保存文件。
2.在运行中输入:services.msc,重启mysql服务。
3.到phpmyadmin中,mysql->show engines;(或执行mysql->show variables like ‘have_%’; ),查看InnoDB为YES,即表示数据库支持InnoDB了。
也就说明支持事务transaction了。
4.在创建表时,就可以为Storage Engine选择InnoDB引擎了。如果是以前创建的表,可以使用mysql->alter table table_name type=InnoDB;
或 mysql->alter table table_name engine=InnoDB;来改变数据表的引擎以支持事务。
*/
/*************** transaction–1 ***************/
$conn = mysql_connect(‘localhost’,’root’,’root’) or die (“数据连接错误!!!”);
mysql_select_db(‘test’,$conn);
mysql_query(“set names ‘GBK'”); //使用GBK中文编码;
//开始一个事务
mysql_query(“BEGIN”); //或者mysql_query(“START TRANSACTION”);
$sql = “INSERT INTO `user` (`id`, `username`, `sex`) VALUES (NULL, ‘test1’, ‘0’)”;
$sql2 = “INSERT INTO `user` (`did`, `username`, `sex`) VALUES (NULL, ‘test1’, ‘0’)”;//这条我故意写错
$res = mysql_query($sql);
$res1 = mysql_query($sql2);
if($res && $res1){
mysql_query(“COMMIT”);
echo ‘提交成功。’;
}else{
mysql_query(“ROLLBACK”);
echo ‘数据回滚。’;
}
mysql_query(“END”);
/**************** transaction–2 *******************/
/*方法二*/
mysql_query(“SET AUTOCOMMIT=0”); //设置mysql不自动提交,需自行用commit语句提交
$sql = “INSERT INTO `user` (`id`, `username`, `sex`) VALUES (NULL, ‘test1’, ‘0’)”;
$sql2 = “INSERT INTO `user` (`did`, `username`, `sex`) VALUES (NULL, ‘test1’, ‘0’)”;//这条我故意写错
$res = mysql_query($sql);
$res1 = mysql_query($sql2);
if($res && $res1){
mysql_query(“COMMIT”);
echo ‘提交成功。’;
}else{
mysql_query(“ROLLBACK”);
echo ‘数据回滚。’;
}
mysql_query(“END”); //事务处理完时别忘记mysql_query(“SET AUTOCOMMIT=1”);自动提交


/******************对于不支持事务的MyISAM引擎数据库可以使用表锁定的方法:********************/

//MyISAM & InnoDB 都支持,
/*
LOCK TABLES可以锁定用于当前线程的表。如果表被其它线程锁定,则造成堵塞,直到可以获取所有锁定为止。
UNLOCK TABLES可以释放被当前线程保持的任何锁定。当线程发布另一个LOCK TABLES时,或当与服务器的连接被关闭时,所有由当前线程锁定的表被隐含地解锁。
*/

mysql_query(“LOCK TABLES `user` WRITE”);//锁住`user`表
$sql = “INSERT INTO `user` (`id`, `username`, `sex`) VALUES (NULL, ‘test1’, ‘0’)”;
$res = mysql_query($sql);
if($res){
echo ‘提交成功。!’;
}else{
echo ‘失败!’;
}
mysql_query(“UNLOCK TABLES”);//解除锁定

MyISAM 是MySQL中默认的存储引擎,一般来说不是有太多人关心这个东西。决定使用什么样的存储引擎是一个很tricky的事情,但是还是值我们去研究一下,这里的文章只考虑 MyISAM 和InnoDB这两个,因为这两个是最常见的。

下面先让我们回答一些问题:
◆你的数据库有外键吗?
◆你需要事务支持吗?
◆你需要全文索引吗?
◆你经常使用什么样的查询模式?
◆你的数据有多大?

myisam只有索引缓存

innodb不分索引文件数据文件 innodb buffer

myisam只能管理索引,在索引数据大于分配的资源时,会由操作系统来cache;数据文件依赖于操作系统的cache。innodb不管是索引还是数据,都是自己来管理

思考上面这些问题可以让你找到合适的方向,但那并不是绝对的。如果你需要事务处理或是外键,那么InnoDB 可能是比较好的方式。如果你需要全文索引,那么通常来说 MyISAM是好的选择,因为这是系统内建的,然而,我们其实并不会经常地去测试两百万行记录。所以,就算是慢一点,我们可以通过使用Sphinx从 InnoDB中获得全文索引。

数据的大小,是一个影响你选择什么样存储引擎的重要因素,大尺寸的数据集趋向于选择InnoDB方式,因为其支持事务处理和故障恢复。数据库的在小决定了 故障恢复的时间长短,InnoDB可以利用事务日志进行数据恢复,这会比较快。而MyISAM可能会需要几个小时甚至几天来干这些事,InnoDB只需要 几分钟。

您操作数据库表的习惯可能也会是一个对性能影响很大的因素。比如: COUNT() 在 MyISAM 表中会非常快,而在InnoDB 表下可能会很痛苦。而主键查询则在InnoDB下会相当相当的快,但需要小心的是如果我们的主键太长了也会导致性能问题。大批的inserts 语句在 MyISAM下会快一些,但是updates 在InnoDB 下会更快一些——尤其在并发量大的时候。

所以,到底你检使用哪一个呢?根据经验来看,如果是一些小型的应用或项目,那么MyISAM 也许会更适合。当然,在大型的环境下使用 MyISAM 也会有很大成功的时候,但却不总是这样的。如果你正在计划使用一个超大数据量的项目,而且需要事务处理或外键支持,那么你真的应该直接使用 InnoDB方式。但需要记住InnoDB 的表需要更多的内存和存储,转换100GB 的MyISAM 表到InnoDB 表可能会让你有非常坏的体验。

===========================================================

MyISAM:这个是默认类型,它是基于传统的ISAM类型,ISAM是 Indexed Sequential Access Method (有索引的顺序访问方法) 的缩写,它是存储记录和文件的标准方法.与其他存储引擎比较,MyISAM具有检查和修复表格的大多数工具. MyISAM表格可以被压缩,而且它们支持全文搜索.它们不是事务安全的,而且也不支持外键。如果事物回滚将造成不完全回滚,不具有原子性。如果执行大量 的SELECT,MyISAM是更好的选择。

InnoDB:这种类型是事务安全的.它与BDB类型具有相同的特性,它们还支持外键.InnoDB表格速度很快.具有比BDB还丰富的特性,因此如果需 要一个事务安全的存储引擎,建议使用它.如果你的数据执行大量的INSERT或UPDATE,出于性能方面的考虑,应该使用InnoDB表,

对于支持事物的InnoDB类型的标,影响速度的主要原因是AUTOCOMMIT默认设置是打开的,而且程序没有显式调用BEGIN 开始事务,导致每插入一条都自动Commit,严重影响了速度。可以在执行sql前调用begin,多条sql形成一个事物(即使autocommit打 开也可以),将大大提高性能。

===============================================================

InnoDB和MyISAM是在使用MySQL最常用的两个表类型,各有优缺点,视具体应用而定。下面是已知的两者之间的差别,仅供参考。

innodb
InnoDB 给 MySQL 提供了具有事务(commit)、回滚(rollback)和崩溃修复能力 (crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表。 InnoDB 提供了行锁(locking on row level),提供与 Oracle 类型一致的不加锁读取(non- locking read in SELECTs)。这些特性均提高了多用户并发操作的性能表现。在InnoDB表中不需要扩大锁定 (lock escalation),因为 InnoDB 的列锁定(row level locks)适宜非常小的空间。 InnoDB 是 MySQL 上第一个提供外键约束(FOREIGN KEY constraints)的表引擎。

InnoDB 的设计目标是处理大容量数据库系统,它的 CPU 利用率是其它基于磁盘的关系数据库引擎所不能比的。在技术上,InnoDB 是一套放在 MySQL 后台的完整数据库系统,InnoDB 在主内存中建立其专用的缓冲池用于高速缓冲数据和索引。 InnoDB 把数据和索引存放在表空间里,可能包含多个文件,这与其它的不一样,举例来说,在 MyISAM 中,表被存放在单独的文件中。InnoDB 表的大小只受限于操作系统的文件大小,一般为 2 GB。
InnoDB所有的表都保存在同一个数据文件 ibdata1 中(也可能是多个文件,或者是独立的表空间文件),相对来说比较不好备份,免费的方案可以是拷贝数据文件、备份 binlog,或者用 mysqldump。

MyISAM
MyISAM 是MySQL缺省存贮引擎 .

每张MyISAM 表被存放在三个文件 。frm 文件存放表格定义。 数据文件是MYD (MYData) 。 索引文件是 MYI (MYIndex) 引伸。

因为MyISAM相对简单所以在效率上要优于InnoDB..小型应用使用MyISAM是不错的选择.

MyISAM表是保存成文件的形式,在跨平台的数据转移中使用MyISAM存储会省去不少的麻烦

以下是一些细节和具体实现的差别:

1.InnoDB不支持FULLTEXT类型的索引。
2.InnoDB 中不保存表的具体行数,也就是说,执行select count(*) from table时,InnoDB要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count(*)语句包含 where条件时,两种表的操作是一样的。
3.对于AUTO_INCREMENT类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他字段一起建立联合索引。
4.DELETE FROM table时,InnoDB不会重新建立表,而是一行一行的删除。
5.LOAD TABLE FROM MASTER操作对InnoDB是不起作用的,解决方法是首先把InnoDB表改成MyISAM表,导入数据后再改成InnoDB表,但是对于使用的额外的InnoDB特性(例如外键)的表不适用。

另外,InnoDB表的行锁也不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表,例如 update table set num=1 where name like “%aaa%”

任何一种表都不是万能的,只用恰当的针对业务类型来选择合适的表类型,才能最大的发挥MySQL的性能优势。

===============================================================

以下是InnoDB和MyISAM的一些联系和区别!

1. 4.0以上mysqld都支持事务,包括非max版本。3.23的需要max版本mysqld才能支持事务。

2. 创建表时如果不指定type则默认为myisam,不支持事务。
可以用 show create table tablename 命令看表的类型。

2.1 对不支持事务的表做start/commit操作没有任何效果,在执行commit前已经提交,测试:
执行一个msyql:
use test;
drop table if exists tn;
create table tn (a varchar(10)) type=myisam;
drop table if exists ty;
create table ty (a varchar(10)) type=innodb;

begin;
insert into tn values(‘a’);
insert into ty values(‘a’);
select * from tn;
select * from ty;
都能看到一条记录

执行另一个mysql:
use test;
select * from tn;
select * from ty;
只有tn能看到一条记录
然后在另一边
commit;
才都能看到记录。

3. 可以执行以下命令来切换非事务表到事务(数据不会丢失),innodb表比myisam表更安全:
alter table tablename type=innodb;

3.1 innodb表不能用repair table命令和myisamchk -r table_name
但可以用check table,以及mysqlcheck [OPTIONS] database [tables]

==============================================================

mysql中使用select for update的必须针对InnoDb,并且是在一个事务中,才能起作用。

select的条件不一样,采用的是行级锁还是表级锁也不一样。
http://www.neo.com.tw/archives/900 的说明

由于InnoDB 预设是Row-Level Lock,所以只有「明确」的指定主键,MySQL 才会执行Row lock (只锁住被选取的资料例) ,否则MySQL 将会执行Table Lock (将整个资料表单给锁住)。

举个例子:

假设有个表单products ,里面有id 跟name 二个栏位,id 是主键。

例1: (明确指定主键,并且有此笔资料,row lock)

SELECT * FROM products WHERE id=’3′ FOR UPDATE;

例2: (明确指定主键,若查无此笔资料,无lock)

SELECT * FROM products WHERE id=’-1′ FOR UPDATE;

例2: (无主键,table lock)

SELECT * FROM products WHERE name=’Mouse’ FOR UPDATE;

例3: (主键不明确,table lock)

SELECT * FROM products WHERE id<>’3′ FOR UPDATE;

例4: (主键不明确,table lock)

SELECT * FROM products WHERE id LIKE ’3′ FOR UPDATE;

注1:
FOR UPDATE 仅适用于InnoDB,且必须在交易区块(BEGIN/COMMIT)中才能生效

《三体1》9.宇宙闪烁

汪淼驱车沿京密路到密云县,再转至黑龙潭,又走了一段盘山路,便到达中科院国家天文观测中心的射电天文观测基地。他看到二十八面直径为九米的抛物面天线在暮色中一字排开,像一排壮观的钢铁植物,2006年建成的两台高大的五十米口径射电望远镜天线矗立在这排九米天线的尽头,车驶近后,它们令汪淼不由想起了那张杨冬母女合影的背景。

但叶文洁的学生从事的项目与这些射电望远镜没有什么关系,沙瑞山博士的实验室主要接收三颗卫星的观测数据:1989年11月升空、即将淘汰的微波背景探测卫星COBE。2003年发射的威尔金森微波各向异性探测卫星WMAP和2007年欧洲航天局发射的普朗克高精度宇宙微波背景探测卫星Planek。

宇宙整体的微波背景辐射频谱非常精确地符合温度为2.726K黑体辐射谱,具有高度各向同性,但在不同局部也存在大约百万分之五涨落的幅度。沙瑞山的工作就是根据卫星观测数据,重新绘制一幅更精确的全宇宙微波辐射背景图。这个实验室不大,主机房中挤满了卫星数据接收设备,有三台终端分别显示来自三颗卫星的数据。

沙瑞山见到汪淼,立刻表现出了那种长期在寂寞之地工作的人见到来客的热情,问他想了解哪方面的观测数据。

“我想观测宇宙背景辐射的整体波动。”

“您能……说具体些吗?”沙瑞山看汪淼的眼神变得奇怪起来。

“就是,宇宙3K微波背景辐射整体上的各向同性的波动,振幅在百分之一至百分之五之间。”

沙瑞山笑笑,早在本世纪初,密云射电天文基地就对游客开放参观,为挣些外快,沙瑞山时常做些导游或讲座的事,这种笑容就是他回答游客(他已适应了他们那骇人的科盲)问题时常常露出的。“汪先生,您……不是搞这个专业的吧?”

“我搞纳米材料。”

“哦,那就对了。不过,对于宇宙3K背景辐射,您大概有个了解吧?”

“知道的不多。目前的宇宙起源理论认为,宇宙诞生于距今约一百四十亿年前的一次大爆炸。在诞生早期,宇宙温度极高,随后开始冷却,形成被称为微波背景辐射的‘T余烬’。这种弥漫全宇宙的残留背景辐射,在厘米波段上是可以观测到的。好像是在一九六几年吧,两个美国人在调试一个高精度卫星接收天线时意外地发现了宇宙背景辐射……”

“足够了,”沙瑞山挥手打断了汪淼的话,“那你就应该知道,与我们观测的不同部分的微小不均匀不同,宇宙整体辐射背景波动是随着宇宙的膨胀,在宇宙时间尺度上缓慢变化的,以Planck卫星的精度,直到一百万年后都未必能测出这种变化你却想在今天晚上发现它百分之五的波动?!知道这意味着什么吗?这意味着整个宇宙像一个坏了的日光灯管那样闪烁!”

而且是为我闪烁,汪淼心里说。

“叶老师这是在开什么玩笑。”沙瑞山摇摇头说。

“但愿真是个玩笑。”汪淼说,本想告诉他,叶文洁并不知道详情,但又怕因此招致他的拒绝,不过这倒是他的心里话。

“既然是叶老师交待的,就观测吧,反正也不费劲,百分之一的精度。用老古董COBE就行了。”沙瑞山说着,在终端上忙活起来,很快屏幕上出现一条平直的绿线,“你看,这就是当前宇宙整体背景辐射的实时数值曲线,哦,应该叫直线才对,数值是2.726±0.010K,那个误差是银河系运动产生的多普勒效应,已经滤掉了。如果发生你所说的超过百分之一振幅的波动,这条线就会变红并将波动显示出来。我敢打赌直到世界末日它也是条绿直线,要看到它显现肉眼看得到的变化,可能比看太阳毁灭还要等更长的时间。”

“这不会影响您的正常工作吧?”

“当然不会,那么粗的精度,用COBE观察数据的边角料就足够了。好了,从现在开始,如果那伟大的波动出现,数值会自动存盘。”

“可能要等到凌晨一点。”

“哇,这么精确?没关系,反正我本来就是值夜班。您吃饭了吗?那好,我带您去参观一下吧。”

这一夜没有月亮,他们沿着长长的天线阵列漫步。沙瑞山指着天线说:“壮观吧?可惜都是聋子的耳朵。”

“为什么?”

“自它们建成以来,在观测频段上就干扰不断,先是上世纪八十年代末的寻呼台,到现在是疯狂发展的移动通信。这些米波综合孔径射电望远镜能做的那些项目,像米波巡天、射电变源、超新星遗迹研究等等,大部分都不能正常开展。多次找过无委会(国家无线电管理委员会),没有用,我们能玩得过中国移动、联通、网通?没有钱,宇宙奥秘算个球!好在我的项目靠卫星数据,与这些‘旅游景观’无关了。”

“近年来很多基础研究的商业运行还是很成功的,比如高能物理。把观测基地建到离城市远些的地方应该好些吧?”

“那还是钱的问题。就目前而言,只能是在技术上屏蔽干扰。唉,叶老师要在就好了,她在这方面造诣很深。”

于是话题转到叶文洁身上。从她的学生那里,汪淼得知了她那历经风霜的一生:他听沙瑞山讲她如何目睹父亲在“文革”中的惨死,讲她后来在建设兵团被诬陷,后来杳无音讯;九十年代初才又回到了这座城市,在父亲曾工作过的大学中讲授天体物理学直到退休。

“最近才知道,她那二十多年,是在红岸基地度过的。”

“红岸?!”汪淼吃惊地停住了脚步,“难道那些传说……”

“大部分是真的。红岸自译解系统的一名研制者移民到欧洲,去年写了一本书,你所说的传说大多来自于那本书,据我了解是真的。红岸工程的参与者大都还健在。”

“这可真是……传奇啊!”

“尤其是发生在那个年代,更是传奇中的传奇。”

……

他们又谈了一会儿,沙瑞山问起进行这次奇怪观测的目的,汪淼避而不答,他也就没有再问。显然,一个专家的尊严,不允许他对这种违反专业常识的观测表现出过多的兴趣。

然后他们到一间为游客开的通宵酒吧中去坐了两个多小时,沙瑞山一杯接着一杯地灌啤酒,变得更加健谈,而汪淼却早已心神不定,脑子里不断地浮现出那条绿色直线。直到差十分钟凌晨一点时,沙瑞山才接受了汪淼的多次提议,起身返回实验室。

这时,照向射电天线阵列的聚光灯已经熄灭,天线在夜空下变成了简明的黑色二维图案,仿佛是一排抽象的符号,以同一个仰角齐齐地仰望着宇宙,似乎在等待着什么。这景象令汪淼不寒而栗,他想起了《三体》中的那些巨摆。

回到实验室时正好是凌晨一点,当他们将目光投向终端屏幕时,波动刚刚出现,直线变成了曲线,出现了间隔不一的尖尖的波峰,颜色也变红了,如同一条冬眠后的蛇开始充血蠕动了。

“肯定是COBE卫星的故障!”沙瑞山惊恐地盯着曲线讲。

“不是故障。”汪淼平静地说,在这样的事情面前,他已经初步学会了控制自己。

“我们马上就能知道!”沙瑞山说着,在另外两台终端上快速操作起来。很快,他调出了另外两颗卫星WMAP和Planck的宇宙背景辐射实时数据,并将其变化显示为曲线——

三条曲线在同步波动,一模一样。

沙瑞山又搬出一台笔记本电脑,手忙脚乱地启动系统,插上宽带网线,然后打电话——汪淼听出他在联系乌鲁木齐射电观测基地——然后等待着。他没有对汪淼解释什么,两眼死盯着屏幕上的浏览器,汪淼能听到他急促的呼吸声:几分钟后,浏览器上出现了一个坐标窗口,一条红色曲线在窗口上出现,与另外三条进行着精确同步的波动。

这样,三颗卫星和一套地面观测设备同时证实了一件事:宇宙在闪烁!

“能将前面的曲线打印出来吗?”汪淼问。

沙瑞山抹了一把头上的冷汗,点点头,移动鼠标启动了打印程序。汪淼迫不及待地抓过激光打印机吐出的第一张纸,用一枝铅笔划过曲线,将波峰问的距离与他刚拿出来的那张莫尔斯电码表对照起来。

短长长长长、短长长长长、短短短短短、长长长短短、长长短短长长、短短长长长、短短短短长、长长短短长长、短短短长长、长长短短短,这是1108:21:37。

短长长长长、短长长长长、短短短短短、长长长短短、长长短短长长、短短长长长、短短短短长、长长短短长长、短短短长长、长短短短短,这是1108:21:36。

短长长长长、短长长长长、短短短短短、长长长短短、长长短短长长、短短长长长、短短短短长、长长短短长长、短短短长长、短短短短短,这是1108:21:35。

……

倒计时在宇宙尺度上继续,已经过去了78小时,还剩1108小时?

沙瑞山焦躁地来回踱步,不时在汪淼身后停下来看看他正在写出的那一串数字。“你真的不能把实情告诉我吗?!”他耐不住大声问。

“沙博士,相信我,一时说不清的。”汪淼推开那一堆印着波动曲线的纸,盯着那行倒计时数字,“也许,三颗卫星和一个地面观测点都出现了故障。”

“你知道这不可能!”

“如果有人故意破坏呢?”

“也不可能!同时改变三颗卫星和一个地面观测站的数据?那这破坏也有些超自然了。”

汪淼点点头,比起宇宙闪烁来,他宁愿接受这个超自然。但沙瑞山立刻抽走了他怀中这唯一的一根救命稻草。

“要想最终证实这一切,其实很简单。宇宙背景辐射这样幅度的波动,已经大到我们能用肉眼觉察的程度。”

“你胡说什么?现在是你在违反常识了:背景辐射的波长是7cm,比可见光大了七八个数量级,怎么能看到?”

“用3K眼镜。”

“3K眼镜?”

“是我们为首都天文馆做的一个科普小玩意儿。现在的技术,已经能将彭齐阿斯和威尔逊在四十多年前用于发现3K背景辐射的二十英尺的喇叭形天线做成眼镜大小,并且在这个眼镜中设置一个转换系统,将接收到的背景辐射的波长压缩七个数量级,将7cm波转换成红光。这样,观众在夜里戴上这种眼镜,就能亲眼看到宇宙的3K背景辐射,现在,也能看到宇宙闪烁。”

“这东西现在在哪”

“在天文馆,有二十副呢。”

“我必须在五点以前拿到它。”

沙瑞山拿起电话拨了个号码,对方很长时间才接起电话,沙瑞山费尽口舌才说服那个被半夜叫醒的人一小时后在天文馆等汪淼。

临别时沙瑞山说:“我就不同您去了,刚才看到的已经足够,我不需要这样的证明。我还是希望您能在适当的时候把实情告诉我,如果这种现象引出什么研究成果的话,我不会忘记您的。”

“闪烁在凌晨五点就会停止,以后别去深究它吧,相信我,不会有什么成果的。”汪淼扶着车门说。

沙瑞山对着汪淼注视良久,点点头:“明白了,现在科学界出了一些事……”

“是的。”汪淼说着,钻进车里,他不想把这个话题继续下去了。

“轮到我们了吗?”

“至少轮到我了。”汪淼说着发动了车子。

汪淼一小时后到达市内,他在新天文馆前下了车。城市午夜的灯光透过这栋巨大玻璃建筑的透明幕墙,将内部的结构隐隐约约显现出来。汪淼现在体会到,如果新天文馆的建筑师想表达对字宙的感觉,那他成功了——越透明的东西越神秘,字宙本身就是透明的,只要目力能及,你想看多远就看多远,但越看越神秘。

那名睡眼惺忪的天文馆1二作人员已经在门口等汪淼了,他把一个手提箱递给汪淼,“这里面有五副3K眼镜,都是充好电的。左边的按钮是开关,右边是光度调节,下面还有十几副,你想怎么看就怎么看吧,我先去睡会儿,就在靠门口那个房间:这个沙博士,真是个神经病。”说完转身走进昏暗的馆内。

汪淼将箱子放到车座上打开,拿出一副3K眼镜,这东西很像他刚用过的v装具中的头盔显示器。他拿起一副走到车外戴上,透过镜片看到的城市夜景没有变化,只是暗了些,这时他才想起要将开关打开,立刻,城市化作一团团朦胧的光晕,大部分亮度固定,还有一些闪烁或移动着。他知道,这都是被转化为可见光的厘米微波,每团光晕的中心就是一个发射源,由于波长的原因,不可能看清形状。

他抬起头,看到了一个发着暗红色微光的天空,就这样,他看到了宇宙背景辐射,这红光来自于一百多亿年前,是大爆炸的延续,是创世纪的余温。看不到星星,本来,由于可见光波段已被推至不可见,星星应该是一个个黑点,但厘米波的衍射淹没了一切形状和细节。

当汪淼的眼睛适应了这一切后,他看到了天空的红光背景在微微闪动,整个太空成一个整体在同步闪烁,仿佛整个宇宙只是一盏风中的孤灯。

站在这闪烁的苍穹下,汗淼突然感到宇宙是这么小,小得仅将他一人禁锢于其中。宇宙是一个狭小的心脏或子宫,这弥漫的红光是充满于其中的半透明的血夜,他悬浮于血液中,红光的闪烁周期是不规则的,像是这心脏或子宫不规则地脉动,他从中感受到了一个以人类的智慧永远无法理解的怪异、变态的巨大存在。

汪淼摘下3K眼镜,虚弱地靠着车轮坐在地上。在他的眼中,午夜的城市重新恢复了可见光波段所描绘的现实图景,但他的目光游移,在捕捉另外一些东西:对面动物园大门旁的一排霓虹灯中有一根灯管坏了,不规则地闪烁着;近处的一棵小树上的树叶在夜风中摇动,反射着街灯的光,不规则地闪烁着;远处北京展览馆俄式尖顶上的五角星也在反射着下面不同街道上车灯的光,不规则地闪烁着……汪森按莫尔斯电码努力破译着这些闪烁。他甚至觉得,旁边几幅彩旗在微风中飘出的皱褶、路旁一洼积水表面的涟漪,都向他传递着莫尔斯电码……他努力地破译着,感受着幽灵倒计时的流逝。

不知过了多久,那个天文馆的工作人员出来了,问汪淼看完了没有。当看到他时,他的样子使那人双眼中的睡意一下子消失了。收拾好了3K眼镜的箱子,那人又盯着汪淼看了几秒钟,提着箱子快步走了回去。

汪森拿出手机,拨通了申玉菲的电话,她很快就接了,也许她也度过一个不眠之夜。

“倒计时的尽头是什么?”汪淼无力地问。

“不知道。”说了这简短的三个字后,电话挂断了。

是什么?也许是自己的死亡,像杨冬那样;也许是一场像前几年印度洋海啸那样的大灾难,谁也不会将其与自己的纳米研究项目相联系(由此联想到,以前的每一次大灾难,包括两次世界大战,是否都是一次次幽灵倒计时的尽头?都有一个谁都想不到的像自己这样的人要负的最终责任);也许是全世界的彻底毁灭,在这个变态的宇宙中,那倒对谁都是一种解脱……有一点可以肯定,不管幽灵倒计时的尽头是什么,在这剩下的千余个小时中,对尽头的猜测将像恶魔那样残酷地折磨他,最后在精神上彻底摧毁他。

汪淼钻进车子,离开了天文馆,在城市里漫无目的地开着。黎明前,路上很空,但他不敢开快,仿佛车开得快,倒计时走得也快。当东方出现一线晨光时,他将车停在路边,下车走了起来,同样漫无目标的。他的意识中一片空白,只有倒计时在那暗红的背景辐射上显现着,跳动着,他自己仿佛变成了一个单纯的计时器,一口不知道为谁而呜的丧钟。天亮了起来,他走累了,在一条长椅上坐下来。当他抬头看看自己下意识走到的目的地时,不由打了个寒颤。

他正坐在王府井天主教堂前。在黎明惨白的天空下,教堂的罗马式尖顶像三根黑色的巨指,似乎在为他指出冥冥太空中的什么东西。

汪淼起身要走,一阵从教堂传出的圣乐留住了他。今天不是礼拜日,这可能是唱诗班为复活节进行的排练,唱的是这个节日弥撒中常唱的《圣灵光照》。在圣乐的庄严深远中,汪淼再次感到宇宙变小了,变成了一座空旷的教堂,穹顶隐没于背景辐射闪烁的红光中,他则是这宏伟教堂地板砖缝中的一只小蚂蚁。他感觉到自己那颗颤抖的心灵被一只无形的巨手抚摸着,一时间又回到了脆弱无助的孩童时代,意识深处硬撑着的某种东西像蜡一样变软了,崩溃了。他双手捂着脸哭了起来。

“哈哈哈,又放倒了一个!”汪淼的哭泣被身后的一阵笑声打断,他扭头一看,大史站在那里,嘴里吐出一口白烟。

maven依赖war包创建项目

对于bs结构的项目,一般公司都是会有一个适合自己的通用框架,项目在此基础上添加业务功能,那么基础框架可以发布成一个独立的war包,其他项目都依赖与这个包来创建,当版本更新的时候就可以很方便的升级基础框架的东西。

下面来看一下创建的过程

1.创建maven项目

image

2.选择简单项目

image

3.输入项目的信息,和依赖的war包的信息

image

这样项目就创建完成了

打开创建好之后的项目里的文件pom.xml

可以看到依赖项如下

  <parent>
    <groupId>fullstacks</groupId>
    <artifactId>fullstacks.parent</artifactId>
    <version>1.0.0</version>
  </parent>

这样如果war包没有问题的话,就可以运行起来了,当然先配好tomcat

4.编译

image

第一次编译的时候会下载war包的内容,并生成目标文件

在项目的根目录下生成一个目录叫target,里面有下载的war包以及和新项目加入的文件,如果新项目里面有相同路径的文件则会覆盖掉依赖的内容,这样就形成了新的项目。如果想覆盖掉基础框架的文件比如js,html和资源文件之类的,只需要在相同的路径建立相同的文件就可以了。

c++创建atl activex的简单步骤

创建activex可以基于mcf框架也可以基于atl创建,如果是创建没有界面的activex控件,atl更加小巧,当然atl也可以支持界面的操作,但是比较简陋可以安装wtl进行扩展。下面来看看一个简单的atl创建控件的简单示例

1.创建项目,勾选下面的选项,其他默认

image

2.打开类视图,添加一个类

image

3.添加一个简单对象,给个类名就可以了

image

4.勾选下面的选项,其他默认

image

5.在类图里面找到接口,在接口上添加方法

image

6.添加输入参数输出参数,注意输出参数必须是指针类型,要不然选不了out类型

image

7.找源文件Add.cpp,看到新添加的方法实现,写上方法的实现逻辑

STDMETHODIMP CAdd::add(LONG a, LONG b, LONG* c)
{

	*c = a + b;

	return S_OK;
}

这样整个过程就完成了,编译项目这个空间就会自动注册了,也可以通过命令行注册

regsvr32 demo.dll

8.注册之后的classid可以从扩展名为idl的文件中找到,如下

image

9.在页面上可以如下使用

<object classid="clsid:8FA96F06-568C-40E3-8F51-69E77D984D9E" id="myAddObj" name="myAddObj"/>
<script>
	var result = myAddObj.add(1,2);
	alert(result);
</script>

总结

atl创建的控件最后打包的是dll文件,而mfc貌似是扩展名为ocx的文件,其实本质都是dll,通过注册可以在页面上根据classid来引用,那么编译完成之后的dll可以打包成cab文件在页面上使用,前面文章有介绍如何打包并签名cab文件。

c++对图片进行base64编码

有时候我们需要把图片编码成base64编码的文本,然后保存起来,这时候就需要把图片的二进制数据读到内存,然后按照base64的算法进行编码得到一大串文本,这样就方便存放或者在网络上传输,当然也可以在网页上显示。

下面是一个完整的c++实现的base64编码解码程序代码

base64.h

#include "stdafx.h"
#include <string>

std::string base64_encode(unsigned char const* , unsigned int len);
std::string base64_decode(std::string const& s);

base64.cpp

#include "base64.h"
#include <iostream>
using namespace std;

static const std::string base64_chars = 
             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
             "abcdefghijklmnopqrstuvwxyz"
             "0123456789+/";

static inline bool is_base64(unsigned char c) {
  return (isalnum(c) || (c == '+') || (c == '/'));
}

/**
* 对二进制进行编码
*/
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {

  std::string ret;
  int i = 0;
  int j = 0;
  unsigned char char_array_3[3];
  unsigned char char_array_4[4];

  while (in_len--) {
    char_array_3[i++] = *(bytes_to_encode++);
    if (i == 3) {
      char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
      char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
      char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
      char_array_4[3] = char_array_3[2] & 0x3f;

      for(i = 0; (i <4) ; i++)
        ret += base64_chars[char_array_4[i]];
      i = 0;
    }
  }

  if (i)
  {
    for(j = i; j < 3; j++)
      char_array_3[j] = '\0';

    char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
    char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
    char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
    char_array_4[3] = char_array_3[2] & 0x3f;

    for (j = 0; (j < i + 1); j++)
      ret += base64_chars[char_array_4[j]];

    while((i++ < 3))
      ret += '=';

  }

  return ret;

}

/**
* 对base64串进行解码
*/
std::string base64_decode(std::string const& encoded_string) {
  int in_len = encoded_string.size();
  int i = 0;
  int j = 0;
  int in_ = 0;
  unsigned char char_array_4[4], char_array_3[3];
  std::string ret;

  while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
    char_array_4[i++] = encoded_string[in_]; in_++;
    if (i ==4) {
      for (i = 0; i <4; i++)
        char_array_4[i] = base64_chars.find(char_array_4[i]);

      char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
      char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
      char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

      for (i = 0; (i < 3); i++)
        ret += char_array_3[i];
      i = 0;
    }
  }

  if (i) {
    for (j = i; j <4; j++)
      char_array_4[j] = 0;

    for (j = 0; j <4; j++)
      char_array_4[j] = base64_chars.find(char_array_4[j]);

    char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
    char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
    char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

    for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
  }

  return ret;
}

base64编码后会得到类似这样的一串文本:“/9j/4AAQSkZJRgABAQAAAQABA…”

在网页上可以直接显示base64编码的图片,需要在前面加上前缀:“data:image/jpg;base64,”

下面是显示图片的例子

<img id="jpg" src="data:image/jpg;base64,/9j/4AAQSkZJRgABAQAAAQABA..."/>

总结

图片不一定非要保存成文件存放在服务器上,也不一定非得保持二进制格式存放,也可以变成base64编码的字符串存放在数据库里面,比如说读取的身份证头像数据完全可以转换成base64编码的字符串存在数据库里,显示的时候直接能够显示不管是在网页上或者在文档里面都是可以的。

windows下cab打包与签名

cab的打包有各式各样的工具,windows下一直有个默认的工具iexpress可以做到这一点,下面简单看一下一个简单的示例如何打包一个cab并在网页上引用

1、制作inf文件

default.INF
最开始一般是[Version]区:

[Version]
signature=” $CHICAGO$ ”
AdvancedINF=2.0
接下来就是最重要的[Add.Code]区:

[Add.Code]
getcertocx.ocx=getcertocx.ocx
前面是要下载的文件名,后面是对应这个文件的区域名,可以是任何名字,不过一般都是和文件的名字相同,这样方便维护。
再接下来是各个文件的区域了
[getcertocx.ocx]
file-win32-x86=thiscab
RegisterServer=yes
clsid={0A0488CF-F9AB-4AC4-AD8F-CD893553DD22}
DestDir=11
FileVersion=1,0,0,1

[getcertocx.ocx]区域中的第一个file值告诉ie到哪里去得到这个ocx,file一共包括三个部分,第一部分是file,这个永远都是这样的;第二部分告诉声明支持的OS,win32表示windows,mac就是苹果MAC OX了;第三部分是CPU类型,比如说x86、mips等

file的值可以取三个:一个URL、ignore和thiscab,如果是URL则说明到URL所在的位置去下;如果是ignore说明对于这种OS和CPU,不需要下载这个文件;如果是thiscab就在当前的cab文件中了。

第二部分是RegisterServer,可以取两个值yes和no,如果为yes则说明ie要注册该ocx,如果是no就不必了;

接下来是clsid,需要填写该ocx的class guid。

再下来是DestDir,它的值是ocx将要存到本地硬盘的位置,如果它的值是10,则将ocx放到\Windows或者\WinNT下;如果是11,则放到\Windows\System或者

\WinNT\System32下;如果是空(就是没有值)则会放到\Windows或者\WinNT下的Downloaded Program Files目录下;

最后是FileVersion,说明了ocx的版本号。

完整的示例default.INF文件如下

[Version] 
signature="$CHICAGO$" 
AdvancedINF=2.0 

[Add.Code] 
getcertocx.ocx=getcertocx.ocx 

[getcertocx.ocx] 
file-win32-x86=thiscab 
RegisterServer=yes 
clsid={0A0488CF-F9AB-4AC4-AD8F-CD893553DD22} 
DestDir=11 
FileVersion=1,0,0,1 

2、制作cab文件

利用iexpress.exe(windows提供的一个向导式cab制作工具) 在路径C:\Windows\System32\iexpress.exe

1

选“创建新的自解压缩指定文件”,点“下一步”。

2

选“仅创建压缩文件(ActiveX安装)”,点“下一步”。

3

点“添加”将上边制作好的default.inf和getcertocx.ocx文件添加进来,点“下一步”。

4

点“浏览”输入要生成的CAB文件名称,并选中“在软件包中使用长文件名保存文件”,之后点“下一步”。

5

选“不保存”,点“下一步”。

6

点“下一步”。

8

点“完成”退出向导,cab文件生成。

9

3.web上引用

<object width="0" HEIGHT="0"
 classid="CLSID:0A0488CF-F9AB-4AC4-AD8F-CD893553DD22"
 codebase="getcert.cab#Version=1,0,0,1">
</object>

注意classid和版本号要与inf文件中的一样,而且版本号是dll或者ocx文件本身的真实版本,这样的话后续版本修改之后网页可自动更新。

4.对cab文件进行签名

一般来说如果做一个自签名的证书其实意义不是很大,只有正版的证书才有意义

这里是制作一个自签名的证书并对cab文件进行签名,使用的是Visual Studio2010的命令行工具

image

执行下面四行命令可对文件进行签名

makecert -r -sv fullstacks_key.pvk -n "CN=fullstacks" fullstacks.cer
cert2spc fullstacks.cer fullstacks.spc
pvk2pfx -pvk fullstacks_key.pvk -pi fullstacks -spc fullstacks.spc -pfx fullstacks.pfx -po fullstacks
signtool  sign  /f fullstacks.pfx /p fullstacks  *.cab

5.在cab安装的时候执行exe

下面是一个示例每次都执行的

[Setup Hooks]
hook1=hook1

[hook1]
run=%EXTRACT_DIR%\example_setup.exe /q

[Version]
; This section is required for compatibility on both Windows 95 and Windows NT.
Signature="$CHICAGO$"
AdvancedInf=2.0

下面是一个有条件执行,当机器里没有注册指定的dll的时候运行,当然也可以加上文件版本,如果版本更新也可以执行

[Add.Code]
example.ocx=example.ocx

[example.ocx]
Clsid={...}
hook=hook1
FileVersion=1,0,0,1

[hook1]
run=%EXTRACT_DIR%\example_setup.exe /q

[Version]
; This section is required for compatibility on both Windows 95 and Windows NT.
Signature="$CHICAGO$"
AdvancedInf=2.0

总结

cab在网页使用的场景比较多,最大的好处就是能够自动下载安装,如果浏览器里面当前网页在新人区里的话可以自动下载安装没有任何提示,这个在用户体验上也比较好,可以默默无闻的进行更新,这种情况比较适合计算机水平比较低的最终用户,不需要操作就可以使用最新的功能,但是对一些高级用户就不一合适。不过这些只是比较合适IE的用户,其他浏览器或多或少的总是有一些不一样的地方。

windows下编译使用libjpeg

libjpeg是一个图片处理c语言实现,可以比较方便的将几种图片格式互相转换,是个开放源代码软件,需要下载源代码编译之后才能使用,下载地址http://www.infai.org/jpeg/,windows下下载jpegsr9a.zip文件,使用Visual Studio 2010编译工具进行编译。

1.使用Visual Studio2010编译

首先将文件解压到某个目录,将文件jconfig.vc改为jconfig.h

然后打开Visual Studio2010的命令行工具

image

在解压目录下输入编译命令

nmake /f makefile.vc nodebug=1

这样就编译完成了,目录下会生成libjpeg.lib文件,还有其他一些工具如cjpeg.exe

可以使用cjpeg.exe通过命令行直接转换文件如

cjpeg.exe a.bmp b.jpg

可以直接将a.bmp文件转换为b.jpg文件。

2.使用libjpeg.lib

将这四个文件libjpeg.lib,jconfig.h,jmorecfg.h,jpeglib.h复制得到自己的项目中

在项目属性里面引入libjpeg.ib

image

在项目中需要用的的地方引入头文件

extern "C" {
	#include "jpeglib.h"
	#include "jmorecfg.h"
	#include "jconfig.h"
}

这样就可以调用库函数了

3.c与c++混合使用

Visual Studio2010里面混合使用c与c++需要配置不要使用预编译头文件

image

4.bmp转换为jpg示例

libjpeg.lib里面并没与读取bmp的辅助方法,但是cjpeg.exe 工具有,使用到以下几个文件

rdbmp.c,cdjpeg.c,cdjpeg.h,jinclude.h,jerror.h

若果是自己实现读取bmp文件的话就不需要添加上面的5个文件,这些文件可以独立放在一个目录里面再在项目中引用即可;

下面是一个完整的示例,使用Visual Studio2010创建一个c++命令行程序,这里是c和c++混合使用,所以需要配置不使用预编译头才能编译通过,使用下面的代码即可编译运行

#include "stdafx.h"
#include <stdio.h>

extern "C" {  
	#include "jpeglib.h"  
	#include "jmorecfg.h"  
	#include "jconfig.h"  
	#include "cdjpeg.h"
}

/**
* bmp 文件转换为jpg文件
*/
int bmp2jpg(char * input_file_name,char * output_file_name){
	//输入输出文件
	FILE * input_file;
	FILE * output_file;

	//图片转换相关数据结构
	cjpeg_source_ptr src_mgr;
	struct jpeg_compress_struct cinfo;
	struct jpeg_error_mgr jerr;
	JDIMENSION num_scanlines;

	//初始化
	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_compress(&cinfo);

	cinfo.in_color_space = JCS_RGB; /* arbitrary guess */
	jpeg_set_defaults(&cinfo);

	//读取源文件
	input_file = fopen(input_file_name,"rb");  
	if(input_file==0) return 0; 
	src_mgr =jinit_read_bmp(&cinfo);

	src_mgr->input_file = input_file;
	(*src_mgr->start_input) (&cinfo, src_mgr);
	jpeg_default_colorspace(&cinfo);
	
	//创面目标文件
	output_file = fopen(output_file_name,"wb");
	if(output_file==0) return 0;
	jpeg_stdio_dest(&cinfo, output_file);

	/* Start compressor */
	jpeg_start_compress(&cinfo, TRUE);

	/* Process data */
	while (cinfo.next_scanline < cinfo.image_height) {
		num_scanlines = (*src_mgr->get_pixel_rows) (&cinfo, src_mgr);
		(void) jpeg_write_scanlines(&cinfo, src_mgr->buffer, num_scanlines);
	}

	/* Finish compression and release memory */
	(*src_mgr->finish_input) (&cinfo, src_mgr);
	jpeg_finish_compress(&cinfo);
	jpeg_destroy_compress(&cinfo);

	fclose(input_file);
	fclose(output_file);

	return 1;
}


int _tmain(int argc, _TCHAR* argv[])
{
	char input_file_name[128]  = "c:\\a.bmp";
	char output_file_name[128] = "c:\\b.jpg";

	bmp2jpg(input_file_name,output_file_name);
}

这里可以手工截屏使用画图工具保存为a.bmp文件,然后执行上面的程序就看到转换之后的文件了。

总结

c语言实现的转换效率还是非常之高的,可以写成公共库让其他平台的语言调用,只需要传入文件名即可,非常的方便,在linux下的话需要重新编译。