2009年4月26日星期日

Java 常用类型转换 总结

Java中常用到的类型转换集中在String Integer int Object之间,下面对具体转换方法进行归纳总结:

一、Integer与int之间

1、 Integer to int
int num=Integer.intValue();
2、 int to Integer
Integer integer=new Integer(i);


二、Integer与String之间
1、 Integer to String
Integer integer;
integer.toString();

2、String to Integer
String s;
Integer integer(s)


三、String 与 int相互转换(实质多半借助Integer)

1、String to int

1) int i = Integer.parseInt([String]);
或 i = Integer.parseInt([String],[int radix]);

2) int i = Integer.valueOf(my_str).intValue();

注: 字串转成 Double, Float, Long 的方法大同小异.


2 int to String

1) String s = String.valueOf(i);

2) String s = Integer.toString(i);

3) String s = "" + i;

注: Double, Float, Long 转成字串的方法大同小异.


总结:int是基本数据类型,String,Integer,Object都是对象类型,转换的时候





参照上面的关系图
按部就班即可。

2009年4月22日星期三

Java 常用正则表达式小结

正则imba,值得研究。

常用的正则表达式:
Java中的17种常用正则表达式归纳:


  
  01、"^\\d+$"  //非负整数(正整数 + 0)
  02、"^[0-9]*[1-9][0-9]*$"  //正整数
  03、"^((-\\d+)(0+))$"  //非正整数(负整数 + 0)
  04、"^-[0-9]*[1-9][0-9]*$"  //负整数
  05、"^-?\\d+$"    //整数

  06、"^\\d+(\\.\\d+)?$"  //非负浮点数(正浮点数 + 0)
  07、"^(([0-9]+\\.[0-9]*[1-9][0-9]*)([0-9]*[1-9][0-9]*\\.[0-9]+)([0-9]*[1-9][0-9]*))$"  //正浮点数
  08、"^((-\\d+(\\.\\d+)?)(0+(\\.0+)?))$"  //非正浮点数(负浮点数 + 0)
  09、"^(-(([0-9]+\\.[0-9]*[1-9][0-9]*)([0-9]*[1-9][0-9]*\\.[0-9]+)([0-9]*[1-9][0-9]*)))$"  //负浮点数
  10、"^(-?\\d+)(\\.\\d+)?$"  //浮点数
  11、"^[A-Za-z]+$"  //由26个英文字母组成的字符串
  12、"^[A-Z]+$"  //由26个英文字母的大写组成的字符串
  13、"^[a-z]+$"  //由26个英文字母的小写组成的字符串
  14、"^[A-Za-z0-9]+$"  //由数字和26个英文字母组成的字符串
  15、"^\\w+$"  //由数字、26个英文字母或者下划线组成的字符串
  16、"^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$"    //email地址
  17、"^[a-zA-z]+://(\\w+(-\\w+)*)(\\.(\\w+(-\\w+)*))*(\\?\\S*)?$"  //url



附具体使用程序:

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

public class SpeStrFilter {


//过滤特殊字符
public static String StringFilter(String str) throws PatternSyntaxException
{
// 只允许字母和数字
// String regEx = "[^a-zA-Z0-9]";
// 清除掉所有特殊字符


String regExSymbol="--―_`〕「」∶※→←%/О<>●·~『』[]〈〉\"《》.~!б㎡@#$%%^&*()+={}':;;',\\[\\].<>/?~!#¥…&*()+ 【】‘:”“’。,、?";//特殊符号
String regExNum="01234567890123456789ⅣⅤⅥⅦⅧⅨⅩⅪⅫ";//数字
String regExletter="a-zA-Z";//字母
String regEmail ="\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*" ;//E-mail
String regURL = "^[a-zA-z]+://(\\w+(-\\w+)*)(\\.(\\w+(-\\w+)*))*(\\?\\S*)?$";
String regTelNum = "\\d{3}-\\d{8}\\d{4}-\\d{7}";//国内电话,xxx-xxxxxxx的格式


//String regEx="[-`<>●『』〈〉\"《》.~!@#$%^&*()+={}':;',\\[\\].<>/?~!@#¥%……&*()——+{} 【】‘;:”“’。,、?1234567890]";


Pattern p = Pattern.compile("["+regExSymbol+regExNum+regExletter+"]"); //选用符号组合(这里用特殊符号+数字,注意顺序,不是数字+特殊符号)
Matcher m = p.matcher(str);
return m.replaceAll("").trim();
}

public static void main(String[] args) throws PatternSyntaxException
{
String str = "*adCVs*34_a _09_b5*[/435^*&城池()^$$&*).{}+..)%%*(*.中国}34{45[]12.fd'*&999下面是中文的字符¥……{}【】。,;’“‘”?";
// String str = " <>\"再 美丽~!@#¥%……&*()- 足 部 美容 中心 采用 专业 技术 和 独特 修复 剂 可 彻底 治愈灰指甲 让 您 再现 靓 丽 双 足 生活 无 忧 无效 退款 诚信 为 本 -";
System.out.println(str);
System.out.println(StringFilter(str));
}

}

参考:http://jiake0504.javaeye.com/blog/182095
http://blog.chinaunix.net/u/21684/showart_435251.html

Java 调用 exe与cmd 小结

有时候,需要在Java中调用现成的.exe文件或是cmd进行操作,下面结合自己的项目实例,做以小结。


现在我们的目标是:用Java调用cmd,执行C:/libsvm/windows/svm-train.exe,svm-train.exe参数为: [options] training_set_file [model_file]
具体是 -c 32 -g 0.0078125 trainset trainset.model
可以将doc窗口的输出显示到JAVA IDE的控制台上。

我们需要用到java.lang.Runtime类
主要成员函数:
getRuntime() 返回与当前 Java 应用程序相关的运行时对象。
Runtime r=Runtime.getRuntime();

exec(String command, String[] envp, File dir) 在有指定环境和工作目录的独立进程中执行指定的字符串命令。 。command为.exe及其参数,envp null即可,dir=new File(FilePath)

java.lang.Process类
主要成员函数:
waitFor() 导致当前线程等待,如果必要,一直要等到由该 Process 对象表示的进程已经终止。
注:Process p = Runtime.getRuntime().exec(arg)

实现代码如下:

import java.io.*;
import java.lang.*;

public class RunExe {

public static void main(String[] arg)
{
Runtime r = Runtime.getRuntime();


try
{
String[] cmd = new String[5];

cmd[0] = "cmd "; //命令行
cmd[1] = "/c "; //运行后关闭,
cmd[2] = "start "; //启动另一个窗口来运行指定的程序或命令(cmd 命令集里的)
cmd[3] = "C:\\libsvm\\windows"; //要运行的.exe程序的目录

cmd[4] = "svm-train -c 32 -g 0.0078125 -v 5 trainset ";//exe程序及其需要的参数
String Naiveexe = "calc.exe";//windows自带计算器
String line;
String space=" ";
//Process p = Runtime.getRuntime().exec("cmd /c svm-train.exe -c 32 -g 0.0078125 -v 5 trainset",null,new File("C:\\libsvm\\windows")); 此时输出到控制台
//Process p = Runtime.getRuntime().exec("cmd /c start svm-train.exe -c 32 -g 0.0078125 -v 5 trainset",null,new File("C:\\libsvm\\windows"));此时弹出dos窗口运行


Process p = Runtime.getRuntime().exec((cmd[0]+cmd[1]+cmd[4]),null,new File(cmd[3]));
//Process p = Runtime.getRuntime().exec("calc.exe"); //直接运行计算器



BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ( (line=br.readLine()) != null)
System.out.println(line);

//p.waitFor();
}


catch(Exception e)
{System.out.println("Error!");

}

OK,enjoy!

2009年4月21日星期二

转义字符小结

记住常用转义字符在某些时候会相当方便。

常用转义字符:

\b:回退:向后退一格

\f:换页

\n:换行,光标到下行行首

\r:回车,光标到本行行首

\t:水平制表

\v:垂直制表

\\:反斜杠

\‘:单引号

\":双引号

\?:问号

\ddd:三位八进制

\40 空格

\xhh:二位十六进制


\0:空字符(NULL),什么都不做。换行只是换一行,不改变光标的横坐标;回车只是回到行首,不改变光标的纵坐标。

2009年4月19日星期日

TFIDF公式

TFIDF是计算向量空间模型中(VSM),词语在文档中所占权值大小的公式
TFIDF=TF xIDF
TF=Term Frequency
IDF=Inverse Document Frequency
具体计算公式如下: 词汇Wi对于文档d的tf为:
tf i(d) = d中Wi出现的总次数/d中所有词汇的总数
idf i = log(总的文档数/包含词汇Wi的文档数+0.1)//0.1 for 数据平滑

为了避免文档长度对tfidf的影响,进行归一化
得到最终公式:
















2009年4月16日星期四

static+

static:
如果一个成员被声明为static,它就能够在它的类的任何对象创建之前被访问,而不必引用任
何对象。你可以将方法和变量都声明为static

Eclipse环境下的Java调试小结

设置断点,debug(Ctrl+F11),跟踪查错
主要快捷键:
F5 本方法内
F6 跳入下一层
F7 跳出该层
F8 调到下个断点


调试经验与技巧

2009年4月15日星期三

Java中应用正则表达式过滤的小例子

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

public class SpeStrFilter {


//过滤特殊字符
public static String StringFilter(String str) throws PatternSyntaxException
{


String regExSymbol="--―_`〕「」∶※→←%/О<>●·~『』[]〈〉\"《》.~!ⅠⅡⅢб㎡@#$%%^&*()+={}':;',\\[\\].<>/?~!@#¥%……&*()——+{} 【】‘;:”“’。,、?";
String regExNum="01234567890123456789ⅣⅤⅥⅦⅧⅨⅩⅪⅫ";
String regExletter="a-zA-Z";

Pattern p = Pattern.compile
("["+regExSymbol+regExNum+"]"); //选择特殊字符和过滤数字,注意顺序
Matcher m = p.matcher(str);
return m.replaceAll("").trim();
}


public static void main(String[] args) throws PatternSyntaxException
{
String str = "*adCVs*34_a _09_b5*[/435^*&城池()^$$&*).{}+..)%%*(*.中国}34{45[]12.fd'*&999下面是中文的字符¥……{}【】。,;’“‘”?";
System.out.println(str);
System.out.println(StringFilter(str));
}

}

输入:*adCVs*34_a _09_b5*[/435^*&城池()^$$&*).{}+..)%%*(*.中国}34{45[]12.fd'*&999下面是中文的字符¥……{}【】。,;’“‘”?
输出:adCVsab城池中国fd下面是中文的字符

ictclas4j bug小结

bug:
1“邓颖超”2深圳的“圳”3发飙的“飚”4發财的“發”5“月份牌”6单独的“年”字

1、邓颖超
分词后漏掉“邓颖”只剩“超”
解决办法:(转http://tinypig.javaeye.com/blog/250926
修改方法是将ictclas4j中PosTagger类的personRecognize方法里面的这段代码

if (sn.getPos() < color="#ff0000">personName += sn.getSrcWord();
也就是将该if判断句的条件部分注释掉,同时将getWord改为getScrWord

深圳的“圳”, 发飙的“飚”
运行异常:说是Dictionary.java中getMaxMatch(String word)函数中的pointernull异常,调试发现是wis=null,
解决方法:将原程序中的语句 for (int j = 0; j < color="#ff0000">for (int j = 0; j 小于 wis.size()&&wis!=null; j++)

对于發财的“發” 解决办法:
将Dictionary.java中的findInOriginalTable方法里的
if (res != null && wts != null )
{ WordTable wt = wts.get(index);
……
}
改为 if (res != null && wts != null && index>=0 && index小于wts.size())
{ WordTable wt = wts.get(index);
……
}

bug5:“月份牌”出现死循环,网友发现
解决办法:
出错位置:org.ictclas4j.segment.GraphGenerate类中源代码90行左右
WordItem wi = null;
for (; j <= atoms.size(); j++) { int totalFreq = 0; wi = dict.getMaxMatch(word); if (wi != null) { // find it if (word.equals(wi.getWord())) { ArrayList wis = dict.getHandle(word); for (WordItem w : wis)totalFreq += w.getFreq();// 1年内,1999年末 if (word.length() == 2 && segGraph.getSize() > 0)
{SegNode g2 = segGraph.getLast();
if(Utility.isAllNum(g2.getWord())
Utility.isAllChinese(g2.getWord())&& (g2.getWord().indexOf("年") == 0 g2.getWord().indexOf("月") == 0))
{if ("末内中底前间初".indexOf(word.substring(1)) != -1)break;}}// 只有一个性词,存贮它
SegNode sg = null;
if (wis.size() == 1)
sg = new SegNode(i, j,wis.get(0).getHandle(),totalFreq , word);
else sg = new SegNode(i, j, 0,totalFreq , word);
segGraph.insert(sg, true);}
if (flag) //此两行不应该放在这,应该放到这个for循环的外面,否则“月份”后面如果有词构成一个词的话会出错如“月份牌”是一个词。
i++; //就会出错,将其放在for循环外
if (j < word2 =" atoms.get(j).getWord();word">

bug6:输入:"年"或“年加一个与年不成词的词” 输出:“始##始年”
问题原因:AdjustSeg.java中对“年”做了特殊判断,而单独的“年”在开头与起始符号“始##始”分在一起了 解决办法: 在文件第80行左右将程序改为: else if ("年".equals(curWord))
{ if (!prevsn.getWord().equals("始##始")&& prevsn != null &&Utility.isYearTime(prevsn.getSrcWord()))
{prevsn.setCol(sn.getCol());
prevsn.setWord(Utility.UNKNOWN_TIME);
prevsn.setSrcWord(prevsn.getSrcWord()+curWord);
prevsn.setPos(-POSTag.TIME); continue; } }
即在if的判断条件里添加语句:!prevsn.getWord().equals("始##始")
new bug? new solution!



续:病有猪流感,bug有新内容
某个不和“發”成词的字 + “發”,比如 :淘發
类比 ,其他生僻字或者繁体字(GB2312外的?)分词结果为“淘”即出现丢字现象。


原因:这类字的pos=4,然后结尾标志“末##末”也是4,然后就被丢弃了

解决:加个判断,别让他们丢……

具体方法:在PosTagger.java的第100行左右
switch (tagType)
{ case TT_NORMAL:
for (SegNode sn : sns)
{ if (sn.getPos() == 0)
{ sn.setPos(getBestTag(sn));
}
}
}

改为

switch (tagType)
{ case TT_NORMAL:
for (SegNode sn : sns)
{ if (sn.getPos() == 0)
{ //sn.setPos(getBestTag(sn));
sn.setPos(getBestTag(sn)==4?3:getBestTag(sn));//修改此行,使pos=4的不被当成结尾,可以在处理繁体字时不丢字
}
}
}

然后就ok了。

2009年4月14日星期二

正则表达式总结

入门教程:http://www.unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm
应用文章:http://www.javaeye.com/topic/350789

补充:

座机:/^((\(\d{3}\))(\d{3}\-))?(\(0\d{2,3}\)0\d{2,3}-)?[1-9]\d{6,7}$/
手机: /^((\(\d{3}\))(\d{3}\-))?1[35]\d{9}$/


(转) 随便说说字符集和编码

快下班时,爱问问题的小朋友Nico又问了一个问题:

"sqlserver里面有char和nchar,那个n据说是指unicode的数据,这个是什么意思。"

并不是所有简单的问题都很容易回答,就像这个问题一样。于是我答应专门写一篇BLOG来从头讲讲编码的故事。那么就让我们找个草堆坐下,先抽口烟,看看夜晚天空上的银河,然后想一想要从哪里开始讲起。嗯,也许这样开始比较好……





很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物。他们看到8个开关状态是好的,于是他们把这称为"字节"。

再后来,他们又做了一些可以处理这些字节的机器,机器开动了,可以用字节来组合出很多状态,状态开始变来变去。他们看到这样是好的,于是它们就这机器称为"计算机"。



开始计算机只在美国用。八位的字节一共可以组合出256(2的8次方)种不同的状态。

他们把其中的编号从0开始的32种状态分别规定了特殊的用途,一但终端、打印机遇上约定好的这些字节被传过来时,就要做一些约定的动作。遇上00x10, 终端就换行,遇上0x07, 终端就向人们嘟嘟叫,例好遇上0x1b, 打印机就打印反白的字,或者终端就用彩色显示字母。他们看到这样很好,于是就把这些0x20以下的字节状态称为"控制码"。

他们又把所有的空格、标点符号、数字、大小写字母分别用连续的字节状态表示,一直编到了第127号,这样计算机就可以用不同字节来存储英语的文字了。大家看到这样,都感觉很好,于是大家都把这个方案叫做 ANSI 的"Ascii"编码(American Standard Code for Information Interchange,美国信息互换标准代码)。当时世界上所有的计算机都用同样的ASCII方案来保存英文文字。

后来,就像建造巴比伦塔一样,世界各地的都开始使用计算机,但是很多国家用的不是英文,他们的字母里有许多是ASCII里没有的,为了可以在计算机保存他们的文字,他们决定采用127号之后的空位来表示这些新的字母、符号,还加入了很多画表格时需要用下到的横线、竖线、交叉等形状,一直把序号编到了最后一个状态255。从128到255这一页的字符集被称"扩展字符集"。从此之后,贪婪的人类再没有新的状态可以用了,美帝国主义可能没有想到还有第三世界国家的人们也希望可以用到计算机吧!

等中国人们得到计算机时,已经没有可以利用的字节状态来表示汉字,况且有6000多个常用汉字需要保存呢。但是这难不倒智慧的中国人民,我们不客气地把那些127号之后的奇异符号们直接取消掉, 规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了。

中国人民看到这样很不错,于是就把这种汉字方案叫做 "GB2312"。GB2312 是对 ASCII 的中文扩展。

但是中国的汉字太多了,我们很快就就发现有许多人的人名没有办法在这里打出来,特别是某些很会麻烦别人的国家领导人。于是我们不得不继续把 GB2312 没有用到的码位找出来老实不客气地用上。

后来还是不够用,于是干脆不再要求低字节一定是127号之后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容。结果扩展之后的编码方案被称为 GBK 标准,GBK 包括了 GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。

后来少数民族也要用电脑了,于是我们再扩展,又加了几千个新的少数民族的字,GBK 扩成了 GB18030。从此之后,中华民族的文化就可以在计算机时代中传承了。

中国的程序员们看到这一系列汉字编码的标准是好的,于是通称他们叫做 "DBCS"(Double Byte Charecter Set 双字节字符集)。在DBCS系列标准里,最大的特点是两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里,因此他们写的程序为了支持中文处理,必须要注意字串里的每一个字节的值,如果这个值是大于127的,那么就认为一个双字节字符集里的字符出现了。那时候凡是受过加持,会编程的计算机僧侣们都要每天念下面这个咒语数百遍:

"一个汉字算两个英文字符!一个汉字算两个英文字符……"



因为当时各个国家都像中国这样搞出一套自己的编码标准,结果互相之间谁也不懂谁的编码,谁也不支持别人的编码,连大陆和台湾这样只相隔了150海里,使用着同一种语言的兄弟地区,也分别采用了不同的 DBCS 编码方案??当时的中国人想让电脑显示汉字,就必须装上一个"汉字系统",专门用来处理汉字的显示、输入的问题,但是那个台湾的愚昧封建人士写的算命程序就必须加装另一套支持 BIG5 编码的什么"倚天汉字系统"才可以用,装错了字符系统,显示就会乱了套!这怎么办?而且世界民族之林中还有那些一时用不上电脑的穷苦人民,他们的文字又怎么办?

真是计算机的巴比伦塔命题啊!

正在这时,大天使加百列及时出现了??一个叫 ISO (国际标谁化组织)的国际组织决定着手解决这个问题。他们采用的方法很简单:废了所有的地区性编码方案,重新搞一个包括了地球上所有文化、所有字母和符号的编码!他们打算叫它"Universal Multiple-Octet Coded Character Set",简称 UCS, 俗称 "UNICODE"。

UNICODE 开始制订时,计算机的存储器容量极大地发展了,空间再也不成为问题了。于是 ISO 就直接规定必须用两个字节,也就是16位来统一表示所有的字符,对于ascii里的那些“半角”字符,UNICODE 包持其原编码不变,只是将其长度由原来的8位扩展为16位,而其他文化和语言的字符则全部重新统一编码。由于"半角"英文符号只需要用到低8位,所以其高8位永远是0,因此这种大气的方案在保存英文文本时会多浪费一倍的空间。

这时候,从旧社会里走过来的程序员开始发现一个奇怪的现象:他们的strlen函数靠不住了,一个汉字不再是相当于两个字符了,而是一个!是的,从 UNICODE 开始,无论是半角的英文字母,还是全角的汉字,它们都是统一的"一个字符"!同时,也都是统一的"两个字节",请注意"字符"和"字节"两个术语的不同,“字节”是一个8位的物理存贮单元,而“字符”则是一个文化相关的符号。在UNICODE 中,一个字符就是两个字节。一个汉字算两个英文字符的时代已经快过去了。

从前多种字符集存在时,那些做多语言软件的公司遇上过很大麻烦,他们为了在不同的国家销售同一套软件,就不得不在区域化软件时也加持那个双字节字符集咒语,不仅要处处小心不要搞错,还要把软件中的文字在不同的字符集中转来转去。UNICODE 对于他们来说是一个很好的一揽子解决方案,于是从 Windows NT 开始,MS 趁机把它们的操作系统改了一遍,把所有的核心代码都改成了用 UNICODE 方式工作的版本,从这时开始,WINDOWS 系统终于无需要加装各种本土语言系统,就可以显示全世界上所有文化的字符了。

但是,UNICODE 在制订时没有考虑与任何一种现有的编码方案保持兼容,这使得 GBK 与UNICODE 在汉字的内码编排上完全是不一样的,没有一种简单的算术方法可以把文本内容从UNICODE编码和另一种编码进行转换,这种转换必须通过查表来进行。

如前所述,UNICODE 是用两个字节来表示为一个字符,他总共可以组合出65535不同的字符,这大概已经可以覆盖世界上所有文化的符号。如果还不够也没有关系,ISO已经准备了UCS-4方案,说简单了就是四个字节来表示一个字符,这样我们就可以组合出21亿个不同的字符出来(最高位有其他用途),这大概可以用到银河联邦成立那一天吧!



UNICODE 来到时,一起到来的还有计算机网络的兴起,UNICODE 如何在网络上传输也是一个必须考虑的问题,于是面向传输的众多 UTF(UCS Transfer Format)标准出现了,顾名思义,UTF8就是每次8个位传输数据,而UTF16就是每次16个位,只不过为了传输时的可靠性,从UNICODE到UTF时并不是直接的对应,而是要过一些算法和规则来转换。

受到过网络编程加持的计算机僧侣们都知道,在网络里传递信息时有一个很重要的问题,就是对于数据高低位的解读方式,一些计算机是采用低位先发送的方法,例如我们PC机采用的 INTEL 架构,而另一些是采用高位先发送的方式,在网络中交换数据时,为了核对双方对于高低位的认识是否是一致的,采用了一种很简便的方法,就是在文本流的开始时向对方发送一个标志符??如果之后的文本是高位在位,那就发送"FEFF",反之,则发送"FFFE"。不信你可以用二进制方式打开一个UTF-X格式的文件,看看开头两个字节是不是这两个字节?



讲到这里,我们再顺便说说一个很著名的奇怪现象:当你在 windows 的记事本里新建一个文件,输入"联通"两个字之后,保存,关闭,然后再次打开,你会发现这两个字已经消失了,代之的是几个乱码!呵呵,有人说这就是联通之所以拼不过移动的原因。

其实这是因为GB2312编码与UTF8编码产生了编码冲撞的原因。

从网上引来一段从UNICODE到UTF8的转换规则:

Unicode

UTF-8

0000 - 007F

0xxxxxxx



0080 - 07FF

110xxxxx 10xxxxxx



0800 - FFFF

1110xxxx 10xxxxxx 10xxxxxx


例如"汉"字的Unicode编码是6C49。6C49在0800-FFFF之间,所以要用3字节模板:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 1100 0100 1001,将这个比特流按三字节模板的分段方法分为0110 110001 001001,依次代替模板中的x,得到:1110-0110 10-110001 10-001001,即E6 B1 89,这就是其UTF8的编码。

而当你新建一个文本文件时,记事本的编码默认是ANSI, 如果你在ANSI的编码输入汉字,那么他实际就是GB系列的编码方式,在这种编码下,"联通"的内码是:

c1 :1100 0001

aa :1010 1010

cd :1100 1101

a8 :1010 1000

注意到了吗?第一二个字节、第三四个字节的起始部分的都是"110"和"10",正好与UTF8规则里的两字节模板是一致的,于是再次打开记事本时,记事本就误认为这是一个UTF8编码的文件,让我们把第一个字节的110和第二个字节的10去掉,我们就得到了"00001 101010",再把各位对齐,补上前导的0,就得到了"0000 0000 0110 1010",不好意思,这是UNICODE的006A,也就是小写的字母"j",而之后的两字节用UTF8解码之后是0368,这个字符什么也不是。这就是只有"联通"两个字的文件没有办法在记事本里正常显示的原因。

而如果你在"联通"之后多输入几个字,其他的字的编码不见得又恰好是110和10开始的字节,这样再次打开时,记事本就不会坚持这是一个utf8编码的文件,而会用ANSI的方式解读之,这时乱码又不出现了。



好了,终于可以回答NICO的问题了,在数据库里,有n前缀的字串类型就是UNICODE类型,这种类型中,固定用两个字节来表示一个字符,无论这个字符是汉字还是英文字母,或是别的什么。

如果你要测试"abc汉字"这个串的长度,在没有n前缀的数据类型里,这个字串是7个字符的长度,因为一个汉字相当于两个字符。而在有n前缀的数据类型里,同样的测试串长度的函数将会告诉你是5个字符,因为一个汉字就是一个字符。

2009年4月10日星期五

libsvm使用心得

Libsvm使用心得
最近在做基于SVM的短信分类的项目,对libsvm的使用进行了小小研究,结合网上泛滥成灾的libsvm使用方法介绍,自己做一简短总结。
libsvm是实现svm的便捷开源工具,应用广泛(除此之外还有lightsvm,没用过)由国立台湾大学Chih-Chung Chang和 Chih-Jen Lin编写,可以实现基于SVM的分类和回归。
由于个人对SVM的理论只是“略懂”,下面只介绍libsvm在win32平台的基本使用方法 。对SVM一窍不通的强烈建议看一下入门文章http://ntu.csie.org/~piaip/svm/svm_tutorial.html

先介绍一下大概的流程。
准备数据集(短信语料),处理成libsvm接受的格式,之后进行训练(svm-train)得到模型
,然后进行测试,完成。其中训练的过程需要不断选取参数寻求最佳分类结果,为此libsvm提供了grid.py(python文件)专门用来帮助自动选取最佳参数。


1、资源准备
下载Libsvm、Python和Gnuplot:
libsvm——那必须有啊,最新版本2.89,主页http://www.csie.ntu.edu.tw/~cjlin/libsvm/上下载得到,建议同时下载一个libsvm的初学者guide
Python——主要是为了运行grid.py,最新版是2.5,可以在python的主页http://www.python.org/上下载
Gnuplot——同样为了选取最佳参数和绘图,自己搜一下,win32版的为gp423win32.zip

2、具体流程

LIBSVM 使用的一般步骤是:
1)准备数据集,转化为 LIBSVM支持的数据格式 :

2009年4月9日星期四

SVM解决多分类问题的方法

SVM算法最初是为二值分类问题设计的,当处理多类问题时,就需要构造合适的多类分类器。目前,构造SVM多类分类器的方法主要有两类:一类是直接法,直接在目标函数上进行修改,将多个分类面的参数求解合并到一个最优化问题中,通过求解该最优化问题“一次性”实现多类分类。这种方法看似简单,但其计算复杂度比较高,实现起来比较困难,只适合用于小型问题中;另一类是间接法,主要是通过组合多个二分类器来实现多分类器的构造,常见的方法有one-against-one和one-against-all两种。
a.一对多法(one-versus-rest,简称1-v-r SVMs)。训练时依次把某个类别的样本归为一类,其他剩余的样本归为另一类,这样k个类别的样本就构造出了k个SVM。分类时将未知样本分类为具有最大分类函数值的那类。
b.一对一法(one-versus-one,简称1-v-1 SVMs)。其做法是在任意两类样本之间设计一个SVM,因此k个类别的样本就需要设计k(k-1)/2个SVM。当对一个未知样本进行分类时,最后得 票最多的类别即为该未知样本的类别。Libsvm中的多类分类就是根据这个方法实现的。
c.层次支持向量机(H-SVMs)。层次分类法首先将所有类别分成两个子类,再将子类进一步划分成两个次级子类,如此循环,直到得到一个单独的类别为止。
对c和d两种方法的详细说明可以参考论文《支持向量机在多类分类问题中的推广》(计算机工程与应用。2004)
d.其他多类分类方法。除了以上几种方法外,还有有向无环图SVM(Directed Acyclic Graph SVMs,简称DAG-SVMs)和对类别进行二进制编码的纠错编码SVMs。

2009年4月8日星期三

Java技术小结

作为一个学C(C++)出身的人,虽然对Java接触不久,但越发感到还是Java更适合自己,也许自己更习惯于所谓的“拿来主义”吧。
下面总结一下近期项目用到的Java技术。


Tips
  1. 文件路径,比如语句 FileReader fr = new FileReader("C:\\input.txt"); 可以用"\\","/","//" "///",多的没再试……
  2. 词频统计通常要用到HashMap,关于Map需要进一步研究,以后单独进行详细讨论;另外关于各种map:如果要效率,用Hashmap ; 如果不想改变put进去的顺序,请用LinkedHashMap; 如果要按某种规则排序,用TreeMap. 如果有更BT的要求,自己实现Map.
  3. 对文件读写和缓冲流读写操作,注意最后关闭 xxx.close()
  4. 注意各种“数据类型”之间的转换。比如String,obj,Integer,int(准确地说,前三者都是类,只有int是内置数据类型)

工作小结

近一周工作小结
目标:基于SVM的短信分类器
主要任务:分词,SVM程序


分词采用中科院分词开源程序ictlas4j(by sinboy,作者blog地址:http://blog.csdn.net/sinboy/category/207165.aspx),初步调试未发现bug。

SVM
SVM(Support Vector Matchine)是近年来在机器学习、自然语言处理领域广泛使用、效果杰出的分类方法,libsvm是国立台湾大学林智仁教授开发的基于SVM的开源工程(http://www.csie.ntu.edu.tw/~cjlin/),SVM的理论并不简单,但是使用libsvm并不困难,推荐经典入门指南http://ntu.csie.org/~piaip/svm/svm_tutorial.html
最近发现,对于训练样本与特征维数都比较大(比如数量级上万)的分类问题(大规模文本分类),应用线性核函数的svm更合适(准确率稍高,时间和空间比较爽),对应的工具是
LIBLINEAR(http://www.csie.ntu.edu.tw/~cjlin/liblinear/

目前正在用JAVA进行程序实现,思路已经明确,希望尽快完成 :)