在某些情况下,我们需要判断一个标量是否为数字类型,而不仅仅是这个标量是否看上去像个数字,例如:
$val 是一个数字,而
这个 $val 只是像一个数字。
虽然大部分情况下我们不需要对它们区分的这么清楚,但是总会遇到不得不区分它们的情况,例如当你对它进行序列化操作时,如果只是按照像数字来进行判断,序列化之后的结果则不够准确。当然需要区分它们的情况还有很多,我们这里不做列举。我们下面重点来讲一下怎么区分它们。
在 Perl 5.14 之后的版本中,我们可以通过 & 运算符来做区分:
Perl代码
- sub isnumeric {
- my $val = shift;
- return length( do { no warnings "numeric"; $val & "" } ) > 0;
- }
这段代码在 5.14 及其之后的版本上工作的很好,但是在 5.14 之前的版本上它返回的结果却是错误的。
经过各种尝试之后,我们最后终于找到了一种更加简单且在各个版本上都有效的测试方式:
Perl代码
- sub isnumeric {
- my $val = shift;
- ($val ^ $val) eq '0';
- }
原理是字符串做^运算,结果为空字符串,数字做^运算,结果为0。
因为C#中没有提供 Switch on Type 的功能,因此要判断类型通常会用一长串的if else,当然这种写法的问题是不够高效,且不够美观。因此 C# 中对常见类型提供了一组枚举值,也就是 TypeCode(这个枚举类型在.NET for Windows App Store 中居然被取消了,大概是因为DBNull这个类型本身也被取消了,这个值没有对应的类型了,干脆连TypeCode就一起取消了)。可以使用 Type.GetTypeCode 方法来得到类型对应的枚举值,之后对得到的枚举值进行switch操作。但需要注意这里有一组特殊的类型,那就是枚举类型,它的返回值是该枚举类型对应的数字类型,而不是Object类型。
因此如果你想区分枚举类型和数字类型的话,记住不要直接用这个方法,至少要先用 IsEnum 来判断一下。否则你可能会错把枚举了类型当成数字类型进行处理。
在C#中,我们可以很方便的在object对象上使用is运算符来快速的判断对象的类型,is运算符还具有在子类对象上判断是否属于某个父类或者接口的能力,与之等价的判断是,使用GetType方法获得类型后,使用父类或接口上的IsAssignableFrom方法来判断。后者速度比使用 is 运算符要慢很多。所以通常我们在存在对象和已知要判断的类型时优先使用is运算符。
但是is运算符在判断数组类型时却有一个bug,当然这个bug也许是设计问题,但是它确实可能跟我们期望的行为不一致。
这个bug可以这样描述,当一个整数数组对象(byte[], sbyte[], short[], ushort[], int[], uint[], long[], unlong[]这八种)被赋值给一个object对象时,在这个object对象上使用is方法判断时,在等位长整数类型上判断的结果总为True,与类型是否有符号无关。例如:
C#代码
- int[] a = new int[] { -3, -2, -1, 0, 1, 2, 3 };
- Console.WriteLine(a is int[]);
- Console.WriteLine(a is uint[]);
- object o = a;
- Console.WriteLine(o is int[]);
- Console.WriteLine(o is uint[]);
的输出结果为:
True
False
True
True
前三个输出是我们期望的,但是最后一个输出的True却是出乎意料的。
这里如果把 int[] a 换成 uint[] a 也是一样,例如:
C#代码
- uint[] a2 = new uint[] { 0, 1, 2, 3 };
- Console.WriteLine(a2 is int[]);
- Console.WriteLine(a2 is uint[]);
- o = a2;
- Console.WriteLine(o is int[]);
- Console.WriteLine(o is uint[]);
的输出结果是:
False
True
True
True
第三个输出结果True是我们意料之外的。
同样byte[]和sbyte[]也存在这种关系,short[]和ushort[],long[]和ulong[]之间同样也存在这种关系。
所以使用is在这几种数组的object对象上做判断时,一定要注意这个问题,否则很容易掉进这个陷阱中去。
要对系统进行汉化与其说是个技术活,不如说是个力气活,但确实又少不了细心。系统汉化可以分为应用汉化、框架汉化、Luna汉化和其它部分汉化。
应用汉化
ROM中预先安装好的应用都在rootfs/usr/palm/applications中,对每个应用汉化时要注意,不同的程序编写方式,汉化方式也不一样。mojo和enyo的程序在多国语言化方面有些差异。
MOJO程序汉化
mojo程序汉化时,在相应的包名目录下,建一个resources/zh/cn目录。
如果要汉化桌面图标上的显示文字,全局搜索等内容,你需要建立一个appinfo.json的文件,这个文件内容可以直接把从软件包中的这个文件复制过去然后修改,修改时需要注意,图标位置,执行文件的位置等内容,一定要加上../../../这样的路径,否则这些文件找不到。
如果你要汉化js中的那些为实现多国语言已经做了处理的字符串(就是用$L()包含的那些字符串),那你需要再建立一个strings.json文件,其中的内容格式为:
- {
- "OK": "确定",
- "Cancel": "取消"
- }
如果字符串中包含一些特殊字符,一定要注意要用json的转义方式来写。
还有些需要汉化的字符串是在views中的html中的,这些内容的汉化,只需要在这个目录下建立对应的目录,将原始的html复制到这些目录下,对其进行汉化即可。
有时候汉化还涉及到css的一些调整,同样把css文件复制过来做修改就可以啦。
ENYO程序汉化
enyo的程序汉化相对来说简单些。
对appinfo.json的汉化与mojo相同。
对字符串的汉化只需要在resources下建一个zh_cn.json的文件就可以了,内容格式跟mojo的strings.json格式相同。
enyo没有views这种东西,所以就不用管了。
框架汉化
在rootfs/usr/palm/frameworks下的许多框架文件也需要汉化。具体那些文件需要汉化就不说了,可以参考其它多语言化文件来处理。
frameworks这里面有些目录在一些比较新版本的原始ROM中就已经包含了汉化内容,比如mojo,mojo2,enyo这些目录,所以如果发现他们里面原本就有汉化内容的,就不需要重复汉化了。
LUNA汉化
在rootfs/usr/lib/luna下,有部分资源是需要汉化的,其中包括luna-network和system。汉化的内容跟汉化的方法跟mojo应用程序相同。
其中关于信标的汉化最主要的部分也在这里。在rootfs/usr/lib/luna/system/luna-systemui/resources/zh/cn/strings.json中,添加:
- "CHINA MOBILE": "中国移动",
- "CHN-UNICOM": "中国联通",
- "China Telecom": "中国电信",
- "China Unicom": "中国联通",
- "CMCC": "中国移动",
就可以实现对信标的汉化了。注意上面"CHINA MOBILE"中间是两个空格,而不是一个。
但要实现更完美的信标汉化,还需要在:
rootfs/usr/palm/applications/com.palm.app.phoneprefs/resources/zh/cn/strings.json
rootfs/usr/palm/sysmgr/localization/zh_cn/strings.json
里面都添加上这一段内容。
对于一卡多号的用户来说,上面这些数据可以能对您的信标中文化无效,因为一卡多号的卡里面的信标内容跟上面的是不同的,有同学反映他的一卡多号的信标后面还带有手机号,所以对于这种卡就没办法做统一的汉化了,但是使用者可以按照这种方法,把自己那些特殊的信标加到这里面。
其它部分汉化
在rootfs/usr/palm/public和rootfs/usr/palm/sysmgr里面也有一点资源需要汉化。至于那些文件,可以参考这里面的其它语言的资源。
这里主要提一点关于启动器中那些页面标题的汉化,这些标题的汉化可以在rootfs/usr/palm/sysmgr/localization/zh_cn/strings.json中添加上。
而且不但可以添加默认的那几个(Applications、Extras、System这些),你还可以添加用户可能会建立的新页面的标题,比如Games、News等,这些添加之后,用户在建立具有这些名称的页面之后,当再次重启系统时候,这些英文名在中文界面下也会自动变成中文了。
最后还要强调一下,汉化是个细心的活,要想完美汉化,对每个词每个句子甚至每个缩写都要字斟句酌才能得到完美的结果。
举个例子,比如在日历的汉化中,有几个英文序数词结尾的汉化,这些序数词后缀分别是"nd"、"rd"、"st"、"th",如果你不了解上下文,当看这几个词你可能不知道什么意思(但如果你够聪明的话,当看到这几个摆在一起时应该也会想到)。我们知道在中文里序数词和基数词是没区别的(如果非要说有区别的话,那区别就是中文的序数词是在基数词前面加一个“第”字),那么在汉化这几个词时,我们就应该把它们汉化为空字符串,而不应该原样保留(因为我们没办法把后缀汉化改成前缀汉化,所以不能把它们汉化成“第”)。
类似的地方还有好多,这里仅仅是举个例子。从这里我们可以看出,汉化的确不是一件容易的事情,所以,在这里我要向一直以来提供系统汉化包的开发者们表示崇高的敬意!
汉化包的打包
汉化包在打包时,如果考虑到集成到ROM中,并且可以方便卸载的话,最好的方式是把这些新添加的汉化资源单独拿出来打成一个包,这些资源在包里面就以原始路径存在,不需要postinst、postrm之类的脚本,直接打包之后,放在<carrier>.tar中就可以啦。
这样做的好处是,便于汉化包的卸载和升级。
但是这种包直接提供给用户安装的话,用户还需要一定的Linux命令行知识,能够自己使用ipkg命令来安装卸载包才行。因为这些包是不能通过preware、WQI来安装的,否则安装位置是不对的。
现在直接提供给用户安装的汉化包,大部分都是以应用程序方式来打包,然后通过postinst方式来将汉化资源再复制到相应的汉化目录下。这种汉化包对用户来说,便于使用preware、WQI来安装,但是却不方便卸载,也不方便集成到ROM中。
所以,两种方式各有利弊,根据具体情况来做选择就行了。
前面6节基本上已经把ROM定制的具体过程都讲完了。从这节开始,我们来讨论一点高级内容。不过本人水平有限,以下内容算是抛砖引玉了。
前面在ROM文件结构一节中,我们已经提到,rootfs下的大部分文件都是预安装好的包,那我们有没有可能将这些包都还原为ipk呢?
当然可以。
下面我们就来看一下这个过程该如何实现。
首先我们需要把ROM分解,分解的工具最方便的还是使用MetaDoctor里面的unpack-doctor这个脚本。在Linux下,我们最好是用root帐号来执行这个命令,以保证包中文件的权限不会改变。
展开之后,我们可以在rootfs/usr/lib/ipkg/info中找到所有已安装的包的control文件,因为每个包都有一个对应的control,所以我们以这个control文件为依据,就可以列出系统内所有的包。
之后循环读取这些control文件名,然后通过它来获取包名,然后用包名为该包建立一个目录,在其中再创建CONTROL和data这两个目录,然后把control复制(或移动)到CONTROL目录下,然后把该包对应的preinst、postinst、prerm、postrm脚本也都统统复制(或移动)到CONTROL目录下,最后再根据list文件中的内容,将该包中包含的文件及其目录复制(或移动)到data目录下,最后使用ipkg-build脚本,对该目录进行打包就可以啦。
下面是将ROM分解为ipk文件的完整脚本:
-
-
- [ -f $1 ] || exit 1
-
- currpath=`pwd`
- $currpath/unpack-doctor $1
-
- NAME=`basename $1 .jar`
-
- rm -rf $NAME/ipkgs $NAME/build
- mkdir -p $NAME/ipkgs $NAME/build
-
- cd $NAME/ipkgs
-
- ROOTFS=$currpath/$NAME/rootfs
- INFOPATH=$ROOTFS/usr/lib/ipkg/info
-
- ls $INFOPATH/*.control | while read control; do
- package=`basename $control .control`
- PACKAGE=$currpath/$NAME/build/$package
- CONTROL=$PACKAGE/CONTROL
- data=$PACKAGE/data
- mkdir -p $CONTROL $data
- cp $control $CONTROL/control
- if [ -f $INFOPATH/$package.preinst ]; then
- mv $INFOPATH/$package.preinst $CONTROL/preinst
- fi
- if [ -f $INFOPATH/$package.postinst ]; then
- mv $INFOPATH/$package.postinst $CONTROL/postinst
- fi
- if [ -f $INFOPATH/$package.prerm ]; then
- mv $INFOPATH/$package.prerm $CONTROL/prerm
- fi
- if [ -f $INFOPATH/$package.postrm ]; then
- mv $INFOPATH/$package.postrm $CONTROL/postrm
- fi
- if [ -f $INFOPATH/$package.list ]; then
- cat $INFOPATH/$package.list | while read filename; do
- pathname=`dirname "$filename"`
- mkdir -p "$data/$pathname"
- mv -f -T "$ROOTFS/$filename" "$data/$filename"
- done
- $currpath/ipkg-build $PACKAGE
- fi
- done
-
- cd $currpath
把这个保存为一个脚本,然后在同一个目录下放上unpack-doctor和ipkg-build脚本,然后再把要展开的ROM放在这个目录下,这个脚本的目录下运行脚本后面跟上ROM名称,就可以将ROM分解为ipk了。
理解了以上脚本之后,根据该原理,就也可以很方便的完成ROM杂交,ROM内置安装包移除等脚本了。这些脚本的编写就看各位高手的啦。