B3log Solo  当前在线人数:12 登录 注册

奔放的胸毛。

天行健 胸毛以自强不息

马勒戈壁

2011-08-17 14:50:23 奔放的胸毛。
2  评论    21,831  浏览

此文来自:编程随想(墙外)

 

今天距离7·23事故正好一周(也是死难者的头七)。虽然过了一周,互联网上依然恶评如潮。当然啦,天朝的真理部也没闲着——不停地删除墙内那些不和谐的声音。俺总是喜欢跟真理部对着干,所以今天特地收集整理了一些图片和微博评论,转载到俺博客上。俺这个博客架设在墙外,且是Google提供的博客平台,朝廷奈何不得。

党经常教导我们——群众的眼睛是雪亮的!所以,本文的图片和评论,皆取材自广大的人民群众(俗称屁民)。另外,很多图片是喷嚏网的每日图卦收集整理的,俺正好捡现成,特此鸣谢。

★事故现场

掩埋车厢的现场照片

奔放的胸毛。

 

@我的隔壁是良人_张洲

这个照片很珍贵,得来不易,请转。再请大家判断:一,这个肉饼里能出来几具全尸。二,我查过,这列火车车票几乎告罄,这只是其中一个车厢。还不包括一样的几节和挂在桥上的几节。那么,死亡人数39人的数据能说得过去吗?

奔放的胸毛。

7月25日,挖掘机正在对事故列车车厢进行拆解。当晚10点30分左右,挖掘车开始对"7·23"甬温线动车追尾事故的列车车厢进行拆解,目前有5台挖掘机正在进行作业。新华社记者 徐昱 摄

奔放的胸毛。

尸骨未寒就急忙通车,到底图个啥?

奔放的胸毛。

奔放的胸毛。

 

★死者家属

 

杨峰,广东肇庆男人。在此次追尾事故中,五位亲人死亡:失去身怀六甲的太太、儿子以及岳父、岳母。几乎灭门。

"只要鞠个躬,就能把问题解决,我相信中国的事故会越来越多的。我给他下跪,从这里跪到你上海铁路局,你能把我的家人还给我吗?我们家属要的是第一时间的营救,而不是这种秀。"他怒斥营救时的草率和作秀。

奔放的胸毛。

 

@新民周刊杨江

25 日下午温州殡仪馆,刚认出怀孕七月妻子尸体的杨峰披麻戴孝质问现场官员你敢随我去看看我面目全非惨不忍睹的亲人遗体吗?他要官方交出是谁宣布无生命迹象转 为抢修铁路的!他失去了妻子岳母及大姨子母女,连同妻子腹内胎儿共五名亲人!现场官员后退:我跟你鞠躬,对不起!杨拒绝:鞠躬有用会发生事故?

奔放的胸毛。

 

@郑启文CTU

今天下午,死者家属到事故现场拦住前来勘察且不愿透露姓名的专家请求给个说法;多次请求无果后家属只好跪在地上并说道"求求您帮我找回我妹妹的另外半个头"。而专家却在......

奔放的胸毛。

多么有力的手啊。。。松开你们的魔爪 !!!她只是失去了亲人

奔放的胸毛。

 

@tmac周

我妹妹还有半个头在里面!!!!(死者家属汤先生声嘶力竭地喊)

奔放的胸毛。

 

D301上传媒大学女生朱平遇难前拨通家里电话。做好一桌菜的朱妈妈从厨房跑去接电话,来电显示是朱平的手机。“你到了?”母亲兴奋。听筒里只传来一点极其轻微的声响。那是重伤的朱平用尽力气留给母亲的最后一点讯息。

今日中青报“冰点”《永不抵达的列车》

 

@吴铭

7月27日,温州南站,遇难者家属提出四点要求:一是公布事故真相;二是铁道部官员直接与家属见面;三是追究相关责任人;四是改善家属待遇,按各地习俗安葬遇难者。(明报)

奔放的胸毛。

 

★事故根源

 

◇责任

 

@X2OOO

首先是雷击造成CTC系统大面积红光带。意思就是,计算机表示,我病了,不能管车了,你们车辆别开了。 然后呢,前一阵子的京沪高铁雷击停运给铁路部门造成了很大压力,于是他们表示,这不行啊,再搞个全线停车媒体不骂翻天吗? 不行,这车必须开,转入非常站控。 再然后,我们的南站发令了,D3115永嘉发车,遇到红灯不停,目视20KM/h限速。也就是**说了,您遇到红灯只管往前开,注意点就行!同时为了红灯开车不被计算机阻挠,D3115的ATP部分关闭,应答机停止工作。也就是D3115开隐身了!它不再向其它列车报告自己的位置。 然后,D301也出来了……不过CTCS已经损坏,所以D301上面一路白灯无码。站上表示没问题,咱继续开!相当于司机给**说:我眼睛出问题了,看不到前车啊!**说:没事,我给你盯着! 然后,D3115准备过桥,同时有乘客拉下了紧急制动。(暂时不知道为什么)。 然后的事情,大家都知道了。

 

@凤凰袁凌

我初步明白了,原来司机没责任,是上海铁路局调度室让前车低速行驶,准备让后车超车,所以后车是以高速运行。但前车并没有驶入备用轨道,导致后车高速驶近来不及刹车。信号系统都是好的,问题全在调度室身上。

 

@谢奕秋

台湾高铁公司表示,台铁采用列车自动控制系统,一个区间内只有一部列车运行,当前车发生故障,另一车绝对进不了这个区间,两车间距至少在1公里以上。台铁 局副局长张应辉表示,2006年台铁全面启用自动防护系统,若号志失灵或司机有任何状况或疏失,列车会自动减速刹车,不明白大陆和谐号为何三关尽失。

 

◇隐患

 

@博飒先生

京沪高铁开通的时候我一直觉得有些不对,总感觉应该没那么快,终于在网上查到了当年的资料,2008年1月份开工的时候说好是工期5年,也就是该在 2013年才预计完工,可事实竟然提前了整整2年!不早不晚正好赶在90大寿之际!这所包含的政治意义大于实际效益、仓促上马的质量大家也可想而知吧。图证:

奔放的胸毛。

 

@香港凤凰周刊

【川崎重工谨慎关注中国动车脱轨事故原因调查】追尾列车疑是日本川崎重工提供技术的CRH2型列车,中国南车公司制造。其最高安全时速在200至275公里。但中方在部分区段开通运营速度达350公里。川崎重工曾让中方立下字据,如果因为超速而导致事故“不追究其责任”。目前正在关注脱轨真正原因。(共同社)

 

@峥椿

北京广渠门铁路桥,跑动车的。今儿被我发现了,有裂缝。这不属于"自然沉降"吧?恐怖。北面墙体外表墙皮都掉了一块儿。 有真相,很担心!

奔放的胸毛。

砖家霸气外露啊!

奔放的胸毛。

日本普通电车司机需半年训练才能驾驶高速列车。中国,当年京津城际通车,30名没有任何经验的大学生,仅经培训半年。 08年李东晓等10位司机,培训10天后就把动车开回北京。李东晓驾驶的京津城际时速达394.3公里,被称"中国第一速"。 此后,10人成为高铁司机教练。

 

★善后处理

 

赔偿

 

胡适日记

听说已经开始进入赔偿流程了,这是一整套程序,开始算一条人命多少钱了,听说提前签合同还有奖励,听说上海铁路系统本来一个人发150元安全无事故奖,现 在出了事,奖金不发了,3000万都拿来赔偿富富有余。38个人一人50万,不到2000万,简直毛毛雨!然后,接下来,等待下一次的杀人狂欢夜!

奔放的胸毛。

 

@haitaode

领取赔偿金,须凭火化证明。

奔放的胸毛。

 

@敬一丹

有报道说,“早签723赔付协议有奖”是谣言,而现在的表述是:“事发后,在短时间接受谈判并签协议的,可酌情予以数万元奖励。” 这有啥区别?

 

@诗风

温州死难者赔偿50万,注意,这里面有20万是保险公司出的,铁道部自己最多出30万,中国公民死于中国火车事故,就是这个价。 我想起2002年5月10日,中国台湾公民刘海若在英国遇到火车脱轨事故,导致脑死亡,经过中英医生奋力抢救,奇迹恢复知觉,经过庭审,最后刘海若获得了300万英镑的赔偿!

 

@李海鹏

事故赔偿,先签协议的遗属可获“奖金”是惯例。成立N个工作组与家属一对一,也是惯例。家属们被安排到不同宾馆,时刻有人陪同,相互不通气,还是惯例。当地如有不同殡仪馆,则遗体多分散安置,总数难统计。遗属中的远房亲戚,情绪相对稳定,考量相对现实,是工作重点。合适者发展为内线,还是惯例。

 

维稳

 

要把维稳摆在非常突出的位置,稳定死者家属、亲友情绪,重视和加强社会舆论,确保社会和谐稳定。

——浙江省副省长王建满在温州召开“7·23”甬温线特别重大铁路交通事故救援善后总指挥部第二次成员会议上表示(南都)

 

@财经网:

【温州律师将不得擅自接受动车事故遇难者家属求助】7月26日,温州市各大律师事务所收到来自温州市司法局、市律师协会发来紧急通知,所有接到寻求法律帮助的律师所和律师,在第一时间向市局律管处和律师协会报告,不得擅自解答与处置。就算家属找上门来,他们也只能把他们推到法律援助中心。(东方早报)

 

@鹏小汤

这是紧靠着事故的村庄,村庄已经和外界拉起警界线,出入必须要有当地暂住证和身份证。其他无关人员一律不许进入,记者更是不许上前靠近。

奔放的胸毛。

 

★官员的嘴脸

 

居然还有这么嚣张的铁道部官员?大伙儿应该把这渣滓人肉出来!

奔放的胸毛。

 

@天佑中华A

【转发一张照片】中午我发了一条微博,问铁道部的领导会不会吃盒饭,现在这张照片证明,他们不但是没吃盒饭,还在温州最高档的酒店吃了饭,也许还喝了茅台。这就是铁道部,这就是中国的政治生态。马英九一年可以吃七百多个盒饭,铁道部的官员,在这种情况下也要吃最好的。

奔放的胸毛。

 

@禹晋永

铁道部的动车,铁道部的管理,铁道部任命的干部,铁道部的事故,铁道部的救人,铁道部的现场清理,铁道部的毁灭证据,铁道部的调查,铁道部的雷雨结论,铁道部的免职干部,铁道部的后事协议,铁道部的补偿金标准,铁道部的奖励金。

这一连串的铁道部哪里还有一丝一毫的公正?这是在向人民炫耀其铁的权力!

 

某匿名网友在俺博客留言

铁道部发言人王勇平先生你好:

您的大女儿王晓英是铁道部财务局主任,大女婿李阁奎是北京市交通局副局长,二女儿王晓霞是北京市计生局处长,二女婿郭亮是北京市中心医院副院长,小儿子王晓飞是铁道部质检科科长,儿媳张宁是市妇联主任。

情况都没错吧?

 

@想做薛定谔的猫

#动车相撞后续进展#凤凰卫视@go佳佳佳确认,铁道部发言人王勇平今晚搭机回京;头等舱。为什么不坐今天匆忙恢复通车的动车到上海,再坐他口中"技术很先进、很有信心"的高铁回京呢?新闻发言人,不该为铁道部的公众形象身体力行么?

@go佳佳佳我们跟王勇平一架飞机。他坐头等舱。已经换好了拖鞋,看着报纸。亲们,我要不要冲过去采访啊????

奔放的胸毛。

 

某铁道部官员爆料:

奔放的胸毛。

 

★官方的忽悠

 

事发次日,党国4大报刊的头版头条:中央军委晋升上将

奔放的胸毛。

 

@宋阳童鞋

看了铁道部新闻发布会就瞎琢磨:这事故要是发生在美国,铁道部人员掩埋车体之前,FBI就在第一时间封锁现场了;要是在日本,内阁又准备换届了;要是在法国,总统准备被弹劾了。但在我们的面前,大爱又开始升华了!!!靠!!!

奔放的胸毛。

 

@冯鹿鹿

晚上打车回来遇见一位愤怒的司机师傅:"船沉了捞不着人那叫失踪,地震了城都被埋了也是失踪,一把火人烧焦了分不出来也叫失踪。现在就两节破车厢掉桥下,顶多胳膊脑袋不在一个地方,找起来很费劲吗?这也能失踪?姑娘你能不能给我解释下什么才叫失踪?!"

啊,劳动人民讲话真是又朴素又智慧啊!

 

@赵楚

历史会做出判决,并给出解释:为什么一个享有世界上最大权力、拥有最多党员的政党,不能展现丝毫最起码的人类正常感情。这个版面不仅是对公众的挑衅,也是对人类数千年文明的公然挑战。

奔放的胸毛。

 

@刘步尘

“国务院:要给民众一个交代”

冒号应该改成逗号!

奔放的胸毛。

 

★外媒的评论

 

(日本朝日新闻) 头版头条:无视残骸的运行

奔放的胸毛。

 

(香港东方日报) 做骚第一,救人第二

奔放的胸毛。

 

(香港东方日报) 救灾无能,全国激愤

奔放的胸毛。

 

(香港明报) 只顾通车不救人 他妈的!

奔放的胸毛。

 

★网友的讽刺

 

葛优的评论(该帐号已被删,无法确认是否葛优本人)

奔放的胸毛。

 

师北宸:

【灾后生存三大法则】

法则1:千万不要等待政府救援;

法则2:在遵循法则1的情况下,和其他幸存者互相帮助尽快逃离现场;

法则3:如果不幸在事故中受伤,一定要赶在重型机械碾压、挖矿、掩埋之前从废墟里爬出来。

补充法则:千万不要抱有幻想,这是中国!

 

每次出门乘坐交通工具前,请记得换上最喜欢的衣服,认真发一条微博,审视自己的QQ签名是否得体,拥抱身边人,亲吻自己的狗,清空自己的移动硬盘,听一遍最喜欢的那首歌,给偶像留言,勇敢地去见情敌最后一面,然后把车票送他。

 

@成德明

以前删贴,现在删车厢了。

奔放的胸毛。

 

◇"耻辱号"

 

中国高速铁路,百度英文翻译为 China Railway High-speed,简称CRH,民间俗称“耻辱号”。

 

@魔法學院-腐班长

悟空,坐这车真的很快到西天吗?

是的,师傅,很快的......

奔放的胸毛。

 

上海西南某高校闵行校区著名涂鸦墙的新作

奔放的胸毛。

 

@ZEEKO

终于有一天,和谐号也被和谐,不因为一场事故,而因为一个奇迹,还被所有人看见了。

奔放的胸毛。

 

至于你信不信,由你,我反正信了

 

原来语出《寻秦记》啊!

奔放的胸毛。

这样的动车永远不会相撞。至于你信不信,我反正信了!

奔放的胸毛。

30多度的高温下冰雹。可见这个世界有多么大的冤情啊。至于你信不信,由你,我反正是信了

奔放的胸毛。

 

◇雷公

 

谁他妈的出事故再赖到我雷公电母身上,老子第一个劈死你!!!劈!死!你!

奔放的胸毛。

 

@许文广

电闪雷鸣,北京下起了冰雹!今天,上海冰雹、合肥冰雹、重庆冰雹、石家庄冰雹、湖北襄阳冰雹、广东梅州冰雹……

难道,被一直用来顶雷的雷公真的怒了?

奔放的胸毛。

窈窕淑女,君子好逑

2011-08-14 11:02:20 奔放的胸毛。
2  评论    17,074  浏览

谁,执我之手,消我半世孤独;

谁,吻我之眸,遮我半世流离;

 

谁,抚我之面,慰我半世哀伤;

谁,携我之心,融我半世冰霜;

 

谁,扶我之肩,驱我一世沉寂。

谁,唤我之心,掩我一生凌轹。

 

谁,弃我而去,留我一世独殇;

谁,可明我意,使我此生无憾;

谁,可助我臂,纵横万载无双;

谁,可倾我心,寸土恰似虚弥;

 

谁,可葬吾怆,笑天地虚妄,吾心狂。

伊,覆我之唇,祛我前世流离;

伊,揽我之怀,除我前世轻浮。

 

执子之手,陪你痴狂千生;

深吻子眸,伴你万世轮回。

 

执子之手,共你一世风霜;

吻子之眸,赠你一世深情。

 

我, 牵尔玉手, 收你此生所有;

我, 抚尔秀颈, 挡你此生风雨。

 

予,挽子青丝,挽子一世情思;

予,执子之手,共赴一世情长;

曾,以父之名,免你一生哀愁;

曾,怜子之情,祝你一生平安。

 

——六世喇嘛·仓央嘉措诗集

,

深入分析 Java 中的中文编码问题——乱码是如何产生的

2011-08-08 10:15:28 奔放的胸毛。
0  评论    25,818  浏览

From:IBM 文档库

几种常见的编码格式

为什么要编码

不知道大家有没有想过一个问题,那就是为什么要编码?我们能不能不编码?要回答这个问题必须要回到计算机是如何表示我们人类能够理解的符号的,这些符号也就是我们人类使用的语言。由于人类的语言有太多,因而表示这些语言的符号太多,无法用计算机中一个基本的存储单元—— byte 来表示,因而必须要经过拆分或一些翻译工作,才能让计算机能理解。我们可以把计算机能够理解的语言假定为英语,其它语言要能够在计算机中使用必须经过一次翻译,把它翻译成英语。这个翻译的过程就是编码。所以可以想象只要不是说英语的国家要能够使用计算机就必须要经过编码。这看起来有些霸道,但是这就是现状,这也和我们国家现在在大力推广汉语一样,希望其它国家都会说汉语,以后其它的语言都翻译成汉语,我们可以把计算机中存储信息的最小单位改成汉字,这样我们就不存在编码问题了。

所以总的来说,编码的原因可以总结为:

  1. 计算机中存储信息的最小单元是一个字节即 8 个 bit,所以能表示的字符范围是 0~255 个
  2. 人类要表示的符号太多,无法用一个字节来完全表示
  3. 要解决这个矛盾必须需要一个新的数据结构 char,从 char 到 byte 必须编码

如何“翻译”

明白了各种语言需要交流,经过翻译是必要的,那又如何来翻译呢?计算中提拱了多种翻译方式,常见的有 ASCII、ISO-8859-1、GB2312、GBK、UTF-8、UTF-16 等。它们都可以被看作为字典,它们规定了转化的规则,按照这个规则就可以让计算机正确的表示我们的字符。目前的编码格式很多,例如 GB2312、GBK、UTF-8、UTF-16 这几种格式都可以表示一个汉字,那我们到底选择哪种编码格式来存储汉字呢?这就要考虑到其它因素了,是存储空间重要还是编码的效率重要。根据这些因素来正确选择编码格式,下面简要介绍一下这几种编码格式。

  • ASCII 码

学过计算机的人都知道 ASCII 码,总共有 128 个,用一个字节的低 7 位表示,0~31 是控制字符如换行回车删除等;32~126 是打印字符,可以通过键盘输入并且能够显示出来。

  • ISO-8859-1

128 个字符显然是不够用的,于是 ISO 组织在 ASCII 码基础上又制定了一些列标准用来扩展 ASCII 编码,它们是 ISO-8859-1~ISO-8859-15,其中 ISO-8859-1 涵盖了大多数西欧语言字符,所有应用的最广泛。ISO-8859-1 仍然是单字节编码,它总共能表示 256 个字符。

  • GB2312

它的全称是《信息交换用汉字编码字符集 基本集》,它是双字节编码,总的编码范围是 A1-F7,其中从 A1-A9 是符号区,总共包含 682 个符号,从 B0-F7 是汉字区,包含 6763 个汉字。

  • GBK

全称叫《汉字内码扩展规范》,是国家技术监督局为 windows95 所制定的新的汉字内码规范,它的出现是为了扩展 GB2312,加入更多的汉字,它的编码范围是 8140~FEFE(去掉 XX7F)总共有 23940 个码位,它能表示 21003 个汉字,它的编码是和 GB2312 兼容的,也就是说用 GB2312 编码的汉字可以用 GBK 来解码,并且不会有乱码。

  • GB18030

全称是《信息交换用汉字编码字符集》,是我国的强制标准,它可能是单字节、双字节或者四字节编码,它的编码与 GB2312 编码兼容,这个虽然是国家标准,但是实际应用系统中使用的并不广泛。

  • UTF-16

说到 UTF 必须要提到 Unicode(Universal Code 统一码),ISO 试图想创建一个全新的超语言字典,世界上所有的语言都可以通过这本字典来相互翻译。可想而知这个字典是多么的复杂,关于 Unicode 的详细规范可以参考相应文档。Unicode 是 Java 和 XML 的基础,下面详细介绍 Unicode 在计算机中的存储形式。

UTF-16 具体定义了 Unicode 字符在计算机中存取方法。UTF-16 用两个字节来表示 Unicode 转化格式,这个是定长的表示方法,不论什么字符都可以用两个字节表示,两个字节是 16 个 bit,所以叫 UTF-16。UTF-16 表示字符非常方便,每两个字节表示一个字符,这个在字符串操作时就大大简化了操作,这也是 Java 以 UTF-16 作为内存的字符存储格式的一个很重要的原因。

  • UTF-8

UTF-16 统一采用两个字节表示一个字符,虽然在表示上非常简单方便,但是也有其缺点,有很大一部分字符用一个字节就可以表示的现在要两个字节表示,存储空间放大了一倍,在现在的网络带宽还非常有限的今天,这样会增大网络传输的流量,而且也没必要。而 UTF-8 采用了一种变长技术,每个编码区域有不同的字码长度。不同类型的字符可以是由 1~6 个字节组成。

UTF-8 有以下编码规则:

  1. 如果一个字节,最高位(第 8 位)为 0,表示这是一个 ASCII 字符(00 - 7F)。可见,所有 ASCII 编码已经是 UTF-8 了。
  2. 如果一个字节,以 11 开头,连续的 1 的个数暗示这个字符的字节数,例如:110xxxxx 代表它是双字节 UTF-8 字符的首字节。
  3. 如果一个字节,以 10 开始,表示它不是首字节,需要向前查找才能得到当前字符的首字节
 

Java 中需要编码的场景

前面描述了常见的几种编码格式,下面将介绍 Java 中如何处理对编码的支持,什么场合中需要编码。

I/O 操作中存在的编码

我们知道涉及到编码的地方一般都在字符到字节或者字节到字符的转换上,而需要这种转换的场景主要是在 I/O 的时候,这个 I/O 包括磁盘 I/O 和网络 I/O,关于网络 I/O 部分在后面将主要以 Web 应用为例介绍。下图是 Java 中处理 I/O 问题的接口:


Figure xxx. Requires a heading 

Reader 类是 Java 的 I/O 中读字符的父类,而 InputStream 类是读字节的父类,InputStreamReader 类就是关联字节到字符的桥梁,它负责在 I/O 过程中处理读取字节到字符的转换,而具体字节到字符的解码实现它由 StreamDecoder 去实现,在 StreamDecoder 解码过程中必须由用户指定 Charset 编码格式。值得注意的是如果你没有指定 Charset,将使用本地环境中的默认字符集,例如在中文环境中将使用 GBK 编码。

写的情况也是类似,字符的父类是 Writer,字节的父类是 OutputStream,通过 OutputStreamWriter 转换字符到字节。如下图所示:


Figure xxx. Requires a heading 

同样 StreamEncoder 类负责将字符编码成字节,编码格式和默认编码规则与解码是一致的。

如下面一段代码,实现了文件的读写功能:


清单 1.I/O 涉及的编码示例

String file = "c:/stream.txt"; 
 String charset = "UTF-8"; 
 // 写字符换转成字节流
 FileOutputStream outputStream = new FileOutputStream(file); 
 OutputStreamWriter writer = new OutputStreamWriter( 
 outputStream, charset); 
 try { 
    writer.write("这是要保存的中文字符"); 
 } finally { 
    writer.close(); 
 } 
 // 读取字节转换成字符
 FileInputStream inputStream = new FileInputStream(file); 
 InputStreamReader reader = new InputStreamReader( 
 inputStream, charset); 
 StringBuffer buffer = new StringBuffer(); 
 char[] buf = new char[64]; 
 int count = 0; 
 try { 
    while ((count = reader.read(buf)) != -1) { 
        buffer.append(buffer, 0, count); 
    } 
 } finally { 
    reader.close(); 
 } 				 
 

 

在我们的应用程序中涉及到 I/O 操作时只要注意指定统一的编解码 Charset 字符集,一般不会出现乱码问题,有些应用程序如果不注意指定字符编码,中文环境中取操作系统默认编码,如果编解码都在中文环境中,通常也没问题,但是还是强烈的不建议使用操作系统的默认编码,因为这样,你的应用程序的编码格式就和运行环境绑定起来了,在跨环境下很可能出现乱码问题。

内存中操作中的编码

在 Java 开发中除了 I/O 涉及到编码外,最常用的应该就是在内存中进行字符到字节的数据类型的转换,Java 中用 String 表示字符串,所以 String 类就提供转换到字节的方法,也支持将字节转换为字符串的构造函数。如下代码示例:

 String s = "这是一段中文字符串"; 
 byte[] b = s.getBytes("UTF-8"); 
 String n = new String(b,"UTF-8");  

 

另外一个是已经被被废弃的 ByteToCharConverter 和 CharToByteConverter 类,它们分别提供了 convertAll 方法可以实现 byte[] 和 char[] 的互转。如下代码所示:

 ByteToCharConverter charConverter = ByteToCharConverter.getConverter("UTF-8"); 
 char c[] = charConverter.convertAll(byteArray); 
 CharToByteConverter byteConverter = CharToByteConverter.getConverter("UTF-8"); 
 byte[] b = byteConverter.convertAll(c);  

 

这两个类已经被 Charset 类取代,Charset 提供 encode 与 decode 分别对应 char[] 到 byte[] 的编码和 byte[] 到 char[] 的解码。如下代码所示:

 Charset charset = Charset.forName("UTF-8"); 
 ByteBuffer byteBuffer = charset.encode(string); 
 CharBuffer charBuffer = charset.decode(byteBuffer);  

 

编码与解码都在一个类中完成,通过 forName 设置编解码字符集,这样更容易统一编码格式,比 ByteToCharConverter 和 CharToByteConverter 类更方便。

Java 中还有一个 ByteBuffer 类,它提供一种 char 和 byte 之间的软转换,它们之间转换不需要编码与解码,只是把一个 16bit 的 char 格式,拆分成为 2 个 8bit 的 byte 表示,它们的实际值并没有被修改,仅仅是数据的类型做了转换。如下代码所以:

 ByteBuffer heapByteBuffer = ByteBuffer.allocate(1024); 
 ByteBuffer byteBuffer = heapByteBuffer.putChar(c); 

 

以上这些提供字符和字节之间的相互转换只要我们设置编解码格式统一一般都不会出现问题。

 

Java 中如何编解码

前面介绍了几种常见的编码格式,这里将以实际例子介绍 Java 中如何实现编码及解码,下面我们以“I am 君山”这个字符串为例介绍 Java 中如何把它以 ISO-8859-1、GB2312、GBK、UTF-16、UTF-8 编码格式进行编码的。


清单 2.String 编码

 public static void encode() { 
        String name = "I am 君山"; 
        toHex(name.toCharArray()); 
        try { 
            byte[] iso8859 = name.getBytes("ISO-8859-1"); 
            toHex(iso8859); 
            byte[] gb2312 = name.getBytes("GB2312"); 
            toHex(gb2312); 
            byte[] gbk = name.getBytes("GBK"); 
            toHex(gbk); 
            byte[] utf16 = name.getBytes("UTF-16"); 
            toHex(utf16); 
            byte[] utf8 = name.getBytes("UTF-8"); 
            toHex(utf8); 
        } catch (UnsupportedEncodingException e) { 
            e.printStackTrace(); 
        } 
 }				 
 

 

我们把 name 字符串按照前面说的几种编码格式进行编码转化成 byte 数组,然后以 16 进制输出,我们先看一下 Java 是如何进行编码的。

下面是 Java 中编码需要用到的类图


图 1. Java 编码类图
图 1. Java 编码类图 

首先根据指定的 charsetName 通过 Charset.forName(charsetName) 设置 Charset 类,然后根据 Charset 创建 CharsetEncoder 对象,再调用 CharsetEncoder.encode 对字符串进行编码,不同的编码类型都会对应到一个类中,实际的编码过程是在这些类中完成的。下面是 String. getBytes(charsetName) 编码过程的时序图


图 2.Java 编码时序图
图 2.Java 编码时序图 

从上图可以看出根据 charsetName 找到 Charset 类,然后根据这个字符集编码生成 CharsetEncoder,这个类是所有字符编码的父类,针对不同的字符编码集在其子类中定义了如何实现编码,有了 CharsetEncoder 对象后就可以调用 encode 方法去实现编码了。这个是 String.getBytes 编码方法,其它的如 StreamEncoder 中也是类似的方式。下面看看不同的字符集是如何将前面的字符串编码成 byte 数组的?

如字符串“I am 君山”的 char 数组为 49 20 61 6d 20 541b 5c71,下面把它按照不同的编码格式转化成相应的字节。

按照 ISO-8859-1 编码

字符串“I am 君山”用 ISO-8859-1 编码,下面是编码结果:


Figure xxx. Requires a heading 

从上图看出 7 个 char 字符经过 ISO-8859-1 编码转变成 7 个 byte 数组,ISO-8859-1 是单字节编码,中文“君山”被转化成值是 3f 的 byte。3f 也就是“?”字符,所以经常会出现中文变成“?”很可能就是错误的使用了 ISO-8859-1 这个编码导致的。中文字符经过 ISO-8859-1 编码会丢失信息,通常我们称之为“黑洞”,它会把不认识的字符吸收掉。由于现在大部分基础的 Java 框架或系统默认的字符集编码都是 ISO-8859-1,所以很容易出现乱码问题,后面将会分析不同的乱码形式是怎么出现的。

按照 GB2312 编码

字符串“I am 君山”用 GB2312 编码,下面是编码结果:


Figure xxx. Requires a heading 

GB2312 对应的 Charset 是 sun.nio.cs.ext. EUC_CN 而对应的 CharsetDecoder 编码类是 sun.nio.cs.ext. DoubleByte,GB2312 字符集有一个 char 到 byte 的码表,不同的字符编码就是查这个码表找到与每个字符的对应的字节,然后拼装成 byte 数组。查表的规则如下:

 c2b[c2bIndex[char >> 8] + (char & 0xff)] 

 

如果查到的码位值大于 oxff 则是双字节,否则是单字节。双字节高 8 位作为第一个字节,低 8 位作为第二个字节,如下代码所示:

 if (bb > 0xff) {    // DoubleByte 
            if (dl - dp < 2) 
                return CoderResult.OVERFLOW; 
            da[dp++] = (byte) (bb >> 8); 
            da[dp++] = (byte) bb; 
 } else {                      // SingleByte 
            if (dl - dp < 1) 
                return CoderResult.OVERFLOW; 
            da[dp++] = (byte) bb; 
 } 

 

从上图可以看出前 5 个字符经过编码后仍然是 5 个字节,而汉字被编码成双字节,在第一节中介绍到 GB2312 只支持 6763 个汉字,所以并不是所有汉字都能够用 GB2312 编码。

按照 GBK 编码

字符串“I am 君山”用 GBK 编码,下面是编码结果:


Figure xxx. Requires a heading 

你可能已经发现上图与 GB2312 编码的结果是一样的,没错 GBK 与 GB2312 编码结果是一样的,由此可以得出 GBK 编码是兼容 GB2312 编码的,它们的编码算法也是一样的。不同的是它们的码表长度不一样,GBK 包含的汉字字符更多。所以只要是经过 GB2312 编码的汉字都可以用 GBK 进行解码,反过来则不然。

按照 UTF-16 编码

字符串“I am 君山”用 UTF-16 编码,下面是编码结果:


Figure xxx. Requires a heading 

用 UTF-16 编码将 char 数组放大了一倍,单字节范围内的字符,在高位补 0 变成两个字节,中文字符也变成两个字节。从 UTF-16 编码规则来看,仅仅将字符的高位和地位进行拆分变成两个字节。特点是编码效率非常高,规则很简单,由于不同处理器对 2 字节处理方式不同,Big-endian(高位字节在前,低位字节在后)或 Little-endian(低位字节在前,高位字节在后)编码,所以在对一串字符串进行编码是需要指明到底是 Big-endian 还是 Little-endian,所以前面有两个字节用来保存 BYTE_ORDER_MARK 值,UTF-16 是用定长 16 位(2 字节)来表示的 UCS-2 或 Unicode 转换格式,通过代理对来访问 BMP 之外的字符编码。

按照 UTF-8 编码

字符串“I am 君山”用 UTF-8 编码,下面是编码结果:


Figure xxx. Requires a heading 

UTF-16 虽然编码效率很高,但是对单字节范围内字符也放大了一倍,这无形也浪费了存储空间,另外 UTF-16 采用顺序编码,不能对单个字符的编码值进行校验,如果中间的一个字符码值损坏,后面的所有码值都将受影响。而 UTF-8 这些问题都不存在,UTF-8 对单字节范围内字符仍然用一个字节表示,对汉字采用三个字节表示。它的编码规则如下:


清单 3.UTF-8 编码代码片段

 private CoderResult encodeArrayLoop(CharBuffer src, 
 ByteBuffer dst){ 
            char[] sa = src.array(); 
            int sp = src.arrayOffset() + src.position(); 
            int sl = src.arrayOffset() + src.limit(); 
            byte[] da = dst.array(); 
            int dp = dst.arrayOffset() + dst.position(); 
            int dl = dst.arrayOffset() + dst.limit(); 
            int dlASCII = dp + Math.min(sl - sp, dl - dp); 
            // ASCII only loop 
            while (dp < dlASCII && sa[sp] < '\u0080') 
                da[dp++] = (byte) sa[sp++]; 
            while (sp < sl) { 
                char c = sa[sp]; 
                if (c < 0x80) { 
                    // Have at most seven bits 
                    if (dp >= dl) 
                        return overflow(src, sp, dst, dp); 
                    da[dp++] = (byte)c; 
                } else if (c < 0x800) { 
                    // 2 bytes, 11 bits 
                    if (dl - dp < 2) 
                        return overflow(src, sp, dst, dp); 
                    da[dp++] = (byte)(0xc0 | (c >> 6)); 
                    da[dp++] = (byte)(0x80 | (c & 0x3f)); 
                } else if (Character.isSurrogate(c)) { 
                    // Have a surrogate pair 
                    if (sgp == null) 
                        sgp = new Surrogate.Parser(); 
                    int uc = sgp.parse(c, sa, sp, sl); 
                    if (uc < 0) { 
                        updatePositions(src, sp, dst, dp); 
                        return sgp.error(); 
                    } 
                    if (dl - dp < 4) 
                        return overflow(src, sp, dst, dp); 
                    da[dp++] = (byte)(0xf0 | ((uc >> 18))); 
                    da[dp++] = (byte)(0x80 | ((uc >> 12) & 0x3f)); 
                    da[dp++] = (byte)(0x80 | ((uc >>  6) & 0x3f)); 
                    da[dp++] = (byte)(0x80 | (uc & 0x3f)); 
                    sp++;  // 2 chars 
                } else { 
                    // 3 bytes, 16 bits 
                    if (dl - dp < 3) 
                        return overflow(src, sp, dst, dp); 
                    da[dp++] = (byte)(0xe0 | ((c >> 12))); 
                    da[dp++] = (byte)(0x80 | ((c >>  6) & 0x3f)); 
                    da[dp++] = (byte)(0x80 | (c & 0x3f)); 
                } 
                sp++; 
            } 
            updatePositions(src, sp, dst, dp); 
            return CoderResult.UNDERFLOW; 
 } 				 

 

UTF-8 编码与 GBK 和 GB2312 不同,不用查码表,所以在编码效率上 UTF-8 的效率会更好,所以在存储中文字符时 UTF-8 编码比较理想。

几种编码格式的比较

对中文字符后面四种编码格式都能处理,GB2312 与 GBK 编码规则类似,但是 GBK 范围更大,它能处理所有汉字字符,所以 GB2312 与 GBK 比较应该选择 GBK。UTF-16 与 UTF-8 都是处理 Unicode 编码,它们的编码规则不太相同,相对来说 UTF-16 编码效率最高,字符到字节相互转换更简单,进行字符串操作也更好。它适合在本地磁盘和内存之间使用,可以进行字符和字节之间快速切换,如 Java 的内存编码就是采用 UTF-16 编码。但是它不适合在网络之间传输,因为网络传输容易损坏字节流,一旦字节流损坏将很难恢复,想比较而言 UTF-8 更适合网络传输,对 ASCII 字符采用单字节存储,另外单个字符损坏也不会影响后面其它字符,在编码效率上介于 GBK 和 UTF-16 之间,所以 UTF-8 在编码效率上和编码安全性上做了平衡,是理想的中文编码方式。

 

Java Web 涉及到的编码

对于使用中文来说,有 I/O 的地方就会涉及到编码,前面已经提到了 I/O 操作会引起编码,而大部分 I/O 引起的乱码都是网络 I/O,因为现在几乎所有的应用程序都涉及到网络操作,而数据经过网络传输都是以字节为单位的,所以所有的数据都必须能够被序列化为字节。在 Java 中数据被序列化必须继承 Serializable 接口。

这里有一个问题,你是否认真考虑过一段文本它的实际大小应该怎么计算,我曾经碰到过一个问题:就是要想办法压缩 Cookie 大小,减少网络传输量,当时有选择不同的压缩算法,发现压缩后字符数是减少了,但是并没有减少字节数。所谓的压缩只是将多个单字节字符通过编码转变成一个多字节字符。减少的是 String.length(),而并没有减少最终的字节数。例如将“ab”两个字符通过某种编码转变成一个奇怪的字符,虽然字符数从两个变成一个,但是如果采用 UTF-8 编码这个奇怪的字符最后经过编码可能又会变成三个或更多的字节。同样的道理比如整型数字 1234567 如果当成字符来存储,采用 UTF-8 来编码占用 7 个 byte,采用 UTF-16 编码将会占用 14 个 byte,但是把它当成 int 型数字来存储只需要 4 个 byte 来存储。所以看一段文本的大小,看字符本身的长度是没有意义的,即使是一样的字符采用不同的编码最终存储的大小也会不同,所以从字符到字节一定要看编码类型。

另外一个问题,你是否考虑过,当我们在电脑中某个文本编辑器里输入某个汉字时,它到底是怎么表示的?我们知道,计算机里所有的信息都是以 01 表示的,那么一个汉字,它到底是多少个 0 和 1 呢?我们能够看到的汉字都是以字符形式出现的,例如在 Java 中“淘宝”两个字符,它在计算机中的数值 10 进制是 28120 和 23453,16 进制是 6bd8 和 5d9d,也就是这两个字符是由这两个数字唯一表示的。Java 中一个 char 是 16 个 bit 相当于两个字节,所以两个汉字用 char 表示在内存中占用相当于四个字节的空间。

这两个问题搞清楚后,我们看一下 Java Web 中那些地方可能会存在编码转换?

用户从浏览器端发起一个 HTTP 请求,需要存在编码的地方是 URL、Cookie、Parameter。服务器端接受到 HTTP 请求后要解析 HTTP 协议,其中 URI、Cookie 和 POST 表单参数需要解码,服务器端可能还需要读取数据库中的数据,本地或网络中其它地方的文本文件,这些数据都可能存在编码问题,当 Servlet 处理完所有请求的数据后,需要将这些数据再编码通过 Socket 发送到用户请求的浏览器里,再经过浏览器解码成为文本。这些过程如下图所示:


图 3. 一次 HTTP 请求的编码示例(查看大图
图 3. 一次 HTTP 请求的编码示例 

如上图所示一次 HTTP 请求设计到很多地方需要编解码,它们编解码的规则是什么?下面将会重点阐述一下:

URL 的编解码

用户提交一个 URL,这个 URL 中可能存在中文,因此需要编码,如何对这个 URL 进行编码?根据什么规则来编码?有如何来解码?如下图一个 URL:


图 4.URL 的几个组成部分
图 4.URL 的几个组成部分 

上图中以 Tomcat 作为 Servlet Engine 为例,它们分别对应到下面这些配置文件中:

Port 对应在 Tomcat 的 <Connector port="8080"/> 中配置,而 Context Path 在 <Context path="/examples"/> 中配置,Servlet Path 在 Web 应用的 web.xml 中的

<servlet-mapping> 
        <servlet-name>junshanExample</servlet-name> 
        <url-pattern>/servlets/servlet/*</url-pattern> 
</servlet-mapping>

 

<url-pattern> 中配置,PathInfo 是我们请求的具体的 Servlet,QueryString 是要传递的参数,注意这里是在浏览器里直接输入 URL 所以是通过 Get 方法请求的,如果是 POST 方法请求的话,QueryString 将通过表单方式提交到服务器端,这个将在后面再介绍。

上图中 PathInfo 和 QueryString 出现了中文,当我们在浏览器中直接输入这个 URL 时,在浏览器端和服务端会如何编码和解析这个 URL 呢?为了验证浏览器是怎么编码 URL 的我们选择 FireFox 浏览器并通过 HTTPFox 插件观察我们请求的 URL 的实际的内容,以下是 URL:HTTP://localhost:8080/examples/servlets/servlet/ 君山 ?author= 君山在中文 FireFox3.6.12 的测试结果


图 5. HTTPFox 的测试结果
图 5. HTTPFox 的测试结果 

君山的编码结果分别是:e5 90 9b e5 b1 b1,be fd c9 bd,查阅上一届的编码可知,PathInfo 是 UTF-8 编码而 QueryString 是经过 GBK 编码,至于为什么会有“%”?查阅 URL 的编码规范 RFC3986 可知浏览器编码 URL 是将非 ASCII 字符按照某种编码格式编码成 16 进制数字然后将每个 16 进制表示的字节前加上“%”,所以最终的 URL 就成了上图的格式了。

默认情况下中文 IE 最终的编码结果也是一样的,不过 IE 浏览器可以修改 URL 的编码格式在选项 -> 高级 -> 国际里面的发送 UTF-8 URL 选项可以取消。

从上面测试结果可知浏览器对 PathInfo 和 QueryString 的编码是不一样的,不同浏览器对 PathInfo 也可能不一样,这就对服务器的解码造成很大的困难,下面我们以 Tomcat 为例看一下,Tomcat 接受到这个 URL 是如何解码的。

解析请求的 URL 是在 org.apache.coyote.HTTP11.InternalInputBuffer 的 parseRequestLine 方法中,这个方法把传过来的 URL 的 byte[] 设置到 org.apache.coyote.Request 的相应的属性中。这里的 URL 仍然是 byte 格式,转成 char 是在 org.apache.catalina.connector.CoyoteAdapter 的 convertURI 方法中完成的:

 protected void convertURI(MessageBytes uri, Request request) 
 throws Exception { 
        ByteChunk bc = uri.getByteChunk(); 
        int length = bc.getLength(); 
        CharChunk cc = uri.getCharChunk(); 
        cc.allocate(length, -1); 
        String enc = connector.getURIEncoding(); 
        if (enc != null) { 
            B2CConverter conv = request.getURIConverter(); 
            try { 
                if (conv == null) { 
                    conv = new B2CConverter(enc); 
                    request.setURIConverter(conv); 
                } 
            } catch (IOException e) {...} 
            if (conv != null) { 
                try { 
                    conv.convert(bc, cc, cc.getBuffer().length - 
 cc.getEnd()); 
                    uri.setChars(cc.getBuffer(), cc.getStart(), 
 cc.getLength()); 
                    return; 
                } catch (IOException e) {...} 
            } 
        } 
        // Default encoding: fast conversion 
        byte[] bbuf = bc.getBuffer(); 
        char[] cbuf = cc.getBuffer(); 
        int start = bc.getStart(); 
        for (int i = 0; i < length; i++) { 
            cbuf[i] = (char) (bbuf[i + start] & 0xff); 
        } 
        uri.setChars(cbuf, 0, length); 
 }  

 

从上面的代码中可以知道对 URL 的 URI 部分进行解码的字符集是在 connector 的 <Connector URIEncoding=”UTF-8”/> 中定义的,如果没有定义,那么将以默认编码 ISO-8859-1 解析。所以如果有中文 URL 时最好把 URIEncoding 设置成 UTF-8 编码。

QueryString 又如何解析? GET 方式 HTTP 请求的 QueryString 与 POST 方式 HTTP 请求的表单参数都是作为 Parameters 保存,都是通过 request.getParameter 获取参数值。对它们的解码是在 request.getParameter 方法第一次被调用时进行的。request.getParameter 方法被调用时将会调用 org.apache.catalina.connector.Request 的 parseParameters 方法。这个方法将会对 GET 和 POST 方式传递的参数进行解码,但是它们的解码字符集有可能不一样。POST 表单的解码将在后面介绍,QueryString 的解码字符集是在哪定义的呢?它本身是通过 HTTP 的 Header 传到服务端的,并且也在 URL 中,是否和 URI 的解码字符集一样呢?从前面浏览器对 PathInfo 和 QueryString 的编码采取不同的编码格式不同可以猜测到解码字符集肯定也不会是一致的。的确是这样 QueryString 的解码字符集要么是 Header 中 ContentType 中定义的 Charset 要么就是默认的 ISO-8859-1,要使用 ContentType 中定义的编码就要设置 connector 的 <Connector URIEncoding=”UTF-8” useBodyEncodingForURI=”true”/> 中的 useBodyEncodingForURI 设置为 true。这个配置项的名字有点让人产生混淆,它并不是对整个 URI 都采用 BodyEncoding 进行解码而仅仅是对 QueryString 使用 BodyEncoding 解码,这一点还要特别注意。

从上面的 URL 编码和解码过程来看,比较复杂,而且编码和解码并不是我们在应用程序中能完全控制的,所以在我们的应用程序中应该尽量避免在 URL 中使用非 ASCII 字符,不然很可能会碰到乱码问题,当然在我们的服务器端最好设置 <Connector/> 中的 URIEncoding 和 useBodyEncodingForURI 两个参数。

HTTP Header 的编解码

当客户端发起一个 HTTP 请求除了上面的 URL 外还可能会在 Header 中传递其它参数如 Cookie、redirectPath 等,这些用户设置的值很可能也会存在编码问题,Tomcat 对它们又是怎么解码的呢?

对 Header 中的项进行解码也是在调用 request.getHeader 是进行的,如果请求的 Header 项没有解码则调用 MessageBytes 的 toString 方法,这个方法将从 byte 到 char 的转化使用的默认编码也是 ISO-8859-1,而我们也不能设置 Header 的其它解码格式,所以如果你设置 Header 中有非 ASCII 字符解码肯定会有乱码。

我们在添加 Header 时也是同样的道理,不要在 Header 中传递非 ASCII 字符,如果一定要传递的话,我们可以先将这些字符用 org.apache.catalina.util.URLEncoder 编码然后再添加到 Header 中,这样在浏览器到服务器的传递过程中就不会丢失信息了,如果我们要访问这些项时再按照相应的字符集解码就好了。

POST 表单的编解码

在前面提到了 POST 表单提交的参数的解码是在第一次调用 request.getParameter 发生的,POST 表单参数传递方式与 QueryString 不同,它是通过 HTTP 的 BODY 传递到服务端的。当我们在页面上点击 submit 按钮时浏览器首先将根据 ContentType 的 Charset 编码格式对表单填的参数进行编码然后提交到服务器端,在服务器端同样也是用 ContentType 中字符集进行解码。所以通过 POST 表单提交的参数一般不会出现问题,而且这个字符集编码是我们自己设置的,可以通过 request.setCharacterEncoding(charset) 来设置。

另外针对 multipart/form-data 类型的参数,也就是上传的文件编码同样也是使用 ContentType 定义的字符集编码,值得注意的地方是上传文件是用字节流的方式传输到服务器的本地临时目录,这个过程并没有涉及到字符编码,而真正编码是在将文件内容添加到 parameters 中,如果用这个编码不能编码时将会用默认编码 ISO-8859-1 来编码。

HTTP BODY 的编解码

当用户请求的资源已经成功获取后,这些内容将通过 Response 返回给客户端浏览器,这个过程先要经过编码再到浏览器进行解码。这个过程的编解码字符集可以通过 response.setCharacterEncoding 来设置,它将会覆盖 request.getCharacterEncoding 的值,并且通过 Header 的 Content-Type 返回客户端,浏览器接受到返回的 socket 流时将通过 Content-Type 的 charset 来解码,如果返回的 HTTP Header 中 Content-Type 没有设置 charset,那么浏览器将根据 Html 的 <meta HTTP-equiv="Content-Type" content="text/html; charset=GBK" /> 中的 charset 来解码。如果也没有定义的话,那么浏览器将使用默认的编码来解码。

其它需要编码的地方

除了 URL 和参数编码问题外,在服务端还有很多地方可能存在编码,如可能需要读取 xml、velocity 模版引擎、JSP 或者从数据库读取数据等。

xml 文件可以通过设置头来制定编码格式

 <?xml version="1.0" encoding="UTF-8"?> 

 

Velocity 模版设置编码格式:

 services.VelocityService.input.encoding=UTF-8 

 

JSP 设置编码格式:

 <%@page contentType="text/html; charset=UTF-8"%> 

 

访问数据库都是通过客户端 JDBC 驱动来完成,用 JDBC 来存取数据要和数据的内置编码保持一致,可以通过设置 JDBC URL 来制定如 MySQL:url="jdbc:mysql://localhost:3306/DB?useUnicode=true&characterEncoding=GBK"。 

 

常见问题分析

在了解了 Java Web 中可能需要编码的地方后,下面看一下,当我们碰到一些乱码时,应该怎么处理这些问题?出现乱码问题唯一的原因都是在 char 到 byte 或 byte 到 char 转换中编码和解码的字符集不一致导致的,由于往往一次操作涉及到多次编解码,所以出现乱码时很难查找到底是哪个环节出现了问题,下面就几种常见的现象进行分析。

中文变成了看不懂的字符

例如,字符串“淘!我喜欢!”变成了“Ì Ô £ ¡Î Ò Ï²»¶ £ ¡”编码过程如下图所示


Figure xxx. Requires a heading 

字符串在解码时所用的字符集与编码字符集不一致导致汉字变成了看不懂的乱码,而且是一个汉字字符变成两个乱码字符。

一个汉字变成一个问号

例如,字符串“淘!我喜欢!”变成了“??????”编码过程如下图所示


Figure xxx. Requires a heading 

将中文和中文符号经过不支持中文的 ISO-8859-1 编码后,所有字符变成了“?”,这是因为用 ISO-8859-1 进行编解码时遇到不在码值范围内的字符时统一用 3f 表示,这也就是通常所说的“黑洞”,所有 ISO-8859-1 不认识的字符都变成了“?”。

一个汉字变成两个问号

例如,字符串“淘!我喜欢!”变成了“????????????”编码过程如下图所示


Figure xxx. Requires a heading 

这种情况比较复杂,中文经过多次编码,但是其中有一次编码或者解码不对仍然会出现中文字符变成“?”现象,出现这种情况要仔细查看中间的编码环节,找出出现编码错误的地方。

一种不正常的正确编码

还有一种情况是在我们通过 request.getParameter 获取参数值时,当我们直接调用

String value = request.getParameter(name);  

 

会出现乱码,但是如果用下面的方式

String value = String(request.getParameter(name).getBytes("
 ISO-8859-1"), "GBK");  			

 

解析时取得的 value 会是正确的汉字字符,这种情况是怎么造成的呢?

看下如所示:


Figure xxx. Requires a heading 

这种情况是这样的,ISO-8859-1 字符集的编码范围是 0000-00FF,正好和一个字节的编码范围相对应。这种特性保证了使用 ISO-8859-1 进行编码和解码可以保持编码数值“不变”。虽然中文字符在经过网络传输时,被错误地“拆”成了两个欧洲字符,但由于输出时也是用 ISO-8859-1,结果被“拆”开的中文字的两半又被合并在一起,从而又刚好组成了一个正确的汉字。虽然最终能取得正确的汉字,但是还是不建议用这种不正常的方式取得参数值,因为这中间增加了一次额外的编码与解码,这种情况出现乱码时因为 Tomcat 的配置文件中 useBodyEncodingForURI 配置项没有设置为”true”,从而造成第一次解析式用 ISO-8859-1 来解析才造成乱码的。

 

总结

本文首先总结了几种常见编码格式的区别,然后介绍了支持中文的几种编码格式,并比较了它们的使用场景。接着介绍了 Java 那些地方会涉及到编码问题,已经 Java 中如何对编码的支持。并以网络 I/O 为例重点介绍了 HTTP 请求中的存在编码的地方,以及 Tomcat 对 HTTP 协议的解析,最后分析了我们平常遇到的乱码问题出现的原因。

综上所述,要解决中文问题,首先要搞清楚哪些地方会引起字符到字节的编码以及字节到字符的解码,最常见的地方就是读取会存储数据到磁盘,或者数据要经过网络传输。然后针对这些地方搞清楚操作这些数据的框架的或系统是如何控制编码的,正确设置编码格式,避免使用软件默认的或者是操作系统平台默认的编码格式。

 

参考资料

学习

  • Unicode 编码规范,详细描述了 Unicode 如何编码。 

  • ISO-8859-1 编码,详细介绍了 ISO-8859-1 的一些细节。 

  • RFC3986 规范,详细描述了 URL 编码规范 

  • HTTP 协议,W3C 关于 HTTP 协议的详细描述。 

  • 查看文章 《 Tomcat 系统架构与设计模式》(developerWorks,2010 年 5 月):了解 Tomcat 中容器的体系结构,基本的工作原理,以及 Tomcat 中使用的经典的设计模式介绍。 

  • Servlet 工作原理解析,(developerWorks,2011 年 2 月):以 Tomcat 为例了解 Servlet 容器是如何工作的?一个 Web 工程在 Servlet 容器中是如何启动的? Servlet 容器如何解析你在 web.xml 中定义的 Servlet ?用户的请求是如何被分配给指定的 Servlet 的? Servlet 容器如何管理 Servlet 生命周期?你还将了解到最新的 Servlet 的 API 的类层次结构,以及 Servlet 中一些难点问题的分析。 

  • developerWorks Java 技术专区:这里有数百篇关于 Java 编程各个方面的文章。 

讨论

  • 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。
,

送给所有的朋友,当你浮躁时愿你能获得内心的安宁

2011-08-05 21:27:26 奔放的胸毛。
0  评论    14,545  浏览



    人们往往会失去理智,失去逻辑,以自我为中心……不管怎样,原谅他们!
    如果你是善良的,别人却指责你自私,别有用心……不管怎样,还是要善良!
    如果你成功了,可能赢得虚伪的朋友,树立真正的敌人……不管怎样,还是要成功!
    如果你是诚实和坦率的,别人却欺骗了你……不管怎样,还是要诚实和坦率!
    如果你花费数年创建的,别人却毁于一旦……不管怎样,还是要继续创建!
    如果你感受到安宁和幸福,别人却妒忌……不管怎样,还是要幸福!
    你今天做的善事,人们往往明天就会忘记……不管怎样,还是要做善事。
    把你最好的给予世界,即使那永远没有尽头……不管怎样,还是要给予!
    你看,归根究底,它是你和上帝之间的事,不管怎样,从不是你和别人之间的事。


                                                    ——德蕾莎修女(Mother Theresa)

极度内涵的一个Java类

2011-08-05 15:31:08 奔放的胸毛。
2  评论    19,470  浏览

一个蛋疼的程序员(作者Google+)写的一个蛋疼的Java Class,这个类充分体现了天朝程序员各种闷骚与明骚、各种奔放与蛋疼百花齐放的美好局面。

public class Goverment{
	private String name = "People's Republic of China";
	private List departments; 
	
	private People you = new People();//people except official
	private People I = new People(); //official
	
	
	//the goverment of PRC. singleton mode.
	private static Goverment instance = new instance();
	
	private static Goverment(){
		departments = new ArrayList;
	}
	
	/**
	* must be singleton mode
	*/
	public Goverment getInstace(){
		return instance;
	}
	
	/**
	* if the target is not in the list of deparment,build one and return;
	*/
	private Department getDepartment(String name){
		for(int i = 0;i < departments.size();i++){
			if(departments.get(i).name.equals(name)){
				return departments.get(i);
			}
		}
		Departments department = new Department(name);
		list.add(department);
		return department;
	}
	
	/**
	* handle the accident,and modify something
	*/
	public void handle(Accident accident){
		if(accident.dead.length > 9 && accident.dead.length < 35){
			accident.dead = new People[9];
		}else if(accident.dead.length > 35){
			accident.dead = new People[35];
		}
		accident.injured = new People[accident.injured.length >> 1];
	}
	
	/**
	*report the accident after do #handle();
	*/
	public void report(accident){
		System.out.println("dead:" + accident.length);
		System.out.println("injured:" + accident.length);
		//cast the reason type to Thunderstrike
		System.out.println("reason:" + ((Thunderstrike)accident.getReason()).class.getName());
		
		try{
			
			if(accident.isSomebodyStillAlive()){
				if(you.believe() || !you.believe()){
					i.believe();
				}
				throw SomeBodyStillAliveExeception();
			}
		}catch(SomeBodyStillAliveExeception e){
			System.out.println("It is a miracle");
		}finally{
			System.out.println("it is just happened");
		}
	}
	
	/**
	*department of PRC.
	*/
	protected class Department{
		//only a field
		String name;
		
		/**
		* build a department 
		*/
		Department(String name){
			this.name = name;
		}
		
		/**
		* nothing to return , only need some teas and newpapers
		*/
		void duty(Tea[] tea,Newspaper[] newspaper){
			//Nothing to do 
		}	
	}
	
	/**
	* inner class of Goverment, so the goverment can modify everything of accident
	*/
	public static class Accident{
		People[] dead;
		People[] injured;
		Reason reason;
		
		public Accident(Object[] objects,People[] dead,People[] injured,Reason reason){
			objects = null;
			this.dead = dead;
			this.injured = injured;
		}
		
		public int getDeadNumber(){
			return dead.length;
		}
		
		public int getInjuredNumber(){
			return injured.length;
		}
		
		public Reason getReason(){
			return this.reason();
		}
		
	}
	
	public static void main(Stirng[] args){
		Goverment goverment = Goverment.getInstance();
		Goverment.Department department = Goverment.getDeparment("Ministry of Railways");
		Goverment.Accident accident = new Goverment.Accident(new Coach[4],new People[200],new People[200],new Misstake());
		
		goverment.handle(accident);
		
		goverment.report(accident);
	}
}
, ,

身边这些五花八门的搜索引擎

2011-03-08 17:53:34 奔放的胸毛。
5  评论    16,664  浏览

有数据的地方就会有Search数据,有需求就会有市场,有市场就会有产品,最后,腾讯无处不在。

搜索先驱

Google

Baidu

YaHoo

在中文搜索这块,除了GoogleBaiduYahoo等这样的搜索引擎先驱,后来的各种搜索也是五花八门。

soso

在国内IT圈子这一亩三分地上哪也少不了腾讯。这是腾讯最近大力扶持的搜搜搜索。

下载资源搜索

狗狗

VeryCD

此类代表:狗狗(迅雷)、VeryCD(电驴)。

两大主流下载工具旗下的资源搜索社区,网上常走动的都知道,不多说。

这里偷偷地推荐个巨暴力的资源搜索,搜啥有啥,你懂的。http://www.hatrix.org/

图片搜索

绝大多数的传统搜索引擎都具备图片搜索功能,但是这里不得不强烈推荐一个开发人员必备的Icon搜索引擎,它里面所有的图片只有两个格式,icon和png。对于我个人来说,很有用很有用。

Icon Finder

Icon Finder

不过这款搜索引擎不是国内产的,用汉字一张图片都搜不到……

但是只要你不是纯英语白痴的话还是很容易使用的。

音乐相关搜索

搜MP3歌曲就不说了,google、baidu、sousou、酷狗等等到处都是。

搜谱

曲谱搜索:搜谱网

目前最大的中文曲谱搜索,还不错,我常用。

我爱歌词

LRC歌词搜索:我爱歌词

当年MP3这个东西刚出来的时候(想想那个时候我读初中,又瘦又小的个子……)跟着冒出来的,那时候搜MP3的地方很多,但是没歌词。很长一段时间我都没能用上我的MP3自动播放歌词的功能,后来发现这么个网站,就常在这里找歌词。MP3播放器出来这么多年了,这个网站也跟着发展了这么多年。

奇特环境下的奇特产物

个人认为,以下这些搜索引擎除了“百谷歌度”这货还不错之外,另外的几乎没有任何实际使用意义。

谷姐

无厘头代表:谷姐

谷姐一下,寂寞全消。

人民搜索

河蟹代表:人民搜索

人民搜索,讲述人民的故事。

河蟹天朝生产的又一废品,只能搜新闻,并且几乎全是表达出“大家要相信党,相信政府!”这样思想的新闻。

早在人民搜索还处于实验室阶段的时候我测试了下,那个时候在里面还能够搜索出全套的岛国爱情动作片演员的简历。

BaiGoogledu

创意代表:百Google度

这个东西的实用性还是不错的,它本身不做任何搜索工作,只是将谷歌和百度的搜索结果并列放在一个页面内,左边是Google的搜索结果,右边是Baidu的搜索结果。输入一个关键字就能得到两个搜索引擎的搜索结果,在很多时候还是很有用的。

 

最后,节日快乐。

ISO 639-1 语言代码

2011-03-04 17:59:51 奔放的胸毛。
0  评论    22,010  浏览

存档备查。

ISO 639-1 为各种语言定义了缩略词。您可以在 HTML 和 XHTML 中的 lang 和 xml:lang 属性中使用它们。

LanguageISO Code
Abkhazian ab
Afar aa
Afrikaans af
Albanian sq
Amharic am
Arabic ar
Armenian hy
Assamese as
Aymara ay
Azerbaijani az
Bashkir ba
Basque eu
Bengali (Bangla) bn
Bhutani dz
Bihari bh
Bislama bi
Breton br
Bulgarian bg
Burmese my
Byelorussian (Belarusian) be
Cambodian km
Catalan ca
Cherokee  
Chewa  
Chinese (Simplified) zh
Chinese (Traditional) zh
Corsican co
Croatian hr
Czech cs
Danish da
Divehi  
Dutch nl
Edo  
English en
Esperanto eo
Estonian et
Faeroese fo
Farsi fa
Fiji fj
Finnish fi
Flemish  
French fr
Frisian fy
Fulfulde  
Galician gl
Gaelic (Scottish) gd
Gaelic (Manx) gv
Georgian ka
German de
Greek el
Greenlandic kl
Guarani gn
Gujarati gu
Hausa ha
Hawaiian  
Hebrew he, iw
Hindi hi
Hungarian hu
Ibibio  
Icelandic is
Igbo  
Indonesian id, in
Interlingua ia
Interlingue ie
Inuktitut iu
Inupiak ik
Irish ga
Italian it
Japanese ja
Javanese jv
Kannada kn
Kanuri  
Kashmiri ks
Kazakh kk
Kinyarwanda (Ruanda) rw
Kirghiz ky
Kirundi (Rundi) rn
Konkani  
Korean ko
Kurdish ku
Laothian lo
Latin la
Latvian (Lettish) lv
Limburgish ( Limburger) li
Lingala ln
Lithuanian lt
Macedonian mk
Malagasy mg
Malay ms
Malayalam ml
   
Maltese mt
Maori mi
Marathi mr
Moldavian mo
Mongolian mn
Nauru na
Nepali ne
Norwegian no
Occitan oc
Oriya or
Oromo (Afan, Galla) om
Papiamentu  
Pashto (Pushto) ps
Polish pl
Portuguese pt
Punjabi pa
Quechua qu
Rhaeto-Romance rm
Romanian ro
Russian ru
Sami (Lappish)  
Samoan sm
Sangro sg
Sanskrit sa
Serbian sr
Serbo-Croatian sh
Sesotho st
Setswana tn
Shona sn
Sindhi sd
Sinhalese si
Siswati ss
Slovak sk
Slovenian sl
Somali so
Spanish es
Sundanese su
Swahili (Kiswahili) sw
Swedish sv
Syriac  
Tagalog tl
Tajik tg
Tamazight  
Tamil ta
Tatar tt
Telugu te
Thai th
Tibetan bo
Tigrinya ti
Tonga to
Tsonga ts
Turkish tr
Turkmen tk
Twi tw
Uighur ug
Ukrainian uk
Urdu ur
Uzbek uz
Venda  
Vietnamese vi
Volapuk vo
Welsh cy
Wolof wo
Xhosa xh
Yi  
Yiddish yi, ji
Yoruba yo
Zulu zu

 

, ,

一个开发者的Web Browser——Chrome

2011-03-03 18:30:34 奔放的胸毛。
10  评论    29,824  浏览

Chrome

作为一名开发人员,使用一些比较Sharp的工具来辅助生产,产生的效果不仅仅是能够用事半功倍来表现的。

在web开发方面,一个开发者的浏览器不应该仅仅只是个单纯的“浏览·—·器”,一个犀利的Developer是绝对不能使用IE这种破铜烂铁的。尽管IE4当年风靡全球,但是据我所知IE6已经是十年前的产物了,十年前。

一个脑袋正常点的开发人员看了此文之后是绝对不会使用IE货的,从以下几个方面稍微比对下国内主流浏览器的实用性(本文所说的IE指IE9以下版本,由于IE9只支持win7,寡人XP没法测试。)。其它一些是否好看漂亮,是否安全,是否支持收藏夹同步等比较浮云的东西就不测了。

  1. 启动速度
  2. javascript执行效率
  3. 开发人员辅助
  4. CSS兼容性
  5. HTML5支持
 启动速度JS执行效率开发人员辅助CSS3.0兼容性HTML5支持
IE8  较快  个人无法忍受  除了查看源文件一无所有  CSS2的屁股都没擦干净  ×
Chrome  快  极快  比较丰富  √  √
FireFox  个人无法忍受  快  极其丰富  √  √
Opera  慢  较快  比较丰富  √  √
Apple Safari  慢  较快  一般丰富  √  自己说支持,事实上不是那回事

其实业内口碑相传,谁好谁坏已经有了大概的印象,除了FireFox和Chrome比较引人注目之外Opera和Safari其实也不错,IE就不提了,懒得骂它。

肯定还有人要弱弱的问,360安全浏览器、腾讯TT浏览器、遨游、世界之窗等等那些浏览器难道不是国内常用的浏览器么?为什么不拿出来溜溜?嗯,寡人再一次耐心地解答下:这些浏览器就是IE浏览器,外观不一样而已,实质木有区别。(遨游虽然有内核切换,但基本上还是属IE的)

其实除了IE系列实在是惨不忍睹十分稀烂之外其它浏览器都是很不错的,我使用Chrome的最重要原因有两点,第一:它非常简洁,界面上每一个按钮都是你所十分需要的,我讨厌凌乱不堪看似强大的一堆工具条堆在浏览器的上方。第二:它十分体贴,像一个温柔的女仆一样细心观察我的生活习性,从而更好的“照顾”我。对于这种享受,我是不太介意它窥探我的部分数据的。在这方面对Google还是大可放心的,像疼讯和360就要小心点了。

我比较排斥FireFox,第一是它的启动速度实在忍受不了,双击Open之后像个SB一样在那苕等着它启动完毕。第二是它太花哨了,它到处都是无所不能功能强大的圈圈叉叉。我个人不喜欢这样的风格。第三是它会自作主张地修改所有页面的字体,使得我看不到原始页面的真面目。

目前我用Chrome还比较爽,推荐一些好用的插件。

Google Dictionary (by Google)

Google Dictionary (by Google)

 

它不仅仅是个翻译工具,事实上它是本辞典。像我这样在English方面比较落后的有本十分便捷的字典是再好不过了,双击网页上的某个单词就能立马显示出其解释,图中展示的是英文解释,安装此插件后可以设置,让它显示中文解释。

红杏浏览

红杏浏览

如果发现哪个网页被墙了,右键—红杏浏览。如此方便,你有什么理由不选择它?

截图快手

Chrome上的截图插件有好几款,但是这一款是全中文的,这足以让像我这样英文不好的人选择它了。它同样的支持超长网页页面的整页截图和即时图片简单处理。不可多得的是,在截图完毕后你可以一键上传图片至互联网,并提供给你此图片的分享连接。

提到截图,我个人使用的一款桌面截图工具那就不得不提了。名字叫FSCapture,是一款桌面应用程序,绿色,无需安装;简体中文;体积1.4M;截图时自动将鼠标所在区域放大,可用方向键移动鼠标位置,微调精确到每一个像素;截图完毕后支持裁剪批注等简单编辑;支持自定义矩形区域截图、自定义不规则区域截图、截取活动窗口、截取指定窗口的工作区域等截图模式;附带屏幕放大镜功能;附带屏幕取色功能,取色时鼠标区域放大,看清楚没个像素之后再决定取哪个像素的颜色。包好用,点击这里下载。

Google Mail Checker

Google Mail Checker

Google Mail Checker用于显示你当前的Gmail邮箱中的未读邮件数量,点击它即可打开你的Gmail。使用Gmail的人必备。

AdBlock

AdBlock

一款十分舒服的广告过滤器,装上它就够了,什么都不用干,能极大的减少广告的困扰。HotMail邮箱内的广告不是一般的多,装上它之后打开我的live邮箱,广告全被Clear掉了。有中文配置界面,建议不要修改过滤列表的选项,默认勾选这几个就很好了。

Resize Window

Resize Window

你开发时使用的是一块巨大的显示器,分辨率高达1680 x 1050。但是我们都知道网页制作时的1024 x 768标准,为了观察页面在1024 x 768的显示器上的显示效果我们通常有两种办法。第一,找一个1024 x 768的小尺寸显示器过来。第二,将本计算机上的显示器显示分辨率修改为1024 x 768。这都不是好主意,使用Resize Window可以将浏览器调整为任意大小(不要问我能不能整成1 x 1这样的白痴问题),来观察网页在此分辨率下的表现。

如果可选的尺寸列表中没有你想要的,你可以手动添加任意尺寸,比如800 x 600。

Note Anywhere

Note Anywhere

在某个网页上贴一张便签,写上几个字,然后某天打开此网页,便签还在那。居家必备!

Firebug Lite For Google Chrome

Firebug

看样子是从火狐平台上整过来的,与Chrome自带的“审查元素”功能类似,查看样式、javascript调试、DOM查看等。个人感觉很不错,开发必备。

但是Chrome原本就带的“审查元素”有些功能Firebug是没有的,比如查看网页中每个文件的加载时间、此页面上每个文件的大小和类型、在此页面上所有文件中查找字符串等功能还是挺实用的,这些是Firebug没有的。

可喜的是这两个家伙完全不存在冲突,可以同时打开同时使用。

Neat Bookmarks

Neat Bookmarks

一款十分舒服的“收藏夹”管理插件。在IE时代里叫收藏夹,Chrome里叫书签,收藏的每一个网页地址都是一个书签。装一个吧,挺好用的。

Web Developer

Pendule

Chrome SEO

这三款插件也是我的Chrome上安装了的,前两款同样是开发人员辅助工具,并且功能相似,好坏我也不好说,各位自己去感受吧。

第三款是一款SEO辅助工具,我不是专业的SEO,这个对我的作用貌似不怎么大。

 

Chrome中还有很多实用的插件,感兴趣的可以在Chrome插件库中找,总有你想要的。

最后不得不提的是Chrome的同步功能,公司一台电脑,家里一台电脑,都使用Chrome。在公司添加新的书签或安装新的插件,甚至是修改插件的配置都可以通过Chrome的同步功能进行同步。

说说具体做法,首先你得有一个google(比如Gmail账号,B³log Solo账号等)或者msn账号都可以。打开Chrome,点击右上角的小扳手-选项-个人资料-进行同步,输入账户信息,选择全部同步即可,这样不管是书签还是插件,都能在全球范围内同步了。

你可以让两个或者两个以上的浏览器同步,只需要你使用同一账户登录就行。同步操作是浏览器自动执行的,你不用手动去点击某个按钮让其同步。

 

IE?你out了。

,

男性也有生理周期

2011-03-03 14:01:19 奔放的胸毛。
4  评论    21,034  浏览

男性例假周期多为2个月 易出现性欲减退等症状男性例假周期多为2个月 易出现性欲减退等症状

 

女人有“例假”,实际上男人也有“例假”。每两个月左右,男人总有那么几天心理上出现意志消沉、精神不振、焦虑、易发火、抑郁等,生理上主要表现为没有“性趣”、头痛、失眠、过敏、磨牙、反胃等。

从生理学角度来说,男人是不可能来“例假”的,但男性每隔一定的时间,总有那么几天出现情绪上的波动和身体不适,医学上称之为男性“低潮”现 象、男性“例假”。这实际上是一种生物节律变化,是男性机体激素水平变化的结果。男性“例假”周期一般在两个月左右,在30岁以上的男性中,超过半数的人 都会在两个月左右经历一次“例假”。

男人“例假”的常见表现有:口舌生疮,牙龈肿痛,甚至口腔溃疡;食欲不振,即便美食也不为所动;经常独自抽着闷烟,时不时长长地喘粗气;郁郁寡 欢;拿筷子的手变得有点发抖,菜撒落在餐桌上;“性趣”大减,变得冷淡,见了妻子或女友也不再有甜言蜜语,当她们接近时,感到不习惯。此外,男性在“例 假”期还可能有的症状包括:头痛、失眠、过敏、磨牙、反胃、背痛、脖子僵硬等。

From:MSN·健康

惊鸿一瞥的青春 无比残酷的现实

2011-02-25 20:51:45 奔放的胸毛。
2  评论    11,186  浏览

总是有一些视频让我们感动或深思,在“生活”这条道路上走得越远,别人的故事更易让我们产生共鸣。因为我们似曾相识,于是彼此间给予安慰,情感流露,爱的涌现,人心冷暖。沉浸在别人的故事里感受生命的意义,忘却了现实。神识忽醒,心中无限感慨,仿佛有无数的话语在喉间哽咽。

惊鸿一瞥的青春,无比残酷的现实。我们所能做的,除了努力,还是努力。努力,多么简单的字眼。

——天行健 胸毛以自强不息

,

公告

业精于勤,荒于嬉;行成于思,毁于随。

有些事现在不做,一辈子都不会做了
这一刻就计划
下一刻就实施
绝对不给自己找任何退却的理由
做,去做
做了才能有改变,有收获

最新评论

评论最多的文章

访问最多的文章

分类标签

友情链接

存档

TOP
Copyright (c) 2009-2017, b3log.org