The NoteBook of EricKong

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  611 Posts :: 1 Stories :: 190 Comments :: 0 Trackbacks

#

WebSphere Application Server Base Trial for Windows Base Trial
English U.S.
2007-12-06

WebSphere Application Server V6.1 for Windows
was.cd.6100.trial.base.windows.ia32.zip  (518.6MB)

以下下载均需IBM 官方账号登陆

IBM Download director:

Download director is required when downloading Common Criteria certified products.

https://www14.software.ibm.com/webapp/iwm/web/reg/download.do?source=was60&S_TACT=109BH33W&lang=en_US&S_PKG=win61&cp=UTF-8&&&&dlmethod=dd

HTTP 下载:

https://www6.software.ibm.com/sdfdl/v2/regs2/was60/was61/Xa.2/Xb.XZT0eQtlFEAtKGXnBl9SUC_lc7t2gur7TkGfwVu9dg/Xc.was61/was.cd.6100.trial.base.windows.ia32.zip/Xd./Xf.LPr.D1vk/Xg.5528606/Xi.was60/XY.regsrvs/XZ.3FTPmw76Xk9MVyoZYj0pxpNV5xA/was.cd.6100.trial.base.windows.ia32.zip

posted @ 2011-05-24 12:11 Eric_jiang 阅读(1465) | 评论 (0)编辑 收藏

很多人初学mainframe都觉得比较困难,就是因为mainframe不像unix或linux,在自己的PC机上很容易就可以搭建起来。但是mainframe操作系统z/OS又不像AIX,只有在IBM的power机上才可以安装运行。在普通的x86或者x64平台上搭建z/OS的虚拟系统,是完全可行的。当然,理论上讲,Mac OS上也是可以虚拟出z/OS的,只是,还没听说有人试过。

想要在自己的PC上搭建一个z/OS虚拟系统,你需要以下几样东西:

一台PC机,最好有2GHz CPU,1G以上内存,硬盘空间10G足矣 
z/OS系统的卷文件。
卷文件有两种,一种是DEMOPKG,只对IBM内部员工发行。另外一种是ADCD,可惜,这一种卷文件也不是免费的,只针对IBM合作伙伴和mainframe用户发行,但是,通过某些非正常途径,也是可以获取到的,只要用于非商业目的,也无大碍,低调一点就好,你懂的。
电驴上一位无私的哥们,也提供了ADCD z/OS 1.9的下载源。地址是:http://www.verycd.com/topics/280391/
本文将以ADCD z/OS 1.6作为范例搭建虚拟z/OS,启动和配置过程与此处下载的1.9并无很大差别。如果哪位需要1.6的卷文件,可以在此留下email。
3270客户端。
- 最常用的当然是IBM Personal Communication,简称PCOM,该软件只能运行于windows平台,并且功能强大,也是最常用的3270客户端。PCOM并非是免费软件,但是你也可以很容易获得该软件,之前提到的verycd链接里就可以下载。同样,保持低调。
- TN3270也是一种不错的选择,虽然TN3270也并非免费,但是你可以通过在其官网注册,即可获得一个evaluation licence,有效期30天。详情请访问TN3270官网:http://www.sdisw.com
- 据我所知,目前唯一免费且开源的3270客户端就是x3270了,可以在http://x3270.bgp.nu下载到源码或者安装包(windows和Linux平台皆可安装)。另外,目前很流行的发行版Ubuntu linux,在其官方源也提供了x3270下载,可以通过命令sudo apt-get install x3270安装。
Hercules
正是有了Hercules,在PC机上虚拟z/OS操作系统才成为了现实。这是一个完全免费开源的软件,Hercules官网http://www.hercules-390.org/提供了hercules在windows,linux和Mac OS平台上的安装包以及源码。该网站上也有许多关于使用和配置hercules的文章,有兴趣可以常上去看看。
另外,如果你使用的是windows系统,为了操作方便,也可以下一个Hercules GUI,下载链接是http://www.softdevlabs.com/Hercules/hercgui-index.html,此处,需要注意一个叫Fish Lib的动态链接库,需要把这个DLL文件解压后跟HercGUI放在同一路径下,这样就不会运行的时候报错了。并且HercGUI要求安装VC Redistributable package,去微软的网站下载安装就好了。
TCP/IP支持软件,可选,不是必须安装的
说这个东东是TCPIP支持软件其实有点勉强。其实是网络封包抓取工具,使用这个可以在hercules上开启tcpip,让你的z/OS系统可以在你的物理局域网中使用FTP,DB2 DRDA等等,对于初学mainframe的人,这个不是必须的。没有这个也一样可以在自己的机器上运行z/OS的。所以,开启TCPIP将会在以后的文章中单独讨论。这里仅仅列出几个可选的封包抓取工具,他们是:CTCI-W32,WinPCap,FishPack和TunTap32。需要注意的是,根据PC机所使用的操作系统版本不同,这类工具所需要的版本也是不一样的,不是任何一种搭配都可以保证成功的在z/OS上开启TCP/IP的。我在Windows XP 32bit上使用WinPCAPC 4.0成功的开启了TCPIP,在win7下就失败了。
在linux下也可以选择一个类似于WinpCap的工具,名为libpcap,Ubuntu的官方源里也可以下载到。
说了这么多,发现有点乱,还是整理一个check list吧

在Windows平台下:
1. http://www.verycd.com/topics/280391/ 下载卷文件(后缀名为CCKD的),PCOM和配置文件zOS1.9.cnf
2. http://www.hercules-390.org/ 下载Hercules 3.07安装包,注意你的系统是32bit还是64bit的,选择相应的下载
3. 安装Microsoft Visual C++ 2008 Service Pack 1 Redistributable Package,google一下就可以了
4. http://www.softdevlabs.com/Hercules/hercgui-index.html 下载Hercules Windows GUI –Version 1.11.1以及FishLib
5. http://www.winpcap.org/ 下载winpcap 4.0
6. http://www.softdevlabs.com/Hercules/ctci-w32-index.html 下载CTCI-W32

在Linux平台下(以Ubuntu为例):
1. http://www.verycd.com/topics/280391/ 下载卷文件(后缀名为CCKD的)和配置文件zOS1.9.cnf
2. http://www.hercules-390.org/ 下载Hercules 3.07 source tarball。redhat也可以下载RPM包
3. 安装x3270,使用命令sudo apt-get update,然后sudo apt-get install x3270
4. 安装LibpCap,使用命令sudo apt-get install libpcap
注:在以上第一步所下载的CCKD文件在linux中直接使用可能会报错,说无法识别的格式。这是因为该CCKD文件是通过windows下的hercules压缩而来。所以,这里需要将CCKD文件在windows中还原为CKD,然后copy到linux下重新压缩为CCKD文件即可。一个3390-3卷对应的CKD文件大约是2.8GB,如果要进行转换,中途可能需要比较多的磁盘空间,压缩完成后,卷文件大小与原CCKD文件基本一样。具体方法在之后讲到在linux下启动z/os的时候再说。

 

 

首先,我们约定以下几个路径,以便存放z/OS模拟环境所需的各项文件和程序:

D:\ADCDV1R6                                所有与模拟环境相关的东西都放在这个目录下
D:\ADCDV1R6\ZOSV1R6              存放卷文件,即CCKD文件
D:\ADCDV1R6\Config                    存放启动配置文件
D:\ADCDV1R6\HercGUI-1.11.1    存放Hercules GUI和FishLib
D:\ADCDV1R6\hercules-3.07         存放Hercules的可执行文件和CTCI-W32
D:\ADCDV1R6\Log                       存放系统运行日志

这样安排文件夹,可以把所有与z/OS模拟器运行相关的程序和文件都集中在一起,这样在系统重装,或者换另一台PC机运行的时候,只需要备份D盘下的ADCDV1R6文件夹,并且安装winpcap和PCOM就可以了。
然后,对照《准备工作》一文中的各项下载的文件,对应放入以上文件夹:
1. CCKD文件放入D:\ADCDV1R6\ZOSV1R6,cnf配置文件放入D:\ADCDV1R6\Config,PCOM稍后安装
2. Hercules-3.07,下载ZIP包,解压后放入D:\ADCDV1R6\hercules-3.07,注意不要多设置了一层文件夹,即D:\ADCDV1R6\hercules-3.07下面就是Hercules的运行文件了,而不是解压后的hercules-3.07文件夹,也就是说不要存在一个D:\ADCDV1R6\hercules-3.07\hercules-3.07文件夹。
3. VC++ Redistribute pack,这个自行默认安装就好了。如果已经安装过就跳过这一步。(装了office的机器一般都有这个了,直接跳过)
4. 将HercGUI和FishLib解压缩放入D:\ADCDV1R6\HercGUI-1.11.1
5. WinPCap也是在windows下默认安装就好
6. CTCI-W32压缩包里包括了FishPack, TunTap32和TT32Test,一并解压缩放入D:\ADCDV1R6\hercules-3.07即可。
最后,安装PCOM,一路点下一步,全部用默认设置就可以了。

在运行Hercules之前,还需要编辑一下Config文件夹下的配置文件。在配置文件中,以#开头的行表示注释。需要注意的是如下配置文件中,红色字体所标出的部分。
- LOADPARM是z/OS在IPL时需要用到的参数,参数选择不同,z/OS中启动后,运行的组件也不同,此参数见文章最后所附的LOADPARM Description。
- MAINSIZE表示Hercules占用的物理内存大小,单位是MB,如果内存够大,最好设为1024,在这里暂且设为832,建议不要小于512,否则MIPS会很低,系统运行会非常的慢。
- NUMCPU表示虚拟的主机有多少个CPU,如果CPU不够强悍,就改为1吧,我的物理机上用的是Intel Core-Due E6400,设为2,运行起来速度还不错。
- Display Terminals 表示3270终端的数量,演示所用的3270终端号是从0700开始的,以十六进制记录,如0700-0710则表示16个端口,其中0700是控制台专用端口,0701至0710为用户连接端口。
- 然后是后面的DASD Device那一段,需要注意的是,要把所有下载的CCKD文件都列进去,这一段的每一行分为三个部分,第一个是设备编号,如0A80,第二个是设备类型,如3390,第三个是卷文件的路径和文件名,这里可以写绝对路径也可以是相对路径。个人觉得相对路径比较好,因为写相对路径,在还盘复制后,不需要重新配置这个文件。
- 最后一段CTC Adapters是跟启动TCPIP有关的,这里暂且放在那里,以后再说。

#
# Hercules Emulator Control file...
# Description: zos160
# MaxShutdownSecs: 15
#
#
# System parameters
#

ARCHMODE  z/Arch
CNSLPORT  3270
CONKPALV  (3,1,10)
CPUMODEL  2064
CPUSERIAL 011519
CPUVERID  00
ECPSVM    NO
LOADPARM  0A8232M1
LPARNAME  HERCULES
MAINSIZE  832
MOUNTED_TAPE_REINIT  DISALLOW
NUMCPU    2
OSTAILOR  Z/OS
PANRATE   30
PGMPRDOS  LICENSED
SHCMDOPT  NODIAG8
SYSEPOCH  1900
TIMERINT  50
TZOFFSET  -0000
YROFFSET  0
HERCPRIO  0
TODPRIO   -20
DEVPRIO   8
CPUPRIO   15

# Display Terminals
0700-0708    3270

# DASD Devices
0A80    3390    ..\ZOSV1R6\Z6RES1.cckd
0A81    3390    ..\ZOSV1R6\Z6res2.cckd
0A82    3390    ..\ZOSV1R6\Z6SYS1.cckd
0A83    3390    ..\ZOSV1R6\Z6uss1.cckd
0A84    3390    ..\ZOSV1R6\Z6uss2.cckd
0A85    3390    ..\ZOSV1R6\Z6CIC1.cckd
0A86    3390    ..\ZOSV1R6\Z6db81.cckd
0A87    3390    ..\ZOSV1R6\Z6db82.cckd
0A88    3390    ..\ZOSV1R6\Z6ims1.cckd
0A89    3390    ..\ZOSV1R6\Z6was1.cckd
0A8A    3390    ..\ZOSV1R6\Z6was2.cckd
0AC1    3390    ..\ZOSV1R6\page01.cckd
0AC2    3390    ..\ZOSV1R6\page02.cckd
0AC3    3390    ..\ZOSV1R6\dump01.cckd
0AC4    3390    ..\ZOSV1R6\dump02.cckd
0AC5    3390    ..\ZOSV1R6\work01.cckd
0AC6    3390    ..\ZOSV1R6\work02.cckd
0AC7    3390    ..\ZOSV1R6\user01.cckd
0AC8    3390    ..\ZOSV1R6\user02.cckd
0AC9    3390    ..\ZOSV1R6\user03.cckd
0ACA    3390    ..\ZOSV1R6\user04.cckd
0AD1    3390    ..\ZOSV1R6\Z6TL01.CCKD
0AD2    3390    ..\ZOSV1R6\Z6TL02.CCKD
0AD3    3390    ..\ZOSV1R6\Z6TL03.CCKD
0AD4    3390    ..\ZOSV1R6\Z6TL04.CCKD

# CTC Adapters
0E20-0E21    CTCI    192.168.1.110 192.168.1.100
 

好了,现在就可以开机了。


--------------------------------------------------------------------------------

首先,找到D:\ADCDV1R6\HercGUI-1.11.1\HercGUI.exe (64bit系统中是运行HercGUI64.exe),双击运行后,出现下面的窗口,这就是Hercules GUI了

 

然后点击菜单栏中的File->Preference,对照本文开始所建的几个文件夹如下,然后确定

 

然后点击菜单栏中的Command->Power On,选择Config文件夹下的配置文件,打开

 

出现以下对话框,若不修改CPU数和内存大小,则无需设置,直接确定

 

然后,在下图对话框中,点选YES,然后OK

 

确定后,Hercules即开始运行,此时防火墙可能会弹出是否允许Hercules访问网络的报警,全部允许即可。

 

左侧框中会列出所有的卷,然后看右侧最下面的message box,如果没有异常,则Hercules mount所有卷成功。
此时,还没有3270 session连接到mainframe,所以左侧的Display Terminals处只显示了终端号,未显示终端IP。

现在,打开PCOM,开始->IBM Personal Communications->Start or Configure session

 

点New Session。

 

点Link Parameters

 

如上图,填入IP和Port,并且可以勾选Auto-Reconnect。点OK,回到Customize Communication对话框

 

点Session Parameter

 

然后一路OK,回到PCOM的Session窗口

 

好了,第一个PCOM session已经连上了,看看Device Number,是0700,那么这个session就是控制台了,只要Hercules还在运行,那么这个session就不能关掉,一定要记住这一点。
在这里,可以用Ctrl+Home来定位光标,也可以通过File->Run the same来运行同样的一个session。
此时,最好通过File->Save,来保存一个WS文件,下次直接双击这个WS文件就可以开启session了。

到这里,z/OS还没有开始启动,我们回到Hercules GUI窗口,点菜单栏里的Command->IPL/Load

 

填入上面两项,然后点OK,这是z/OS就开始启动过程了

 
10.启动错误


走到这里不动了就回车(MF里面的enter是ctrl)

按回车时会提示如何输入

按照提示输入r 00,i

输入s jes2,,,parm='FORMAT,NOREQ'
注:第一次IPL需要JES2 COLD START

这个要登陆后解决。将HFS开头的data set全部catalog

用d a,l来查看启动起来的服务

11.登陆。
一般运行至少有2个terminal, 通常其中一个为3270 Console(控制大机用的),其它为TSO Terminal。

去掉登陆的打印提示:
[LT]
IgnoreWCCStartPrint=Y
UndefinedCode=Y
UndefinedDBCSChar=Y

PS:在PCOMM中,选择File -> Save As... 你就可以看到你的配置文件(.ws)保存的地方


默认用户      密码   权限
ADCDMST          ADCDMST   (RACF special authority)  
IBMUSER          IBMUSER   (RACF special authority)
SYSADM           SYSADM    (DB2 and RACF special auth)  
SYSOPR           SYSOPR    (DB2 and RACF special auth)  
ADCDA - ADCDZ   TEST
OPEN1 - OPEN3   SYS1

看看Hercules GUI窗口右上角的MIPS参数,这个参数越大,说明你的z/OS运行得越顺畅。如果MIPS很小,可以试试改变配置文件里MAINSIZE的值来调整一下,记住MAINSIZE千万不能大于物理内存,否则,MIPS将会只有零点几。

好了,回到PCOM的控制台session。

 

这里,绿色信息代表正常,红色的表示有错误或者警告,白色的信息如果有一个编号(如上图的*02),则需要操作员做应答,这里需要应答的信息是r 02,y
输入后按键盘右边的CTRL键(IBM PCOM默认的确认键是键盘右边的CTRL)

通过PCOM打开一个相同的session。

 

 
出现以上画面时,输入logon ibmuser,以IBMUSER用户的身份登录,出现打印信息对话框时,直接cancel就好了。

 

输入IBMUSER用户的密码,然后确认

 

恭喜,成功登陆。到此,启动过程完毕。


--------------------------------------------------------------------------------

下一个问题就是如何关机了。
回到PCOM控制台session

 

在下面命令行处,输入S Shutdown,按右Ctrl,则z/OS关机过程开始

 

上图所示,出现的*05白色信息表示在关机开始时,还有用户登录到了z/OS,如果不打算等用户退出就关机,在下方输入命令r 05,fstop

 

几分钟后,出现如上图所示的SHUTDOWN - ENDED信息后,表示z/OS关机过程基本上结束,在下方命令行输入quiesce,确认后回到HercGUI窗口

 

此时,上方的那个指针不在偏转,MIPS和INSTCOUNT数值也不便动了,说明所有CPU都已经停下来了。

点菜单Command->Power Off,关闭Hercules电源

 

在信息框中出现Hercules terminated之后,可关掉该窗口。

注意:
1. 在启动hercules时,系统可能出现各种需要应答的信息,具体情况要具体分析,不同信息的处理方式是不同的,如果不知道怎么办,把控制台上的信息copy到google里搜一下看看。
2. 为了防止在PCOM登陆z/OS时出现打印对话框,可以用记事本打开保存的PCOM session的WS文件,在文件最后加入如下内容后,保存退出。
[LT]
IgnoreWCCStartPrint=Y
UndefinedCode=Y
UndefinedDBCSChar=Y
3. 关于IPL参数LOADPARM
在启动z/OS的时候,本文所用的启动参数是0A8032M1,这里的32代表冷启动,启动了TSO,CICS,DB2,WAS,JES2。也可以将这里的32换成以下参数来根据需要启动不同的组件
LOADPARM  list(z/OS 1.6)
CS    CLPA and cold start of JES2. Base z/OS system functions i.e. no CICS, DB2, IMS, WAS, etc.
00    Warm start of JES2. Base z/OS system functions i.e. no CICS, DB2, IMS, WAS, etc.
WS    Warm start of JES2. Base z/OS system functions i.e. no CICS, DB2, IMS, WAS, etc.
DC    CLPA, brings in CICS LPA modules, cold start of JES2, starts up DB2 and CICS.
DB    Warmstart of JES2 and starts the DB2 and CICS.
DI    CLPA and cold start of JES2 and loads the IMS Libraries. IMS must be manually started.
CC    CLPA and cold start of JES2, loads the CICS Libraries, starts up CICS, no DB2.
CW    Warm start of JES2, and starts up CICS.
7C    CLPA, cold start of JES2, starts up DB2 V7, no CICS.
7W    Warm start of JES2, starts up DB2 V7, no CICS.
8C    CLPA, cold start of JES2, starts up DB2 v8, no CICS.
8W    Warm start of JES2, starts up DB2 v8, no CICS.
IC    CLPA and cold start of JES2 and load the IMS Libraries, start IMS, no DB2 or CICS.
IW    Warm start of JES2 start IMS, no DB2 or CICS.
AC    CLPA and cold start of JES2 load IMS and CICS libraries, start IMS, DB/2, and CICS.
AW    Warmstart of JES2. start IMS, DB/2, and CICS.
BC    CLPA and cold start of JES2, load WAS libraries, WAS is manually started
BW    Warmstart of JES2. WAS is manually started.
99    Points to IODF99 for IPL on MP3000. Reply 00,SYSP=xx were xx is any of the above options i.e. for cics only
4.ADCD z/OS 1.6卷文件的内容
1, VOLUME的内容(z/OS 1.6)
VOLUME  UCB   Contents
Z6RES1  A80 - Res Volume 1 - Required for IPL
Z6RES2  A81 - Res Volume 2 - Required for IPL
Z6SYS1  A82 - IPLPARM, JES2 Spool, Public Work Volume, Mastercat required for IPL
Z6USS1  A83 - USS Root and accompanying HFS files - required for IPL
Z6USS2  A84 - Supplemental HFS files - required for IPL
Z6DIS1  A85 - Distribution Lib volume 1
Z6DIS2  A86 - Distribution Lib volume 2
Z6DIS3  A87 - Distribution Lib volume 3
Z6DIS4  A88 - Distribution Lib volume 4
Z6DIS5  A89 - Distribution Lib volume 5
Z6WAS1  A8A - Websphere Application Server Distribution Libs
Z6WAS2  A8B - Websphere Application Server Target Libs
Z6DB81  A8C - DB2 V8.1 Target Libs
Z6DB82  A8D - DB2 V8.1 Distribution Libs
Z6CIC1  A8E - CICS TS 2.3 Target and Dlibs
Z6IMS1  A8F - IMS 8.1 Target and Dlibs
SARES1  A90 - Stand Alone IPL volume


解决登陆时出现的错误。
系统在IPL的时候问IEA101A时,需要确认member LOAD99里面是否有SYSPARM和IEASYM这两个参数






 

遇到HFS错误时,把HFS开头的data set全部catalog



已经被catalog了

posted @ 2011-05-21 20:02 Eric_jiang 阅读(10559) | 评论 (19)编辑 收藏

法语、德语、意大利语、西班牙语、葡萄牙语、俄语、日语、韩语、阿拉伯语是除英语之外在中国最受众的九大外语了,它们有一个共同的名字——“小语种。

  小语种在就业形势上的相近源于在就业渠道上的类似,虽然在语言结构上有诸多的不同,但在就业渠道上有很强的共性,主要集中在以下几个领域:

  1.政府公务员;

  2.高校教师;

  3.新闻传媒机构;

  4.外企和中外合资企业;

  5.各省市外办;

  6.外贸公司;

  7.旅游公司;

  8.留学培训机构;

  9.文化传播公司;

  10.自由职业者。小语种的就业形式的差异,主要受以下三个方面的影响:

  1.学习这种语言的人数;

  2.讲这种语言的国家数量及经济情况;

  3.中国与这些国家的关系,包括地缘上的和政治经济方面的。

  1.葡萄牙语(开办院校6所,讲葡语的国家和地区7个左右):

  葡语国家大部分集中在非洲,有5个。在语言方面,葡语跟西语情同手足,在就业方面,葡语倒是跟法语亲如兄弟,像法语一样,非洲也是葡语目前就业的一个主要战场。提到葡语非洲,就必须提到一个国家——安哥拉安哥拉对中国的贡献不只是奥运会上的篮球,他还是中国在非洲的第一大贸易合作伙伴,更夸张的是08年初小安居然取代小阿成为中国最大石油供应国,中安之间有多强的经济互补也就不言而喻了。中国同另外一个非洲葡语国家莫桑比克也保持着很好的经济关系,目前是他的第六大投资国。从我们网站上企业自己登记的招聘信息来看,学葡语的去非洲,起薪会达到2000-2500美金,这样的工资水平,是其他小语种望尘莫及的。

  不过非洲对很多人而言,都是只想远观,不愿近临,如果不想去的话也无妨,巴西,这个讲葡语的南美足球王国,同样是吸收葡语人才的重要阵地。巴西、中国、俄罗斯、印度目前被称为“金砖四国”,经济发展潜力最为巨大。无论是现在还是在将来,目前的葡语毕业生数量,都远远满足不了需求。

  葡语的源产地葡萄牙提供不了多少就业机会,但需求也总还是有的,再者他好歹占着欧洲这么一块宝地,让葡语的在三大洲都有落脚点和根据地。小语种的就业形式的差异,主要受以下三个方面的影响:

  1.学习这种语言的人数;

  2.讲这种语言的国家数量及经济情况;

  3.中国与这些国家的关系,包括地缘上的和政治经济方面的。

  1.葡萄牙语(开办院校6所,讲葡语的国家和地区7个左右):

  葡语国家大部分集中在非洲,有5个。在语言方面,葡语跟西语情同手足,在就业方面,葡语倒是跟法语亲如兄弟,像法语一样,非洲也是葡语目前就业的一个主要战场。提到葡语非洲,就必须提到一个国家——安哥拉安哥拉对中国的贡献不只是奥运会上的篮球,他还是中国在非洲的第一大贸易合作伙伴,更夸张的是08年初小安居然取代小阿成为中国最大石油供应国,中安之间有多强的经济互补也就不言而喻了。中国同另外一个非洲葡语国家莫桑比克也保持着很好的经济关系,目前是他的第六大投资国。从我们网站上企业自己登记的招聘信息来看,学葡语的去非洲,起薪会达到2000-2500美金,这样的工资水平,是其他小语种望尘莫及的。

  不过非洲对很多人而言,都是只想远观,不愿近临,如果不想去的话也无妨,巴西,这个讲葡语的南美足球王国,同样是吸收葡语人才的重要阵地。巴西、中国、俄罗斯、印度目前被称为“金砖四国”,经济发展潜力最为巨大。无论是现在还是在将来,目前的葡语毕业生数量,都远远满足不了需求。

posted @ 2011-05-21 19:58 Eric_jiang 阅读(366) | 评论 (0)编辑 收藏

 

本文假设读者对WEB和portal及portlet有基本的了解。

 

一、开发环境

RAD 7.5 + Websphere Portal Server 7.0

二、开发目的

开发一个地址薄,用来记录姓名和地址。

三、开发过程

1.         创建一个portlet项目

选择File->New->Portlet Project,弹出如下所示的对话框:


  输入或选择以上信息后,单击Next按钮,弹出如下所示的界面:

 

为简便起见,只选择了view和edit两个模式。继续点击Next按钮弹出如下界面:

 

 取消第一个复选按钮的默认选择,点击Finish按钮完成项目的创建。

项目创建完成后,项目中会生成如下4个关键文件:

AddressBookPortlet.java是portlet的处理文件;

AddressBookPortletEdit.jsp对应portlet的编辑状态;

AddressBookPortletView.jsp对应portlet的视图状态;

portlet.xml是portlet的部署描述文件。


2.         增加姓名地址信息

修改编辑模式所对应的AddressBookPortletEdit.jsp文件,提供用户输入界面。

 
修改AddressBookPortlet.java中的processAction方法

 编辑界面提交的姓名和地址作为名称/值对的形式保存在PortletPreferences对象中。

response.setPortletMode(PortletMode.VIEW)表示处理完edit模式后迁移到view模式以显示最新的地址信息。

3.         显示地址薄中的名称地址信息

由于名称/地址信息已经保存在PortletPreferences对象中,因此只需要从该对象中取出所有的名称/地址信息并显示出来即可。修改AddressBookPortletView.jsp:


4.         部署

启动PortServer,可能需要较长的时间。

选中服务器WebSphere Portal V7.0 at localhost,弹出如下所示的菜单:

 
点击Add and Remove Projects…,弹出如下所示的界面:

 

将AddressBookPortletEAR添加到右边列表框中。点击Finish按钮完成部署。

5.  运行

从浏览器地址栏输入URL:http://localhost:10039/wps/myportal,输入用户名和密码后进入如下所示的界面:

 
点击该portlet所在页面右上角处(用蓝色椭圆标出了),弹出菜单,如下图所示:

 

点击个性化菜单条,弹出增加地址界面:

 

输入姓名和地址,点击保存按钮,则跳转到显示页面:

 

至此,一个具有地址簿功能的portlet开发部署完成,在以后的portal页面中就可以使用该portlet了。

 http://www.puyufanyi.com/

 

posted @ 2011-05-13 18:14 Eric_jiang 阅读(2205) | 评论 (2)编辑 收藏

/* 输出

Original:       黄   彪彪

to unicode:     \u9EC4\ \t\u5F6A\u5F6A

from unicode:   黄   彪彪

使用命令转换: native2ascii -encoding utf-8 a.txt a.txt

Java的properties属性文件会把字符先转换成unicode的形式存储.

*/

 


import java.io.UnsupportedEncodingException;

 


public class UnicodeConverter {

    public static void main(String[] args) throws UnsupportedEncodingException {

        String s = "黄 \t彪\u5F6A";

        System.out.println("Original:\t\t" + s);

 


        s = toEncodedUnicode(s, true);

        System.out.println("to unicode:\t\t" + s);

 


        s = fromEncodedUnicode(s.toCharArray(), 0, s.length());

        System.out.println("from unicode:\t" + s);

    }

 


    private static final char[] hexDigit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A',

            'B', 'C', 'D', 'E', 'F' };

 


    private static char toHex(int nibble) {

        return hexDigit[(nibble & 0xF)];

    }

 


    /**

     * 将字符串编码成 Unicode 形式的字符串. 如 "黄" to "\u9EC4"

     * Converts unicodes to encoded \\uxxxx and escapes

     * special characters with a preceding slash

     *

     * @param theString

     *        待转换成Unicode编码的字符串。

     * @param escapeSpace

     *        是否忽略空格,为true时在空格后面是否加个反斜杠。

     * @return 返回转换后Unicode编码的字符串。

     */

    public static String toEncodedUnicode(String theString, boolean escapeSpace) {

        int len = theString.length();

        int bufLen = len * 2;

        if (bufLen < 0) {

            bufLen = Integer.MAX_VALUE;

        }

        StringBuffer outBuffer = new StringBuffer(bufLen);

 


        for (int x = 0; x < len; x++) {

            char aChar = theString.charAt(x);

            // Handle common case first, selecting largest block that

            // avoids the specials below

            if ((aChar > 61) && (aChar < 127)) {

                if (aChar == '\\') {

                    outBuffer.append('\\');

                    outBuffer.append('\\');

                    continue;

                }

                outBuffer.append(aChar);

                continue;

            }

           

            switch (aChar) {

            case ' ':

                if (x == 0 || escapeSpace) outBuffer.append('\\');

                outBuffer.append(' ');

                break;

            case '\t':

                outBuffer.append('\\');

                outBuffer.append('t');

                break;

            case '\n':

                outBuffer.append('\\');

                outBuffer.append('n');

                break;

            case '\r':

                outBuffer.append('\\');

                outBuffer.append('r');

                break;

            case '\f':

                outBuffer.append('\\');

                outBuffer.append('f');

                break;

            case '=': // Fall through

            case ':': // Fall through

            case '#': // Fall through

            case '!':

                outBuffer.append('\\');

                outBuffer.append(aChar);

                break;

            default:

                if ((aChar < 0x0020) || (aChar > 0x007e)) {

                    // 每个unicode有16位,每四位对应的16进制从高位保存到低位

                    outBuffer.append('\\');

                    outBuffer.append('u');

                    outBuffer.append(toHex((aChar >> 12) & 0xF));

                    outBuffer.append(toHex((aChar >> 8) & 0xF));

                    outBuffer.append(toHex((aChar >> 4) & 0xF));

                    outBuffer.append(toHex(aChar & 0xF));

                } else {

                    outBuffer.append(aChar);

                }

            }

        }

        return outBuffer.toString();

    }

 


    /**

     * 从 Unicode 形式的字符串转换成对应的编码的特殊字符串。 如 "\u9EC4" to "黄".

     * Converts encoded \\uxxxx to unicode chars

     * and changes special saved chars to their original forms

     *

     * @param in

     *        Unicode编码的字符数组。

     * @param off

     *        转换的起始偏移量。

     * @param len

     *        转换的字符长度。

     * @param convtBuf

     *        转换的缓存字符数组。

     * @return 完成转换,返回编码前的特殊字符串。

     */

    public static String fromEncodedUnicode(char[] in, int off, int len) {

        char aChar;

        char[] out = new char[len]; // 只短不长

        int outLen = 0;

        int end = off + len;

 


        while (off < end) {

            aChar = in[off++];

            if (aChar == '\\') {

                aChar = in[off++];

                if (aChar == 'u') {

                    // Read the xxxx

                    int value = 0;

                    for (int i = 0; i < 4; i++) {

                        aChar = in[off++];

                        switch (aChar) {

                        case '0':

                        case '1':

                        case '2':

                        case '3':

                        case '4':

                        case '5':

                        case '6':

                        case '7':

                        case '8':

                        case '9':

                            value = (value << 4) + aChar - '0';

                            break;

                        case 'a':

                        case 'b':

                        case 'c':

                        case 'd':

                        case 'e':

                        case 'f':

                            value = (value << 4) + 10 + aChar - 'a';

                            break;

                        case 'A':

                        case 'B':

                        case 'C':

                        case 'D':

                        case 'E':

                        case 'F':

                            value = (value << 4) + 10 + aChar - 'A';

                            break;

                        default:

                            throw new IllegalArgumentException("Malformed \\uxxxx encoding.");

                        }

                    }

                    out[outLen++] = (char) value;

                } else {

                    if (aChar == 't') {

                        aChar = '\t';

                    } else if (aChar == 'r') {

                        aChar = '\r';

                    } else if (aChar == 'n') {

                        aChar = '\n';

                    } else if (aChar == 'f') {

                        aChar = '\f';

                    }

                    out[outLen++] = aChar;

                }

            } else {

                out[outLen++] = (char) aChar;

            }

        }

        return new String(out, 0, outLen);

    }

}

 

posted @ 2011-05-11 10:15 Eric_jiang 阅读(14464) | 评论 (1)编辑 收藏

public class Conversion {

 /**
  * 将字符串转成unicode
  *
  * @param str
  *            待转字符串
  * @return unicode字符串
  */
 public String convert(String str) {
  str = (str == null ? "" : str);
  String tmp;
  StringBuffer sb = new StringBuffer(1000);
  char c;
  int i, j;
  sb.setLength(0);
  for (i = 0; i < str.length(); i++) {
   c = str.charAt(i);
   sb.append("\\u");
   j = (c >>> 8); // 取出高8位
   tmp = Integer.toHexString(j);
   if (tmp.length() == 1)
    sb.append("0");
   sb.append(tmp);
   j = (c & 0xFF); // 取出低8位
   tmp = Integer.toHexString(j);
   if (tmp.length() == 1)
    sb.append("0");
   sb.append(tmp);

  }
  return (new String(sb));
 }

 /**
  * 将unicode 字符串
  *
  * @param str
  *            待转字符串
  * @return 普通字符串
  */
 public String revert(String str) {
  str = (str == null ? "" : str);
  if (str.indexOf("\\u") == -1)// 如果不是unicode码则原样返回
   return str;

  StringBuffer sb = new StringBuffer(1000);

  for (int i = 0; i < str.length() - 6;) {
   String strTemp = str.substring(i, i + 6);
   String value = strTemp.substring(2);
   int c = 0;
   for (int j = 0; j < value.length(); j++) {
    char tempChar = value.charAt(j);
    int t = 0;
    switch (tempChar) {
    case 'a':
     t = 10;
     break;
    case 'b':
     t = 11;
     break;
    case 'c':
     t = 12;
     break;
    case 'd':
     t = 13;
     break;
    case 'e':
     t = 14;
     break;
    case 'f':
     t = 15;
     break;
    default:
     t = tempChar - 48;
     break;
    }

    c += t * ((int) Math.pow(16, (value.length() - j - 1)));
   }
   sb.append((char) c);
   i = i + 6;
  }
  return sb.toString();
 }

 public static void main(String args[]) {
  // unicode转中文
  String str = "\u767b\u5f55\u793e\u533a";
  Conversion con = new Conversion();
 
  System.out.println(con.revert(str));

 }

}

posted @ 2011-05-10 19:10 Eric_jiang 阅读(1761) | 评论 (0)编辑 收藏

1 编码
在计算机中,各种信息都是以二进制编码的形式存在的,也就是说,不管是文字、图形、声音、动画,还是电影等各种信息,在计算机中都是以0和1组成的二进制代码表示的。为了区分这些信息,人们就为计算机设计了一套识别准则,这也就是计算机编码。例如:英文字母与汉字的的区别,就是英文字母用的是单字节的ASCII码,汉字采用的是双字节的汉字内码。
1.1 基本概念
* 字符:字符是抽象的最小文本单位。它没有固定的形状(可能是一个字形),而且没有值。“A”是一个字符,“”(德国、法国和许多其他欧洲国家通用货币的标志)也是一个字符。
* 字符集:字符集是字符的集合。例如,汉字字符是中国人最先发明的字符,在中文、日文、韩文和越南文的书写中使用。
* 编码字符集:编码字符集是一个字符集,它为每一个字符分配一个唯一数字。Unicode 标准的核心是一个编码字符集,字母“A”的编码为 004116 和字符“”的编码为 20AC16。Unicode 标准始终使用十六进制数字,而且在书写时在前面加上前缀“U+”,所以“A”的编码书写为“U+0041”。
* 代码点:代码点是指可用于编码字符集的数字。编码字符集定义一个有效的代码点范围,但是并不一定将字符分配给所有这些代码点。有效的Unicode代码点范围是 U+0000 至U+10FFFF。Unicode4.0将字符分配给一百多万个代码点中的96,382代码点。
* 增补字符:增补字符是代码点在 U+10000 至 U+10FFFF 范围之间的字符,也就是那些使用原始的Unicode的16 位设计无法表示的字符。从U+0000至 U+FFFF之间的字符集有时候被称为基本多语言面 (BMP)。因此,每一个Unicode 字符要么属于BMP,要么属于增补字符。
* 字符编码方案:字符编码方案是从一个或多个编码字符集到一个或多个固定宽度代码单元序列的映射。最常用的代码单元是字节,但是 16 位或 32 位整数也可用于内部处理。UTF-32、UTF-16 和 UTF-8 是 Unicode 标准的编码字符集的字符编码方案。
* UTF-32:即将每一个 Unicode 代码点表示为相同值的 32 位整数。很明显,它是内部处理最方便的表达方式,但是,如果作为一般字符串表达方式,则要消耗更多的内存。
* UTF-16:使用一个或两个未分配的 16 位代码单元的序列对Unicode代码点进行编码。值U+0000至U+FFFF编码为一个相同值的16位单元。增补字符编码为两个代码单元,第一个单元来自于高代理范围(U+D800 至 U+DBFF),第二个单元来自于低代理范围(U+DC00至U+DFFF)。这在概念上可能看起来类似于多字节编码,但是其中有一个重要区别:值 U+D800至U+DFFF 保留用于UTF-16;没有这些值分配字符作为代码点。这意味着,对于一个字符串中的每个单独的代码单元,软件可以识别是否该代码单元表示某个单单元字符,或者是否该代码单元是某个双单元字符的第一个或第二单元。这相当于某些传统的多字节字符编码来说是一个显著的改进,在传统的多字节字符编码中,字节值 0x41既可能表示字母“A”,也可能是一个双字节字符的第二个字节。
* UTF-8:使用一至四个字节的序列对编码Unicode代码点进行编码。U+0000至U+007F 使用一个字节编码,U+0080至U+07FF使用两个字节,U+0800至U+FFFF 使用三个字节,而U+10000至U+10FFFF使用四个字节。UTF-8设计原理为:字节值0x00至0x7F 始终表示代码点U+0000至U+007F(Basic Latin 字符子集,它对应 ASCII 字符集)。这些字节值永远不会表示其他代码点,这一特性使UTF-8可以很方便地在软件中将特殊的含义赋予某些ASCII字符。
(表1-1 编码的一些基本概念)
1.2 字符编码
字符编码即英文编码,包括字母、数字、标点、运算符等。
    字符的编码采用国际通用的ASCII码(American Standard Code for Information Interchange,美国信息交换标准代码),每个ASCII码以1个字节(Byte)储存,从0到数字127代表不同的常用符号,例如大写A的 ASCII码是65,小写a则是97。由于ASCII码只用了字节的七个位,最高位并不使用,所以后来又将最高的一个位也编入这套编码码中,成为八个位的延伸ASCII(ExtendedASCII)码,这套内码加上了许多外文和表格等特殊符号,成为目前常用的编码。基本的ASCII字符集共有128个字符,其中有96个可打印字符,包括常用的字母、数字、标点符号等,另外还有32个控制字符。标准ASCII码使用7个二进位对字符进行编码,对应的ISO 标准为ISO646标准。
    字母和数字的ASCII码的记忆是非常简单的。我们只要记住了一个字母或数字的ASCII码(例如记住A为65,0的ASCII码为48),知道相应的大小写字母之间差32,就可以推算出其余字母、数字的ASCII码。
    虽然标准ASCII码是7位编码,但由于计算机基本处理单位为字节(1byte = 8bit),所以一般仍以一个字节来存放一个ASCII字符。每一个字节中多余出来的一位(最高位)在计算机内部通常保持为0(在数据传输时可用作奇偶校验位)。由于标准ASCII字符集字符数目有限,在实际应用中往往无法满足要求。为此,国际标准化组织又制定了ISO2022标准,它规定了在保持与 ISO646兼容的前提下将ASCII字符集扩充为8位代码的统一方法。ISO陆续制定了一批适用于不同地区的扩充ASCII字符集,每种扩充ASCII 字符集分别可以扩充128个字符,这些扩充字符的编码均为高位为1的8位代码(即十进制数128~255),称为扩展ASCII码。
1.3 汉字编码
1.3.1 汉字内码
    汉字信息在计算机内部也是以二进制方式存放。由于汉字数量多,用一个字节的128种状态不能全部表示出来,因此在1980年我国颁布的《信息交换用汉字编码字符集——基本集》,即国家标准GB2312-80方案中规定用两个字节的十六位二进制表示一个汉字,每个字节都只使用低7位(与ASCII码相同),即有128×128=16384种状态。由于ASCII码的34个控制代码在汉字系统中也要使用,为不致发生冲突,不能作为汉字编码,128除去34只剩 94种,所以汉字编码表的大小是94×94=8836,用以表示国标码规定的7445个汉字和图形符号。
    每个汉字或图形符号分别用两位的十进制区码(行码)和两位的十进制位码(列码)表示,不足的地方补0,组合起来就是区位码。把区位码按一定的规则转换成的二进制代码叫做信息交换码(简称国标码)。国标码共有汉字6763个(一级汉字,是最常用的汉字,按汉语拼音字母顺序排列,共3755个;二级汉字,属于次常用汉字,按偏旁部首的笔划顺序排列,共3008个),数字、字母、符号等682个,共7445个。
    由于国标码不能直接存储在计算机内,为方便计算机内部处理和存储汉字,又区别于ASCII码,将国标码中的每个字节在最高位改设为1,这样就形成了在计算机内部用来进行汉字的存储、运算的编码叫机内码(或汉字内码,或内码)。内码既与国标码有简单的对应关系,易于转换,又与ASCII码有明显的区别,且有统一的标准(内码是惟一的)。
1.3.2 汉字外码
    无论是区位码或国标码都不利于输入汉字,为方便汉字的输入而制定的汉字编码,称为汉字输入码,即汉字外码。不同的输入方法,形成了不同的汉字外码。常见的输入法有以下几类:
* 按汉字的排列顺序形成的编码(流水码):如区位码;
* 按汉字的读音形成的编码(音码):如全拼、简拼、双拼等;
* 按汉字的字形形成的编码(形码):如五笔字型、郑码等;
* 按汉字的音、形结合形成的编码(音形码):如自然码、智能ABC。
* 输入码在计算机中必须转换成机内码,才能进行存储和处理。
(表1-2 常见的输入法)
1.3.3 汉字字形码
    为了将汉字在显示器或打印机上输出,把汉字按图形符号设计成点阵图,就得到了相应的点阵代码(字形码)。
    全部汉字字码的集合叫汉字字库。汉字库可分为软字库和硬字库。软字库以文件的形式存放在硬盘上,现多用这种方式,硬字库则将字库固化在一个单独的存储芯片中,再和其它必要的器件组成接口卡,插接在计算机上,通常称为汉卡。
    用于显示的字库叫显示字库。显示一个汉字一般采用16×16点阵或24×24点阵或48×48点阵。已知汉字点阵的大小,可以计算出存储一个汉字所需占用的字节空间。例:用16×16点阵表示一个汉字,就是将每个汉字用16行,每行16个点表示,一个点需要1位二进制代码,16个点需用16位二进制代码(即2个字节),共16行,所以需要16行×2字节/行=32字节,即16×16点阵表示一个汉字,字形码需用32字节。即:字节数 = 点阵行数 * 点阵列数 / 8。
    用于打印的字库叫打印字库,其中的汉字比显示字库多,而且工作时也不像显示字库需调入内存。可以这样理解,为在计算机内表示汉字而统一的编码方式形成汉字编码叫内码(如国标码),内码是惟一的。为方便汉字输入而形成的汉字编码为输入码,属于汉字的外码,输入码因编码方式不同而不同,是多种多样的。为显示和打印输出汉字而形成的汉字编码为字形码,计算机通过汉字内码在字模库中找出汉字的字形码,实现其转换。例如:
1:已知汉字"春"的国标码为343AH,求其机内码?
答:机内码 = 国标码 + 8080H = 343AH + 8080H = B4BAH

2:用24×24点阵来表示一个汉字(一点为一个二进制位),则2000个汉字需要多少KB容量?
答: 容量 = (24 * 24/8)* 2000 / 1024 = 140.7KB ≈ 141KB
(表1-3 汉字内码与字形码的转换实例)
2 UNICODE编码
Unicode 是基于通用字符集(Universal Character Set)的标准来发展,并且同时也以书本的形式(The Unicode Standard,目前第五版由Addison-Wesley Professional出版,ISBN-10: 0321480910)对外发表。Unicode包含了超过十万个字符(在2005年,Unicode的第十万个字符被采纳且认可成为标准之一)、一组可用以作为视觉参考的代码图表、一套编码方法与一组标准字符编码、一套包含了上标字、下标字等字符特性的列举等。
在计算机科学领域中,Unicode(统一码、万国码、单一码、标准万国码)是业界的一种标准,它可以使电脑得以呈现世界上数十种文字的系统。Unicode组织(The Unicode Consortium)是由一个非营利性的机构所运作,并主导Unicode的后续发展,其目标在于:将既有的字符编码方案,以Unicode编码方案来加以取代,特别是既有的方案在多语环境下,皆仅有有限的空间以及不相容的问题。
Unicode在字符集认可的成功,使其得以在电脑软件的国际化与本地化领域中,广泛且具优势的被采用。这标准已在近年来的多种新科技当中被加以采用,包含了可扩展置标语言(XML)、Java编程语言、以及最新的操作系统中。
2.2 UNICODE的起源与发展
Unicode是由于传统的字符编码方式的局限性而产生的,例如 ISO 8859所定义的字符虽然在不同的国家中广泛地使用,可是在不同国家间却经常出现不相容的情况。很多传统的编码方式都具有一个共通的问题,即其容许电脑进行双语环境式的处理(通常使用拉丁字母以及其本地语言),但却无法同时支援多语言环境式的处理(指可同时处理混合多种语言的情况)。
Unicode试图将字位(字素,graphemes)与类字位字符加以认定与编码,而非以不同的字形(glyphs)来加以区分。然而在汉字的个案来看,这样方式有时会引起一字多形的认定争议(详见中日韩统一表意文字主题)。
在文字处理方面,Unicode的功用是为每一个字符提供一个唯一的代码(即一组数字),而不是一种字形。换句话说,Unicode是将字符以一种抽象的方式来呈现,而将视觉上的演绎工作(例如字体大小、外观形状、字体形态、文体等)留给其他软件来处理,例如网页浏览器或是文字处理器。
    为了使Unicode与已存在和广泛使用的旧有编码互相兼容,尤其是差不多所有电脑系统都支援的基本拉丁字母部分,所以Unicode的首256字符仍旧保留给ISO 8859-1所定义的字符,使既有的西欧语系文字的转换不需特别考量;另方面因相同的原因,Unicode 把大量相同的字符重复编到不同的字符码中去,使得旧有纷杂的编码方式得以和 Unicode 编码间互相直接转换,而不会遗失任何资讯。举例来说,全角格式区段包含了主要的拉丁字母的全角格式,在中文、日文、以及韩文字形当中,这些字符以全角的方式来呈现,而不以常见的半角形式显示,这对竖排文字和等宽排列文字有重要作用。
    Unicode组织位于美国加州,组织允许任何愿意支付会员费用的公司或是个人加入,其成员包含了主要的电脑软硬件厂商,例如奥多比系统(Adobe Systems)、苹果公司(Apple)、惠普(HP)、IBM、微软(Microsoft)、全录(Xerox)等。Unicode 组织在 1991 年首次发布了 The Unicode Standard(ISBN 0-321-18578-1)。Unicode的开发结合了国际标准化组织(International Organization for Standardization,简称 ISO)所制定的ISO/IEC 10646,即通用字符集(Universal Character Set,简称 UCS)。Unicode 与 ISO/IEC 10646 在编码的运作原理相同,但 The Unicode Standard 包含了更详尽的实现资讯、涵盖了更细节的主题,诸如字符编码(bitwise encoding)、校对以及呈现等。 The Unicode Standard 也列举了诸多的字符特性,包含了那些必须支援双方向呈现的文字(由左至右或由右至左的文字呈现方向,例如阿拉伯文是由右至左)。Unicode与 ISO/IEC 10646两个标准在术语上的使用有些微的不同。
Unicode截至目前为止历次的版次与发布时间如下:
Unicode 1.0:1991年10月;
Unicode 1.0.1:1992年6月;
Unicode 1.1:1993年6月;
Unicode 2.0:1997年7月;
Unicode 2.1:1998年5月;
Unicode 2.1.2:1998年5月;
Unicode 3.0:1999年9月;涵盖了来自ISO 10646-1的十六位元通用字符集(UCS)基本多文种平面(Basic Multilingual Plane);
Unicode 3.1:2001年3月;新增从ISO 10646-2定义的辅助平面(Supplementary Planes);
Unicode 3.2:2002年3月;
Unicode 4.0:2003年4月;
Unicode 4.0.1:2004年3月;
Unicode 4.1:2005年3月;
Unicode 5.0:2006年7月;
Unicode 5.1:2008年4月。
(表2-1 unicode编码的版次与发布时间)
2.3 Unicode 的编码和实现
2.3.1 编码方式
Unicode 的编码方式与 ISO 10646 的通用字符集(Universal Character Set,UCS)概念相对应,目前实际应用的 Unicode 版本对应于 UCS-2,使用16位的编码空间。也就是每个字符占用2个字节。这样理论上一共最多可以表示 216 即 65536 个字符。基本满足各种语言的使用。实际上目前版本的 Unicode 尚未填充满这16位编码,保留了大量空间作为特殊使用或将来扩展。
上述16位Unicode字符构成基本多文种平面(Basic Multilingual Plane,简称BMP)。最新(但未实际广泛使用)的Unicode 版本定义了16个辅助平面,两者合起来至少需要占据21位的编码空间,比3字节略少。但事实上辅助平面字符仍然占用4字节编码空间,与UCS-4保持一致。未来版本会扩充到 ISO 10646-1 实现级别3,即涵盖UCS-4的所有字符。UCS-4 是一个更大的尚未填充完全的31位字符集,加上恒为0的首位,共需占据32位,即4字节。理论上最多能表示231个字符,完全可以涵盖一切语言所用的符号。
BMP字符的Unicode编码表示为 U+hhhh,其中每个 h 代表一个十六进制数位。与UCS-2编码完全相同。对应的4字节UCS-4编码后两个字节一致,前两个字节的所有位均为0。
2.3.2 实现方式
Unicode的实现方式不同于编码方式。一个字符的Unicode 编码是确定的。但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对Unicode编码的实现方式有所不同。 Unicode的实现方式称为Unicode转换格式(Unicode Translation Format,简称为 UTF)。
       例如,如果一个仅包含基本7位ASCII字符的Unicode文件,如果每个字符都使用2字节的原Unicode 编码传输,其第一字节的8位始终为0。这就造成了比较大的浪费。对于这种情况,可以使用 UTF-8 编码,这是一种变长编码,它将基本7位ASCII字符仍用7位编码表示,占用一个字节(首位补0)。而遇到与其他 Unicode 字符混合的情况,将按一定算法转换,每个字符使用1-3个字节编码,并利用首位为0或1进行识别。这样对以7位ASCII字符为主的西文文档就大大节省了编码长度。类似的,对未来会出现的需要4个字节的辅助平面字符和其他 UCS-4 扩充字符,2字节编码的UTF-16也需要通过一定的算法进行转换。
再如,如果直接使用与Unicode编码一致(仅限于 BMP 字符)的UTF-16编码,由于每个字符占用了两个字节,在Macintosh机和PC机上,对字节顺序的理解是不一致的。这时同一字节流可能会被解释为不同内容,如编码为U+594E的字符“奎”同编码为 U+4E59 的“乙”就可能发生混淆。于是在UTF-16编码实现方式中使用了大尾序(big-endian)、小尾序(little-endian)的概念,以及 BOM(Byte Order Mark)解决方案。
       此外 Unicode 的实现方式还包括UTF-7、Punycode、CESU-8、SCSU、UTF-32等,这些实现方式有些仅在一定的国家和地区使用,有些则属于未来的规划方式。目前通用的实现方式是 UTF-16小尾序(BOM)、UTF-16大尾序(BOM)和 UTF-8。在微软公司Windows XP操作系统附带的记事本中,“另存为”对话框可以选择的四种编码方式除去非 Unicode 编码的 ANSI 外,其余三种“Unicode”、“Unicode big endian”和“UTF-8”即分别对应这三种实现方式。

3 UTF-8
UTF是UCS / Unicode Transformation Format(Unicode转换格式)的缩写,UTF-8(8位元Universal Character Set/Unicode Transformation Format)是一种针对 Unicode 的可变长度字符编码。它可以用来表示 Unicode 标准中的任何字符,且其编码中的第一个字节仍与ASCII相容,这使得原来处理ASCII字符的软件无须或只须做少部份修改,即可继续使用。因此,它逐渐成为电子邮件、网页及其他储存或传送文字的应用中,优先采用的编码。
3.1 UTF-8的历史
1992年初,为建立良好的字节串编码系统(byte-stream encoding)以供多字节字符集(multi-byte character sets)使用,开始了一个正式的研究。ISO/IEC 10646的初稿中有一个非必须的附录,名为UTF。当中包含了一个供32位元的字符使用的字节串编码系统。这个编码方式的性能并不令人满意,但它提出了将0-127的范围保留给ASCII以相容旧系统的概念。
1992年7月,X/Open委员会XoJIG开始寻求一个较佳的编码系统。Unix系统实验室(UNIX System Laboratories, USL)的Dave Prosser为此提出了一个编码系统的建议。它具备可更快速实作的特性,并引入一项新的改进。其中,7位元的ASCII符号只代表原来的意思,所有多字节序列则会包含第8位元的符号,也就是所谓的最高有效位元(Most significant bit)。
1992年8月,这个建议由IBMX/Open的代表流传到一些感兴趣的团体。与此同时,贝尔实验室Plan 9操作系统工作小组的肯·汤普逊对这编码系统作出重大的修改,让编码可以自我同步(self-synchronizing),使得不必从字串的开首读取,也能找出字符间的分界。1992年9月2日,肯·汤普逊和Rob Pike一起在美国新泽西州一架餐车的餐桌垫上描绘出此设计的要点。接下来的日子,Pike及汤普逊将它实现,并将这编码系统完全应用在Plan 9当中,及后他将有关成果回馈X/Open。
1993年1月25-29日的在圣地牙哥举行的USENIX会议首次正式介绍UTF-8。
自1996年起,微软的CAB(MS Cabinet)规格在UTF-8标准正式落实前就明确容许在任何地方使用UTF-8编码系统。但有关的编码器实际上从来没有实作这方面的规格。
3.2 UTF-8的优缺点
3.2.1 UTF-8的特质
UTF-8的设计有以下的多字符组序列的特质:
?单字节字符的最高有效位元永远为0;
?多字节序列中的首个字符组的几个最高有效位元决定了序列的长度。最高有效位为110的,是2字节序列,而1110的是三字节序列,如此类推;
?多字节序列中其余的字节中的首两个最高有效位元为10。
(表3-1 UTF-8的特质)
UTF-8的这些特质,保证了一个字符的字节序列不会包含在另一个字符的字节序列中。这确保了以字节为基础的部份字串比对(sub-string match)方法可以适用于在文字中搜寻字或词。有些比较旧的可变长度8位元编码(如Shift JIS)没有这个特质,故字串比对的算法变得相当复杂。虽然这增加了UTF-8编码的字串的信息冗余,但是利多于弊。另外,资料压缩并非Unicode的目的,所以不可混为一谈。即使在传送过程中有部份字节因错误或干扰而完全遗失,还是有可能在下一个字符的起点重新同步,令受损范围受到限制。
       另一方面,由于其字节序列设计,如果一个疑似为字符串的序列被验证为UTF-8编码,那么我们可以有把握地说它是UTF-8字符串。一段两字节随机序列碰巧为合法的UTF-8而非ASCII 的机率为32分1。对于三字节序列的机率为256分3,对更长的序列的机率就更低了。
3.2.2 UTF-8的优点
UTF-8编码可以通过屏蔽位和移位操作快速读写。字符串比较时 strcmp()和wcscmp()的返回结果相同,因此使排序变得更加容易。字节FF和FE在UTF-8编码中永远不会出现,因此他们可以用来表明 UTF-16或UTF-32文本(见BOM) UTF-8 是字节顺序无关的。它的字节顺序在所有系统中都是一样的,因此它实际上并不需要BOM。
UTF-8是ASCII的一个超集。因为一个纯ASCII字符串也是一个合法的UTF-8字符串,所以现存的ASCII文本不需要转换。为传统的扩展ASCII字符集设计的软件通常可以不经修改或很少修改就能与UTF-8 一起使用;
使用标准的面向字节的排序例程对UTF-8排序将产生与基于 Unicode代码点排序相同的结果。(尽管这只有有限的有用性,因为在任何特定语言或文化下都不太可能有仍可接受的文字排列顺序);
UTF-8和UTF-16都是可扩展标记语言文档的标准编码,所有其它编码都必须通过显式或文本声明来指定;
任何面向字节的字符串搜索算法都可以用于UTF-8的数据(只要输入仅由完整的UTF-8字符组成)。但是,对于包含字符记数的正则表达式或其它结构必须小心。
UTF-8字符串可以由一个简单的算法可靠地识别出来。就是:一个字符串在任何其它编码中表现为合法的UTF-8的可能性很低,并随字符串长度增长而减小。举例说,字符值C0,C1,F5至FF从来没有出现。为了更好的可靠性,可以使用正则表达式来统计非法过长和替代值(可以查看W3 FAQ: Multilingual Forms上的验证UTF-8字符串的正则表达式)。
3.2.3 UTF-8的缺点
A 不利于正则表达式检索,正则表达式可以进行很多英文高级的模糊检索。例如,[a-h]表示a到h间所有字母。同样GBK编码的中文也可以这样利用正则表达式,比如在只知道一个字的读音而不知道怎么写的情况下,也可用正则表达式检索,因为GBK编码是按读音排序的。只是UTF-8不是按读音排序的,所以会对正则表达式检索造成不利影响。但是这种使用方式并未考虑中文中的破音字,因此影响不大。Unicode是按部首排序的,因此在只知道一个字的部首而不知道如何发音的情况下,UTF-8 可用正则表达式检索而GBK不行。
B 你无法从UNICODE字符数判断出 UTF-8文本的字节数,因为UTF-8是一种变长编码它需要用2个字节编码那些用扩展ASCII字符集只需1个字节的字符 ISO Latin-1 是UNICODE的子集,但不是UTF-8的子集 8位字符的UTF-8编码会被email网关过滤,因为internet信息最初设计为7为ASCII码。因此产生了UTF-7编码。UTF-8 在它的表示中使用值100xxxxx的几率超过50%,而现存的实现如ISO 2022,4873,6429,和8859系统,会把它错认为是C1 控制码。因此产生了UTF-7.5编码。
一份写得很差(并且与当前标准的版本不兼容)的UTF-8解析器可能会接受一些不同的伪UTF-8表示并将它们转换到相同的Unicode输出上。这为设计用于处理八位表示的校验例程提供了一种遗漏信息的方式。
C 占据空间大,与其他 Unicode 编码相比,特别是UTF-16,在 UTF-8 中 ASCII 字符占用的空间只有一半,可是在一些字符的 UTF-8 编码占用的空间就要多出,特别是中文、日文和韩文(CJK)这样的象形文字,所以具体因素因文档而异,但不论哪种情况,差别都不可能很明显。
3.3 UTF-8的编码方式
UTF-8是UNICODE的一种变长度的编码表达方式(一般 UNICODE为双字节[指UCS2]),UTF-8就是以8位为单元对UCS进行编码,而UTF-8不使用大尾序和小尾序的形式,每个使用UTF-8储存的字符,除了第一个字节外,其余字节的头两个位元都是以"10"开始,使文字处理器能够较快地找出每个字符的开始位置。
为了与以前的ASCII码相容(ASCII为一个字节),因此 UTF-8 选择了使用可变长度字节来储存 Unicode,具体转换关系如下表:
UCS-4(UNICODE)编码

UTF-8字节流
U-00000000 – U-0000007F

0xxxxxxx
U-00000080 – U-000007FF

110xxxxx 10xxxxxx
U-00000800 – U-0000FFFF

1110xxxx 10xxxxxx 10xxxxxx
U-00010000 – U-001FFFFF

11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 – U-03FFFFFF

111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 – U-7FFFFFFF

1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
(表3-2 Unicode和UTF-8之间的转换关系表)
在ASCII码的范围,用一个字节表示,超出ASCII码的范围就用字节表示,这就形成了我们上面看到的UTF-8的表示方法,这?的好处是当UNICODE文件中只有ASCII码时,储存的文件都为一个字节,所以就是普通的ASCII文件无异,读取的时候也是如此,所以能与以前的ASCII文件相容。
    大于ASCII码的,就会由上面的第一字节的前几位表示该unicode字符的长度,比如110xxxxxx前三位的二进制表示告诉我们这是个 2BYTE的UNICODE字符;1110xxxx是个三位的UNICODE字符,依此类推;xxx 的位置由字符编码数的二进制表示的位填入。越靠右的 x 具有越少的特殊意义。只用最短的那个足够表达一个字符编码数的多字节串。注意在多字节串中,第一个字节的开头"1"的数目就是整个串中字节的数目。
ASCII字母继续使用1字节储存,重音文字、希腊字母或西里尔字母等使用2字节来储存,而常用的汉字就要使用3字节。辅助平面字符则使用4字节。
在UTF-8文件的开首,很多时都放置一个U+FEFF字符(UTF-8以EF,BB,BF代表),以显示这个文字档案是以UTF-8编码。

4 UNICODE与UTF-8的转换
4.1 UNICODE转换为UTF-8
UTF-8的特点是对不同范围的字符使用不同长度的编码。对于 0x00-0x7F之间的字符,UTF-8编码与ASCII编码完全相同。UTF-8编码的最大长度是4个字节。从表3-2可以看出,4字节模板有21个 x,即可以容纳21位二进制数字。Unicode的最大码位0x10FFFF也只有21位。
    如:“汉”字的Unicode编码是0x6C49。0x6C49在0x0800-0xFFFF之间,使用用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将0x6C49写成二进制是:0110 1100 0100 1001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。
    又如:Unicode编码0x20C30在0x010000-0x10FFFF之间,使用4字节模板了:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx。将0x20C30写成21位二进制数字(不足21位就在前面补0):0 0010 0000 1100 0011 0000,用这个比特流依次代替模板中的x,得到:11110000 10100000 10110000 10110000,即F0 A0 B0 B0。
4.2 UTF-8转换为UNICODE
4.2.1 java中文乱码出现的原因
乱码指的是计算机系统不能显示正确的字符,而显示其他无意义的字符或空白,如一堆ASCII代码。这样所显示出来的文本统称为乱码。
乱码是因为“所使用的字符的源码在本地计算机上使用了错误的显示字库”,或在本地计算机的字库中找不到相应于源码所指代的字符所致。不同国家和地区的文本字库采用了相同的一段源码,或是源文件中因为文件受到破坏,致使计算机默认提取的源码错误,或是计算机没有安装相应字库,都有可能产生乱码。
java 采用unicode 编码来处理字符。Java 程序无论是从向文件系统以字符流读、写文件,还是向URL连接写HTML信息,或从URL连接读取参数值,都会有字符编码的转换。下图编码——解码的示意图:
(图4-1 编码/解码示意图)
java乱码产生的根源是由于编码和解码采用的不是同一种码(GBK、 UTF-8、iso8859-1、GB2312等),如将UNICODE编码按照UTF-8进行了编码,而解码的时候却用的是iso8859-1,此时在 java程序中便会出现与原UNICODE编码不一致的情况,即出现乱码。下面以字符串“中国”为例,具体说明中文乱码出现的原因。
    字符(String或char[])"中国" 经过java 编码后的字节流(unicode 字节流)为:4E 2D  56 FD,如果你用new String("中国".getBytes("UTF-8"), "GB2312") 就会产生乱码,如图:
(图4-2 乱码出现原因示意图)
因为getBytes("UTF-8") 取得的是"中国" 经过UTF-8编码后的字节流E4 B8 AD E5 9B BD(UTF-8字节流),而在用new String(bytes, "GB2312") 构造字符串时java 则将UTF-8字节流(E4 B8 AD E5 9B BD)当作是unicode 字节流(因为java是采用unicode 来处理字符的,所以它把字节流统统当作是unicode 字节流),因此它把E4 B8 AD E5 9B BD也看成是unicode字节流。而unicode 字节流(E4 B8 AD E5 9B BD)经过GB2312 编码后就成了“涓??”。于是,乱码产生了。
解决乱码的一般方法是进行正确的解码操作,如编码使用的是utf-8 的方式,那么解码是也须用utf-8进行解码。
正确的解码流程应该是:
(图4-3 UTF-8正确的解码示意图)
4.2.2 将UTF-8转换为UNICODE
    图3-3勾画了将UTF-8转换为UNICODE的基本流程,根据表3-2(Unicode和UTF-8之间的转换关系)可以看出,将UTF-8转换为 UNICODE的过程,实际上是将按UTF-8编码规则填充至UNICODE的编码提取出来的过程,即将8位的字节“xxxx xxxx”提取出来的过程,如某字符以“1110xxxx 10xxxxxx 10xxxxxx”的编码方式进行UTF-8编码,还原后UNICODE编码为:“xxxx xxxx”。
    例如字符“中”,二进制序列(双字节)为:0100 1110 0010 1101,进行UTF-8编码后的字节二进制序列(三字节)为:1110 0100  10 111000  10 101101,进行UTF-8转码即去掉第一个字节的“1110”,第二个字节的“10”,第三个字节的“10”,然后再将剩余的二进制序列组合成一个双字节的二进制序列,即还原为:0100 1110 0010 1101,如此则完成转码。
下面的代码是将以“1110xxxx 10xxxxxx 10xxxxxx”的编码方式进行UTF-8编码,还原为UNICODE编码的操作:
/**
* 将utf-8编码转化为unicode编码
* @param aByte byte[] -原utf-8编码字节数组
* return sByte byte[] -转化后的unicode编码字节数组
*/
public static String changeUtf8ToUnicode(byte[] aByte) {
   int sLength = aByte.length; //原字节数组长度
   //存储转化为unicode编码后的StringBuffer字符串
   StringBuffer sUnicodeStringBuffer = new StringBuffer();
   char sChar; //用于临时存放每个从utf-8中解析出来的unicode编码
   //以下操作是判断字节是否以"1110 xxxx 10xxxxxx 10xxxxxx"的形式出现
   for (int i = 0; i < sLength; i++) { //循环每一个字节
       if (i + 2 < sLength) {
          /**
           * aByte[i] & 0xF0 == 0xE0       ---> 判断当前字节是否以“1110”的形式开始;
           * aByte[i + 1] & 0xC0 == 0x80   ---> 判断下一个字节是否以“10”的形式开始;
           * aByte[i + 2] & 0xC0 == 0x80   ---> 判断下下一个字节是否以“10”的形式开始。
           * 假如条件都满足,则表示此断字节进行了utf-8编码,则将对其进行解码操作(即转
* 化为unicode编码)
           */
          if ((aByte[i] & 0xF0) == 0xE0 && (aByte[i + 1] & 0xC0) == 0x80 &&
              (aByte[i + 2] & 0xC0) == 0x80) {
              /**
               * 将当前字节 1110 xxxx 转化为 xxxx 000000 000000 的形式,具体步骤为:
               * 1110 xxxx << 12 = xxxx 000000 000000
               * 1110 0100 << 12 = 0100 000000 000000
               */
              sChar = (char) (aByte[i] << 12);
              /**
               * 将 前两个字节 转化为 xxxx xxxxxx 000000 的形式,具体步骤为:
               * 10 xxxxxx & 0x003F = 0000 000000 xxxxxx
               * 10 111000 & 0x003F = 0000 000000 111000
               *
               * 0000 000000 xxxxxx << 6 = 0000 xxxxxx 000000
              * 0000 000000 111000 << 6 = 0000 111000 000000
               *
               * xxxx 000000 000000 | 0000 xxxxxx 000000 = xxxx xxxxxx 000000
               * 0100 000000 000000 | 0000 111000 000000 = 0100 111000 000000
               */
              sChar = (char) ((((aByte[i + 1] & 0x003F) << 6) | sChar));
              /**
               * 将此三个字节转化为 xxxx xxxxxx xxxxxx 的形式,具体步骤为:
               * 10 xxxxxx & 0x003F = 0000 0000 00 xxxxxx
               * 10 101101 & 0x003F = 0000 0000 00 101101
               *
               * xxxx xxxxxx 000000 | 0000 000000 xxxxxx = xxxx xxxxxx xxxxxx
               * 0100 111000 000000 | 0000 000000 101101 = 0100 111000 101101
               */
              sChar = (char) ((aByte[i + 2] & 0x003F) | sChar);
               i = i + 2;
               sUnicodeStringBuffer.append(sChar);
           } else {
               sUnicodeStringBuffer.append((char) aByte[i]);
           }
       }
     }
   return sUnicodeStringBuffer.toString();
}
(代码4-1 将UTF-8的一种编码方式转换为UNICODE)
    该代码是仅转换以“1110xxxx 10xxxxxx 10xxxxxx”形式进行UTF-8编码过的字节流,而要彻底的将UTF-8编码转换为UNICODE编码,则需要对表3-2(Unicode和 UTF-8之间的转换关系)中的六种编码方式进行处理。在java中,Unicode代码点的作用范围是U+0000至U+10FFFF之间的字符值,所以表3-2中的前四种编码方式在java中有效,前三种编码方式(BOM)可以直接将编码前的两个字节(1个单位的char)提取出来,组成一个 char,以达到解码的目的,具体实现代码为:
/**
* 将UTF-8编码解码
* @param aByte byte[]
* @return String
*/
public static String changeUtf8ToUnicode(byte[] aByte) {
     StringBuffer sUnicodeStringBuffer = new StringBuffer();
     int sLength = aByte.length;
     int sInt_1, sInt_2, sInt_3, sInt_4, sInt_5, sInt_6;
     for (int i = 0; i < sLength; i++) {
         sInt_1 = (int) aByte[i] & 0xff;
         switch (sInt_1 >> 4) {
         case 0:
         case 1:
         case 2:
         case 3:
         case 4:
         case 5:
         case 6:
         case 7:
              /* 0xxxxxxx*/
             sUnicodeStringBuffer.append((char) aByte[i]);
             break;
         case 12:
         case 13:
              /* 110x xxxx   10xx xxxx*/
             if (i + 1 < sLength) {
                 sInt_2 = (char) aByte[i + 1];
                 if ((sInt_2 & 0xC0) == 0x80) {
                     sUnicodeStringBuffer.append((char)(((sInt_1 & 0x1F) << 6)
| (sInt_2 & 0x3F)));
                     i ++;
                 }
             }
             break;
         case 14:
             /* 1110 xxxx 10xx xxxx 10xx xxxx */
             if (i + 2 < sLength) {
                 sInt_2 = (int) aByte[i + 1];
                 sInt_3 = (int) aByte[i + 2];
                 if (((sInt_2 & 0xC0) == 0x80) || ((sInt_3 & 0xC0) == 0x80)) {
                     sUnicodeStringBuffer.append((char)(((sInt_1 & 0x0F) << 12) | ((sInt_2 & 0x3F) << 6) | ((sInt_3 & 0x3F) << 0)));
                     i = i + 2;
                 }
             }
             break;
         }
        }
        return sUnicodeStringBuffer.toString();
    }
(代码4-2 将UTF-8的前三种编码方式[BOM]还原为UNICODE)
前三种编码方式,即从U-00000000 - U- 0000FFFF区域,包含了99.9%的日常使用字符,一般进行UTF-8与UNICODE的转换时,仅考虑前三中编码方式便可。
第四个编码方式(U-00010000 – U-001FFFFF)则需要使用增补字符进行处理。下面将具体讲解java平台中的增补字符。
4.2.3 Java平台中的增补字符
增补字符是Unicode标准中代码点超出U+FFFF的字符,而 java中的增补字符则是指Unicode代码点的作用范围在U+FFFF至U+10FFFF之间的字符值。
4.2.3.1 Java平台中增补字符出现背景
Unicode最初设计是作为一种固定宽度的16位字符编码。在 Java编程语言中,基本数据类型char初衷是通过提供一种简单的、能够包含任何字符的数据类型来充分利用这种设计的优点。不过,现在看来,16位编码的所有65,536 个字符并不能完全表示全世界所有正在使用或曾经使用的字符。于是,Unicode 标准已扩展到包含多达 1,112,064 个字符。那些超出原来的 16 位限制的字符被称作增补字符。Unicode 标准 2.0 版是第一个包含启用增补字符设计的版本,但是,直到 3.1 版才收入第一批增补字符集。由于 J2SE 的 5.0 版必须支持 Unicode 标准 4.0 版,因此它必须支持增补字符。
    对增补字符的支持也可能会成为东亚市场的一个普遍商业要求。政府应用程序会需要这些增补字符,以正确表示一些包含罕见中文字符的姓名。出版应用程序可能会需要这些增补字符,以表示所有的古代字符和变体字符。中国政府要求支持GB18030(一种对整个 Unicode字符集进行编码的字符编码标准),因此,如果是Unicode 3.1版或更新版本,则将包括增补字符。台湾标准 CNS-11643包含的许多字符在 Unicode 3.1 中列为增补字符。香港政府定义了一种针对粤语的字符集,其中的一些字符是Unicode中的增补字符。最后,日本的一些供应商正计划利用增补字符空间中大量的专用空间收入50,000多个日文汉字字符变体,以便从其专有系统迁移至基于Java平台的解决方案。
    因此,Java平台不仅需要支持增补字符,而且必须使应用程序能够方便地做到这一点。由于增补字符打破了Java编程语言的基础设计构想,而且可能要求对编程模型进行根本性的修改,因此,Java Community Process召集了一个专家组,以期找到一个适当的解决方案。该小组被称为JSR-204专家组,使用 Unicode增补字符支持的Java 技术规范请求的编号。从技术上来说,该专家组的决定仅适用于J2SE平台,但是由于 Java 2平台企业版 (J2EE)处于 J2SE 平台的最上层,因此它可以直接受益,我们期望Java 2平台袖珍版 (J2ME)的配置也采用相同的设计方法。
4.2.3.2 Java平台中增补字符的设计方法
JSR-204 专家组必须作出的主要决定是如何在Java API中表示增补字符,包括单个字符和所有形式的字符序列。专家组考虑并排除了多种方法:
* 重新定义基本类型char,使其具有32位,这样也会使所有形式的 char序列成为UTF-32序列;
* 在现有的16位类型char的基础上,为字符引入一种新的32位基本类型(例如,char32)。所有形式的Char序列均基于 UTF-16;
* 在现有的16位类型char的基础上,为字符引入一种新的32位基本类型(例如,char32)。String和StringBuffer 接受并行API,并将它们解释为UTF-16序列或UTF-32序列;其他char序列继续基于UTF-16;
* 使用int表示增补的代码点。String和StringBuffer接受并行API,并将它们解释为UTF-16序列或UTF-32序列;其他char 序列继续基于UTF-16;
* 使用代理char对,表示增补代码点。所有形式的char序列基于UTF-16;
* 引入一种封装字符的类。String和StringBuffer接受新的API,并将它们解释为此类字符的序列;
* 使用一个CharSequence实例和一个索引的组合表示代码点。
(表4-1 专家组考虑的增补字符设计方法)
在这些方法中,一些在早期就被排除了。例如,重新定义基本类型 char,使其具有32位,这对于全新的平台可能会非常有吸引力,但是,对于J2SE来说,它会与现有的Java虚拟机、序列化和其他接口不兼容,更不用说基于 UTF-32的字符串要使用两倍于基于UTF-16的字符串的内存了。添加一种新类型的 char32可能会简单一些,但是仍然会出现虚拟机和序列化方面的问题。而且,语言更改通常需要比API更改有更长的提前期,因此,前面两种方法会对增补字符支持带来无法接受的延迟。为了在余下的方法中筛选出最优方案,实现小组使用四种不同的方法,在大量进行低层字符处理的代码(java.util.regex包)中实现了对增补字符支持,并对这四种方法的难易程度和运行表现进行了比较。最终,专家组确定了一种分层的方法:
* 使用基本类型int在低层API中表示代码点,例如Character类的静态方法。
* 将所有形式的char序列均解释为UTF-16序列,并促进其在更高层级API中的使用。
* 提供API,以方便在各种char和基于代码点的表示法之间的转换。
(表4-2 专家组最终确定的增补字符设计方法)
在需要时,此方法既能够提供一种概念简明且高效的单个字符表示法,又能够充分利用通过改进可支持增补字符的现有API。同时,还能够促进字符序列在单个字符上的应用,这一点一般对于国际化的软件很有好处。
4.2.3.2 开放的增补字符——基于代码点的API
    新增的低层API分为两大类:用于各种char和基于代码点的表示法之间转换的方法和用于分析和映射代码点的方法。
    最基本的转换方法是Character.toCodePoint(char high, char low)(用于将两个UTF-16代码单元转换为一个代码点)和 Character.toChars(int codePoint)(用于将指定的代码点转换为一个或两个 UTF-16 代码单元,然后封装到一个char[]内。不过,由于大多数情况下文本以字符序列的形式出现,因此,另外提供codePointAt和 codePointBefore方法,用于将代码点从各种字符序列表示法中提取出来:Character.codePointAt(char[] a, int index)和String.codePointBefore(int index)是两种典型的例子。在将代码点插入字符序列时,大多数情况下均有一些针对StringBuffer和StringBuilder类的 appendCodePoint(int codePoint)方法,以及一个用于提取表示代码点的int[]的String构建器。
    几种用于分析代码单元和代码点的方法有助于转换过程:Character类中的 isHighSurrogate和isLowSurrogate方法可以识别用于表示增补字符的char 值;charCount(int codePoint)方法可以确定是否需要将某个代码点转换为一个或两个char。
    但是,大多数基于代码点的方法均能够对所有Unicode字符实现基于char的旧方法对BMP字符所实现的功能。以下是一些典型例子:
* Character.isLetter(int codePoint)可根据Unicode标准识别字母;
* Character.isJavaIdentifierStart(int codePoint)可根据Java语言规范确定代码点是否可以启动标识符;
* Character.UnicodeBlock.of(int codePoint)可搜索代码点所属的Unicode字符子集;
* Character.toUpperCase(int codePoint)可将给定的代码点转换为其大写等值字符。尽管此方法能够支持增补字符,但是它仍然不能解决根本的问题,即在某些情况下,逐个字符的转换无法正确完成。例如,德文字符“??”应该转换为“SS”,这需要使用String.toUpperCase方法;
(表4-3 代码点方法对BOM字符的处理实例)
注意:大多数接受代码点的方法并不检查给定的int值是否处于有效的 Unicode代码点范围之内(如上所述,只有0x0至0x10FFFF之间的范围是有效的)。在大多数情况下,该值是以确保其有效的方法产生的,在这些低层API中反复检查其有效性可能会对系统性能造成负面的影响。在无法确保有效性的情况下,应用程序必须使用 Character.isValidCodePoint方法确保代码点有效。大多数方法对于无效的代码点采取的行为没有特别加以指定,不同的实现可能会有所不同。
    API包含许多简便的方法,这些方法可使用其他低层的API实现,但是专家组觉得,这些方法很常用,将它们添加到J2SE平台上很有意义。不过,专家组也排除了一些建议的简便方法,这给我们提供了一次展示自己实现此类方法能力的机会。例如,专家组经过讨论,排除了一种针对String类的新构建器(该构建器可以创建一个保持单个代码点的String)。以下是使应用程序使用现有的API提供功能的一种简便方法:
/**
* 创建仅含有指定代码点的新 String。
*/
String newString(int codePoint) {
    return new String(Character.toChars(codePoint));
}
(代码4-3 使应用程序使用现有的API提供功能的一种简便方法)
您会注意到,在这个简单的实现中,toChars方法始终创建一个中间数列,该数列仅使用一次即立即丢弃。如果该方法在您的性能评估中出现,您可能会希望将其优化为针对最为普通的情况,即该代码点为BMP字符:
/**
* 创建仅含有指定代码点的新String。
* 针对BMP字符优化的版本。
*/
String newString(int codePoint) {
    if (Character.charCount(codePoint) == 1) {
        return String.valueOf((char) codePoint);
    } else {
        return new String(Character.toChars(codePoint));
    }
}
(代码4-4 使应用程序使用现有的API提供功能的最为普通的情况)
或者,如果您需要创建许多个这样的string,则可能希望编写一个重复使用toChars方法所使用的数列的通用版本:
/**
* 创建每一个均含有一个指定
* 代码点的新 String。
* 针对 BMP 字符优化的版本。
*/
String[] newStrings(int[] codePoints) {
    String[] result = new String[codePoints.length];
    char[] codeUnits = new char[2];
    for (int i = 0; i < codePoints.length; i++) {
         int count = Character.toChars(codePoints[i], codeUnits, 0);
         result[i] = new String(codeUnits, 0, count);
    }
    return result;
}
(代码4-5 toChars方法所使用的数列的通用版本)
不过,最终您可能会发现,您需要的是一个完全不同的解决方案。新的构建器String(intcodePoint)实际上建议作为String.valueOf(char)的一个基于代码点的备选方案。在很多情况下,此方法用于消息生成的环境,例如:
System.out.println("Character " + String.valueOf(char) + " is invalid.");
(代码4-6 消息生成的环境)
新的格式化API支持增补文字,提供一种更加简单的备选方案:
System.out.printf("Character %c is invalid.%n", codePoint);
(代码4-7 增补文字的备选方案)
使用此高层API不仅简捷,而它有很多特殊的优点:它可以避免串联(串联会使消息很难本地化),并将需要移进资源包(resourcebundle)的字符串数量从两个减少到一个。
    在Java编程语言源文件中,如果使用可以直接表示增补字符的字符编码,则使用增补字符最为方便。UTF-8是最佳的选择。在所使用的字符编码无法直接表示字符的情况下,Java编程语言提供一种Unicode转义符语法。此语法没有经过增强,无法直接表示增补字符。而是使用两个连续的Unicode转义符将其表示为UTF-16字符表示法中的两个编码单元。例如,字符U+20000写作“\uD840\uDC00”。最好是写入支持所需增补字符的编码,然后使用一种工具(如native2ascii)将其转换为转义序列。
    遗憾的是,由于其编码问题,属性文件仍局限于ISO8859-1(除非您的应用程序使用新的XML格式)。这意味着您始终必须对增补字符使用转义序列,而且可能要使用不同的编码进行编写,然后使用诸如native2ascii的工具进行转换。
4.2.3.3 经修订的UTF-8
    Java平台对经修订的UTF-8已经很熟悉,但是,问题是应用程序开发人员在可能包含增补字符的文本和UTF-8之间进行转换时需要更加留神。需要特别注意的是,某些J2SE接口使用的编码与UTF-8相似但与其并不兼容。以前,此编码有时被称为“JavamodifiedUTF-8”(经Java修订的UTF-8)或(错误地)直接称为“UTF-8”。对于J2SE5.0,其说明文档正在更新,此编码将统称为“modifiedUTF-8”(经修订的 UTF-8)。
    经修订的UTF-8和标准UTF-8之间之所以不兼容,其原因有两点。其一,经修订的UTF-8将字符U+0000表示为双字节序列0xC00x80,而标准UTF-8使用单字节值0x0。其二,经修订的UTF-8通过对其UTF-16表示法的两个代理代码单元单独进行编码表示增补字符。每个代理代码单元由三个字节来表示,共有六个字节。而标准UTF-8使用单个四字节序列表示整个字符。
    Java虚拟机及其附带的接口(如Java本机接口、多种工具接口或Java类文件)在java.io.DataInput和DataOutput接口和类中使用经修订的UTF-8实现或使用这些接口和类,并进行序列化。Java本机接口提供与经修订的UTF-8之间进行转换的例程。而标准UTF-8由 String类、java.io.InputStreamReader和OutputStreamWriter类、java.nio.charset设施 (facility)以及许多其上层的API提供支持。
    由于经修订的UTF-8与标准的UTF-8不兼容,因此切勿同时使用这两种版本的编码。经修订的UTF-8只能与上述的Java接口配合使用。在任何其他情况下,尤其对于可能来自非基于Java平台的软件的或可能通过其编译的数据流,必须使用标准的UTF-8。需要使用标准的UTF-8时,则不能使用 Java本机接口例程与经修订的UTF-8进行转换。
4.2.3.4 在应用程序内支持增补字符
    对于仅以各种形式char序列([char[]、java.lang.CharSequence实现、 java.text.CharacterIterator实现)处理文本和仅使用接受和退回序列(如char序列)的JavaAPI的应用程序,可能根本不需要进行任何更改。Java平台API的实现应该能够处理增补字符。
    对于本身解释单个字符、将单个字符传送给Java平台API或调用能够返回单个字符的方法的应用程序,则需要考虑这些字符的有效值。在很多情况下,往往不要求支持增补字符。例如,如果某应用程序搜索char序列中的HTML标记,并逐一检查每个char,它会知道这些标记仅使用BasicLatin字符子集中的字符。如果所搜索的文本含有增补字符,则这些字符不会与标记字符混淆,因为UTF-16使用代码单元表示增补字符,而代码单元的值不会用于BMP字符。
    只有在某应用程序本身解释单个字符、将单个字符传送给Java平台API或调用能够返回单个字符的方法且这些字符可能为增补字符时,才必须更改该应用程序。在提供使用char序列的并行API时,最好转而使用此类API。在其他情况下,有必要使用新的API在char和基于代码点的表示法之间进行转换,并调用基于代码点的API。当然,如果您发现在J2SE5.0中有更新、更方便的API,使您能够支持增补字符并同时简化代码(如上格式化范例中所述),则没有必要这样做。
    您可能会犹豫,是将所有文本转换为代码点表示法(即int[])然后在该表示法中处理,还是在大多数情况下仍采用char序列,仅在需要时转换为代码点,两者之间孰优孰劣很难确定。当然,总体来说,Java平台API相对于char序列肯定具有一定的优势,而且采用Java平台API可以节省内存空间。
    对于需要与UTF-8之间进行转换的应用程序,还需要认真考虑是需要标准的UTF-8还是经修订的UTF-8,并针对每种UTF-8采用适当的Java平台。“经修订的UTF-8”部分介绍进行正确选择所需的信息。

 

posted @ 2011-05-10 19:02 Eric_jiang 阅读(6662) | 评论 (0)编辑 收藏

public class EscapeUnescape {
 public static String escape(String src) {
  int i;
  char j;
  StringBuffer tmp = new StringBuffer();
  tmp.ensureCapacity(src.length() * 6);
  for (i = 0; i < src.length(); i++) {
   j = src.charAt(i);
   if (Character.isDigit(j) || Character.isLowerCase(j)
     || Character.isUpperCase(j))
    tmp.append(j);
   else if (j < 256) {
    tmp.append("%");
    if (j < 16)
     tmp.append("0");
    tmp.append(Integer.toString(j, 16));
   } else {
    tmp.append("%u");
    tmp.append(Integer.toString(j, 16));
   }
  }
  return tmp.toString();
 }

 public static String unescape(String src) {
  StringBuffer tmp = new StringBuffer();
  tmp.ensureCapacity(src.length());
  int lastPos = 0, pos = 0;
  char ch;
  while (lastPos < src.length()) {
   pos = src.indexOf("%", lastPos);
   if (pos == lastPos) {
    if (src.charAt(pos + 1) == 'u') {
     ch = (char) Integer.parseInt(src
       .substring(pos + 2, pos + 6), 16);
     tmp.append(ch);
     lastPos = pos + 6;
    } else {
     ch = (char) Integer.parseInt(src
       .substring(pos + 1, pos + 3), 16);
     tmp.append(ch);
     lastPos = pos + 3;
    }
   } else {
    if (pos == -1) {
     tmp.append(src.substring(lastPos));
     lastPos = src.length();
    } else {
     tmp.append(src.substring(lastPos, pos));
     lastPos = pos;
    }
   }
  }
  return tmp.toString();
 }

 public static void main(String[] args) {
  String tmp = "~!@#$%^&*()_+|\\=-,./?><;'][{}\"";
  System.out.println("testing escape : " + tmp);
  tmp = escape(tmp);
  System.out.println(tmp);
  System.out.println("testing unescape :" + tmp);
  System.out.println(unescape(tmp));
 }
}
www.puyufanyi.com

posted @ 2011-05-10 15:25 Eric_jiang 阅读(496) | 评论 (0)编辑 收藏

        广州市残疾人职业培训中心是广州市残疾人联合会直属事业单位,承担广州地区残疾人的职业技能培训服务工作。中心具有丰富的残疾人职业培训经验。中心的训练服务部现正开设对精神病康复者的职业技能训练项目---春晖训练班。目前春晖训练班有学员40名,训练场地超过一千平方米。开设有清洁庶务、中国结艺编织、车缝技能训练三个训练项目。学员已掌握办公场所及家居清洁、中国结编织、车缝等的技能。春辉训练项目现希望承接办公及家居清洁、饰品加工、服饰加工等业务。“同在蓝天下,奉献一片心”。残疾人事业的发展,离不开社会热心人士和机构的支持和关注,春晖训练项目期望得到热心公益、有志于业务发展的社会人士和机构的支持和合作。广州市残疾人职业培训中心训练部地址:广州市西湾路85号荔新大厦3楼电话:020-86504910传真:020-86511032联系人:赖老师 唐老师
桑巴葡语翻译
posted @ 2011-05-05 09:26 Eric_jiang 阅读(188) | 评论 (0)编辑 收藏

       任何一个构件都有其唯一的坐标,根据这个坐标可以定义其在仓库中的唯一存储路径,这便是Maven的仓库布局方式。例如log4j:log4j:1.2.15这一依赖,其对应的仓库路径为log4j/log4j/1.2.15/log4j-1.2.15.jar,细心的读者可以观察到,该路径与坐标的大致对应关系为groupId/artifactId/version/artifactId-version.packaging。下面看一段Maven的源码并结合具体的实例来理解Maven仓库的布局方式:

    private static final char PATH_SEPARATOR = '/';

    private static final char GROUP_SEPARATOR = '.';

    private static final char ARTIFACT_SEPARATOR = '-';

    public String pathOf( Artifact artifact )
    {
        ArtifactHandler artifactHandler = artifact.getArtifactHandler();

        StringBuilder path = new StringBuilder( 128 );

        path.append( formatAsDirectory( artifact.getGroupId() ) ).append( PATH_SEPARATOR );
        path.append( artifact.getArtifactId() ).append( PATH_SEPARATOR );
        path.append( artifact.getBaseVersion() ).append( PATH_SEPARATOR );
        path.append( artifact.getArtifactId() ).append( ARTIFACT_SEPARATOR ).append( artifact.getVersion() );

        if ( artifact.hasClassifier() )
        {
            path.append( ARTIFACT_SEPARATOR ).append( artifact.getClassifier() );
        }

        if ( artifactHandler.getExtension() != null && artifactHandler.getExtension().length() > 0 )
        {
            path.append( GROUP_SEPARATOR ).append( artifactHandler.getExtension() );
        }

        return path.toString();
}

    private String formatAsDirectory( String directory )
    {
        return directory.replace( GROUP_SEPARATOR, PATH_SEPARATOR );
    }
??

该pathOf()方法的目的是根据构件信息生成其在仓库中的路径。在阅读本段代码之前,读者可以先回顾一下上一章Maven坐标的相关内容。这里,我们根据一个实际的例子来分析路径的生成,考虑这样一个构件:groupId=org.testng、artifactId=testng、version=5.8、classifier=jdk15、packaging=jar,其对应的路径按如下步骤生成:

        首先基于构件的groupId准备路径,formatAsDirectory()将groupId中的句点分隔符转换成路径分隔符,该例中,groupId org.testng就会被转换成org/testng,之后再加一个路径分隔符斜杠,那么org.testng就成为了org/testng/。
基于构件的artifactId准备路径,也就是在前面的基础上加上artifactId以及一个路径分隔符,该例中的artifactId为testng,那么在这一步过后路径就成为了org/testng/testng/。
        接着使用版本信息,在前面的基础上加上version和路径分隔符,该例中版本是5.8,那么路径就成为了org/testng/tesgng/5.8/。
这一步再依次加上artifactId,构件分隔符连字号,以及version,于是构建的路径就变成了org/testng/testng/5.8/testng-5.8。读者可能会注意到这里使用了artifactId.getVersion(),而上一步用的是artifactId.getBaseVersion(),version和baseVersion的区别在本章讨论SNAPSHOT的时候会具体阐述。
紧接着如果构件有classifier,就加上构件分隔符和classifier,该例中构件的classifier是jdk15,那么路径就变成org/testng/testng/5.8/testng-5.8-jdk5。
最后第检查构件的extension,若extension存在,则加上句点分隔符和extension,从代码中可以看到,extension是从artifactHandler而非artifact获取,artifactHandler是由项目的packaging决定的,因此可以说,packaging决定了构件的扩展名,该例的packaging是的jar,因此最终的路径为org/testng/testng/5.8/testng-5.8-jdk5.jar。
        到这里笔者(包括读者你)都应该感谢Maven开源社区,正是由于Maven的所有源代码都是开放的,我们才能仔细得深入到其内部工作的所有细节。
        由于Maven仓库是基于简单文件系统存储的,现在我们又理解了其存储方式,因此当遇到一些与仓库相关的问题时,可以很方便的查找相关文件,方便定位问题。例如当Maven无法获得项目声明的依赖时,可以简单该依赖对应的文件在仓库中是否存在,如果不存在,是否有其它版本可用,等等。

 

posted @ 2011-04-21 12:12 Eric_jiang 阅读(317) | 评论 (0)编辑 收藏

仅列出标题
共57页: First 上一页 40 41 42 43 44 45 46 47 48 下一页 Last