转到正文

宁静海

发现,理解,提升

 

最近在寻找一些好的建模工具,用来把玩一下。搜索了一圈又一圈,发现常见的就那么几个。

 

PowerDesigner

ERWin

Rational Rose

Visio

MySQL Workbench

怎么说呢,曾经有朋友提到过PowerDesigner似乎用的人多些,Rose实在贵,估计也就大公司会想到用,不实际。都是收费的反正,根据公司大小也许会选择有所不同,仁者见仁的吧。

最后选择的是MySQL Workbench,关键是因为免费的,而且画的图不难看。也许可以用PowerDesigner导入次看看效果什么的。

目前的数据库选择范围似乎有点固定,不是MySQL就是Oracle,Sybase的名字很少听见了。前段时间还有人推荐个免费UML工具叫做BOUML,看了下screenshot,实在难看,哎,唯一吸引人的估计就是免费了。对于数据库设计来说,似乎不是太有针对性,类图用他画应该也可以。但是感觉出来的东西不够专业,很难有说服力呀。

最终花了很大力气,把PowerDesigner拉下来装好,发现的确是个好东西,可以作的图的种类很全,成品样子也很不错,也难怪搜索结果大多是说的他了,Rose找不到最新版本的下载,但是对于IBM的一贯庞大作风实在是不敢去试。

我的概念是,一个好的工具不但要好用,画出来的图也要显的专业美观,这样才能有说服力不是嘛。纠结的结果就是PowerDesigner暂时胜出,看来要顺手用下ERWin的感觉,又要用盗版的东西,很不爽。。。。

经常进行Http请求本质分析,本文将一些奇怪的难点总结一下,不定期更新:

Accept-Language问题:

在普遍概念上,发出request的语言类型都是系统默认的Zh-cn,似乎没有什么问题,但是碰到了个超变态的网站,竟然检查Accept-Language类型,排除加密算法和用户名密码之类常见问题之后只是以猜测的方式改了这个值,真TMD坑爹,竟然成功返回正常response了。棒子啊棒子,叫我说什么好。不过也许还有更坑爹的是样本请求竟然就是Zh-cn,还TMD是成功的,只能说世界很神奇了。

Cookie传递问题:

在做样本请求时,一定要清空Cookie,某次的项目中的网站竟然会判断客户端的Cookie值是否正确,等于有几个来回的交互都把Cookie的值算进去了。所以在觉得一切都正常,但是就是不返回正确response的

 

在Webservice项目中,对一个普通POJO定义需要进行序列化处理,以便给其他客户端使用。在Java端用Date类型时,往往使用java.sql.Date,这在进行XSD生成时报错

 

error: java.sql.Date does not have a no-arg default constructor

 

这说明JAXB无法正确的序列化java.sql.Date类型,常见的处理方法是改数据类型,改成java.util.Date。本文提供另外一种解决方式,那就是使用XML的Annotation,添加额外的Adapter来转换Date类型。

在需要使用java.sql.Date类型的那个方法上方添加@XmlJavaTypeAdapter,参数为Adapter实现类。

 

@XmlJavaTypeAdapter(SQLDateAdapter.class)
public Date getSomeSqlDate() {
    return someSqlDate;
}

 

改Adapter实现类继承自XmlAdapter

 

import java.util.Date;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class SQLDateAdapter extends XmlAdapter<Date, java.sql.Date> {

@Override
public Date ma

 

科普了,好久不用真的忘记光了

google出来的CSDN参考地址

 

主要是2个参数

int argc和数组 char *argv[]

前者代表参数数量,后者代表参数内容,关键是第一个参数包含了被调用的函数本身。

参考代码(狗血的是有问题的代码竟然是段C,还不是C++)

 

 

#include <stdio.h>

int mai

cat /proc/version
cat /etc/redhat-release
cat /etc/issue

在项目中碰到个问题,想要建立一个反向的键值对关系,什么叫反向的键值对关系类?先看如下的需求。

旧项目中有很多很多表,每个表的机构类似,但是字段的类型不同,有可能是varchar2,有可能是Number,也有可能是Char或者Int,但是我们需要在一个Config表中来描述这些表,有个data_type的字段,在系统中只定义了Number,String,Date等广泛意义的类型定义,从字典表中可查询到字段类型,但是哪几种字段类型算是String,哪几种算是Number呢?这就构成一种多对1的关系。问题也就产生了,传统意义上的Map都是一个键对应多个值,比如:

 

Map<String, List<String>> map

在我们这个Map中,按照我们的需求,就是根据List中的value获得Key,当然,我们假设List中的值全部都是唯一的,更精确的描述应该是一个Set<String>,当然这只是为了方便,使用的List。

 

 

搜索了一圈google,本以为有现成的反向map实现,但似乎没有。看来只能自己实现下了。代码如下。

 

 

package com.catpaw.utils.collection;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class MapUtil<K, V> {

public K getKeyFromValue(Map<K, V> sourceMap, String value) {
if (value == null) {
return null;
}
Set<Entry<K, V>> mapEntries = sourceMap.entrySet();
for (Entry<K, V> mapEntry : mapEntries) {
if (value.equals(mapEntry.getValue())) {
return mapEntry.getKey();
}
}
return null;
}

public K getKeyFromListValue(Map<K, List<V>> sourceMap, String value) {
if (value == null) {
return null;
}
Set<Entry<K, List<V>>> mapEntries = sourceMap.entrySet();
for (Entry<K, List<V>> mapEntry : mapEntries) {
List<V> values = mapEntry.getValue();
for (V val : values) {

 

一直以来我都认为,HttpURLConnection是Java中对网络相关原生的实现,可以说用他来模拟做Http请求就是官方版本的 HttpClient实现。个人比较崇尚官方的东西,但是在使用中发现HttpURLConnection的应用很容易让人造成困扰,本文对该部分技术的 使用经验做一定的总结,以作备忘之用。

 

关于流的使用

 

 

conn.getOutputStream()
conn.getInputStream()

在使用输入输出流的时候,往往会错误的认为

conn.getOutputStream()调用时会相应的发出请求,而在conn.getInputStream()用来读取服务器响应。但是,事实是:

请求只在conn.getInputStream()被调用时才会被发出

这个过程很让人郁闷,因为这样直接导致了编码时流程上控制的混乱。

因为以前一直认为流的操作往往时随着代码的执行而同步传输的,但是在这里情况似乎不是这样。

 

关于参数传递

在发送HTTP请求时,我们常用的都是GET和POST,一般会用conn.setRequestMethod来控制发送的类型,在默认情况下使用的是GET

我常犯的一个错误就是在conn.setRequestMethod(“GET”);后,再次调用conn.setDoOutput(true), 这个方法会导致发出去的请求类型变成POST,而忽略setRequestMethod。这个原因在于,setDoOutput等于告诉了JVM需要进行 数据的传递,而这个传递是在请求正文中的(该段说明可能不是非常正确,还需更进一步推敲,因为我认为GET请求其实也是在请求正文中的)

 

关于Cookie解析

在模拟请求中Cookie的功能不言而喻,因为在 一些站点上Cookie的值会随同请求一起发出,因此当服务器返回Cookie时,一定要全部抓住保存下来,那么Cookie在保存时应该保存成什么样的 数据格式就值得探讨了。众所周知的是Cookie是key-value的键值对,但是在真实的请求头中,全部都是纯文本,以下是一段Cookie在 Response中的例子。

 

session-id=-; path=/; domain=.www.amazon.cn; expires=Tue, 15-Jun-1999 16:49:18 GMT

 

很明显是一段session-id的本地化保存。由于每个cookie都有3个属性

path

domain

expires

这3个属性代表了cookie的作用域。

根据这个作用域,我设计了如下的数据结构来存储所有的cookie

 

Map<String, Map<String, String[]>>

KEY的String代表Domain,因为cookie的主要作用范围是以Domain来划分的,而VALUE也是个Map

 

他的Key代表属性名,其Value代表的是该属性的path, domain,expires,我认为这个结构很好的描述了cookie的结构。

以下一段将cookie的长串字符串,写入该map的一段方法。算法有待改进。

 

private void adaptCookieStr(String setCookieStr) {
    System.out.println(setCookieStr);
    String[] cookieValueEquals = setCookieStr.split(";\\s*");
    String[] cookieValueArray = new String[cookieValueEquals.length];
    String domain = "";
    String cookieName = "";

    for (int i = 0; i < cookieValueEquals.length; i++) {
        String cookieValueEqual = cookieValueEquals[i];
        String[] eqArray = cookieValueEqual.split("=");
        if ("domain".equalsIgnoreCase(eqArray[0])) {
            domain = eqArray[1];
        }
        if (!"domain".equalsIgnoreCase(eqArray[0]) && !"expires".equalsIgnoreCase(eqArray[0]) && !"path".equalsIgnoreCase(eqArray[0])) {
            cookieName = eqArray[0];
        }
        cookieValueArray[i] = eqArray[1];
    }
    if (cookieMap.get(domain) != null) {
        cookieMap.get(domain).put(cookieName, cookieValueArray);
    } else {
        Map<String, String[]> cookieValueMap = new LinkedHashMap<String, String[]>();
        cookieValueMap.put(cookieName, cookieValueArray);
        cookieMap.put(domain, cookieValueMap);
    }
}

 

fileserve :

http://generatory.3xg.pl/fileserve

filesonic

http://generatory.3xg.pl/filesonic

megaupload :

http://generatory.3xg.pl/megaupload

 首先,根据编码格式的不同,文本的编码分成2种:

 ASCII 和UNICODE。

 计算机字符一个字符由8位(bit)组成,单字符称为1字节(byte)。

 ASCII字符实际站用7位,首位恒为0。(左边第一位为首位)

 因此,对于其他种类的字符,就必须使用其他的编码,替代方案就是UNICODE

 这里仅仅简单提几点UNICODE的概念,详细参考Wiki的定义。

 首先UNICODE只是个编码标准,不包括实现,他只是一类编码的统称。

 其次UNICODE将代码影射到17个平面上分别是0-16,这种影射称为字符平面影射

 我们最常使用的汉字字符位于第0平面(Plane 0),该平面称为基本多文种平面


 “Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。”

 也就是说,Unicode定义了字符与编码的关系,但当编码转换成2进制的时候,长度是不确定的,可能达到甚至超过常规的1-2个字节(数字英文1个字节8位,中文日文2个字节需要16位),但其他国家字符可能跟多。

 因此会造成2个问题:

 1、程序拿到文本时,无法确定到底几个字节算是一个有意义的字符。

 2、对于存储纯英文或数字字符时只需要1个字符8位,强行定义固定字符个数来解析时将会造成空间浪费。

 要解决这个问题才引出了UTF字符集。


 Unicode只是个标准,而UTF字符集则是该标准的具体实现。我们常用的UTF-8字符集就是最典型的实现。

 UTF-8字符集是可变字符长度的,长度范围在1-4字节内

 编码规则:

 1个字节

 以0开头

 2个字节

 以110开头,第二个字节以10开头

 3个字节

 以1110开头,后面每个字节以10开头

 4个字节

 以11110开头,后面每个字节以10开头

 对应范围如下表:

Unicode范围 编码格式
 000000 – 00007F    (128个代码)  0xxxxxxx      
 000080 – 0007FF    (1920个代码)  110xxxxx  10xxxxxx    
 000800 – 00D7FF 00E000 – 00FFFF (61440个代码)  1110xxxx  10xxxxxx  10xxxxxx  
 000800 – 00D7FF 00E000 – 00FFFF (61440个代码)  11110xxx  10xxxxxx  10xxxxxx  10xxxxxx

 

 以下有个比较说明问题的编码例子:

 瀚 字:Unicode的16进制编码为 701A

 转换成2进制为

7,0,1,A

0111,0000,0001,1010

从表中可知,701A的范围在第三行中,所以该字符以UTF-8编码时存储为3个字节

依次填充就是

11100111,10000000,10011010

(注意左边补0位也要填入)

再次转换成16进制就是

E7809A

这个就是该字符的UTF-8编码


大小头问题(Big Endian & Little Endian)

这个概念的意义是决定一个字符的字节在内存中存储的顺序。

比如“瀚”字是  701A,这个是大头的存法(为什么是大头我下面说明),如果需要直接存储那就是2个字符,70和1A

大头存法就是

70,1A

小头存法就是

1A,70

在UNICODE规范中规定,使用零宽非换行空格(ZERO WIDTH NO-BREAK SPACE)来判断该文件使用哪种存储方式

FEFF表示用BE

FFFE表示用LE

在Windows自带的工具中有记事本,可以证明701A是大头存法,验证方法如下:

1、打开记事本,输入“瀚”字

2、保存,在保存类型里选择“Unicode Big endian”,然后确定保存。

3、使用UltraEdit打开这个文件,使用16进制模式可以看到

证明701A就是大头存法

顺便补张图证明”瀚“字的UTF-8计算结果正确(保存为UTF-8格式)

UTF-8文件的文件头用EF BB BF表示

 

本文主要阐述了ASCII和Unicode的区别,需要注意的是,本文文中强调过UTF-8是Unicode的实现,在Windows中这点就很明显,在记事本里就有保存为Unicode/Unicode Big 和UTF-8的3种选择,我认为,记事本中的Unicode和Unicode Big是Windows下的Unicode实现,该实现将字符以Unicode编码,并规定编码长度为16位。这里不作展开,因为该实现与Windows下的VC编程有一定交集。

另外,关于中文编码和本文提到的ASCII以及Unicode(UTF-8)的区别,我会再另开文章说明。

本文参考自博客文章

http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

相关链接

 unicode.org

汉字Unique对照表(大头)

Unicode Wiki EN 

Unicode Wiki CN 

Unicode 字符平面映射