2008 年 3 月 13 日
DB2 V9 中非常重要的一个新特性就是对 XML 的支持,我们可以将结构良好的 XML 文档以分层结构形式进行存储和访问。在全球化软件产品中,我们需要在 XML 中存储一些多字节字符,并以正确的编码信息处理它们。本文将从实例来分析如何处理 DB2 中 XML 类型编码,并学习 XML 内部编码,外部编码,BOM 等概念以及操作 DB2 中 XML 数据的一些基本方法。
通常处理 XML 的数据源来自文件,那么如何以正确编码读取文件成为问题的关键。如果开发人员已经事先确定了文件的编码类型,那么这个问题就不存在了,但是很多情况下需要根据文件本身来判断这个文件的编码。我们来看如何获取一个XML文件的编码信息。
一个XML文件中可能包含编码信息的地方有三处:
- BOM(Byte Order Mark)。如果这个文件是 unicode 编码,那么 BOM 就可能出现在 XML 文件的开始部分。在 XML 中,BOM 不仅用于显示输入文本流的字节顺序,而且可以帮助检测字符编码方式。BOM 与编码的对应关系如表
BOM 值 编码 X'EFBBBF' UTF-8 X'FEFF' UTF-16BE X'FFFE' UTF-16LE X'0000FEFF' UTF-32BE X'FFFE0000' UTF-32LE - XML 修饰行编码:XML 修饰行所用的编码。如果 XML 文件中包含修饰行,比如
<?xml version="1.0"?>,那么我们可以根据前两个字符的编码编码来判断文档采用的编码信息。是 ASCII 码,UTF-8,UTF-16BE, UTF-16LE, 还是UTF-32? - XML 修饰行中编码属性:XML 修饰行中的 encoding 的属性值。通常在 XML decoration 中会带有编码信息,比如
<?xml version="1.0" encoding="UTF-8"?>。
那么我们对XML文件处理之前,就可以从这三处得到文件的编码。如果得到的编码彼此矛盾,比如 BOM 显示文件是 UTF-8 编码,而 XML 修饰行中显示<?xml version="1.0" encoding="UTF-8"?>, 那么我们就报错;如果从这几个地方都得不到编码信息,那么按照缺省值 UTF-8 来处理。Java 代码如清单 1.
清单 1. Get XML encoding from file
try{
//step 1: read file to byte array
File file = new File(filename);
int size = (int)file.length();
FileInputStream fis = new FileInputStream(file);
byte[] buff = new byte[size];
int readed = 0;
while (readed < size)
readed += fis.read(buff, readed, size-readed);
String BOM=null;
String Dec=null;
String Enc=null;
String Encoding = null;
boolean hasdec = false;
//step 2: get encoding from BOM if exist
int dec_start = 0;
int b0 = buff[0] & 0xFF;
int b1 = buff[1] & 0xFF;
int b2 = buff[2] & 0xFF;
if (b0 == 0xFE && b1 == 0xFF){
BOM = "UTF-16BE";
Encoding = BOM;
dec_start = 2;
}
else if (b0 == 0xFF && b1 == 0xFE){
BOM = "UTF-16LE";
Encoding = BOM;
dec_start = 2;
}
else if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF){
BOM = "UTF-8";
Encoding = BOM;
dec_start = 3;
}
//step 3: get encoding from xml decoration
int d0 = buff[dec_start] & 0xFF;
int d1 = buff[dec_start+1] & 0xFF;
int d2 = buff[dec_start+2] & 0xFF;
int d3 = buff[dec_start+3] & 0xFF;
if (d0 == 0x3C && d1 == 0x3F){ //ascii encoding of "<?"
Dec = "ASCII";
if (Encoding!=null && Encoding.compareTo("UTF-8")!=0)
System.out.print("Conflict");
if (Encoding == null)
Encoding = Dec;
hasdec = true;
}
else if (d0 == 0x3C && d1 == 0x00 && d2== 0x3F &&
d3 == 0x00){ //UTF-16 Little Ending encoding of "<?"
Dec = "UTF-16LE";
if (Encoding!=null && Encoding.compareTo("UTF-16LE")!=0)
System.out.print("Conflict");
if (Encoding == null)
Encoding = Dec;
hasdec = true;
}
else if (d0 == 0x00 && d1 == 0x3C && d2== 0x00 &&
d3 == 0x3F){ //UTF-16 Big Ending encoding of "<?"
Dec = "UTF-16BE";
if (Encoding!=null && Encoding.compareTo("UTF-16BE")!=0)
System.out.print("Conflict");
if (Encoding == null)
Encoding = Dec;
hasdec = true;
}
//step 4: get encoding from decoration encoding
if (hasdec){
int i = dec_start;
while ((buff[i]&0xFF) != 0x3E && i<buff.length)
i++;
if (i==buff.length){
System.out.print("bad file.");
return "";
}
String decoration = new String(buff, dec_start, i-dec_start, Dec);
int pos = decoration.indexOf("encoding");
if (pos!=-1){
while (pos<decoration.length() && decoration.charAt(pos)!
='"' && decoration.charAt(pos)!='\'')
pos++;
if (pos == decoration.length()){
System.out.print("bad file.");
return "";
}
char quot = decoration.charAt(pos);
int encstart = ++pos;
while (pos<decoration.length() && decoration.charAt(pos)!=quot)
pos++;
if (pos == decoration.length()){
System.out.print("bad file.");
return "";
}
Enc = decoration.substring(encstart,pos);
if (Encoding.compareTo(Enc)!=0 && Encoding.compareTo("ASCII")!=0){
System.out.print("bad file.");
return "";
}
Encoding = Enc;
}
if (Encoding.compareTo("ASCII")==0)
Encoding = "UTF-8";
}
System.out.println("Encoding:"+Encoding);
return new String(buff, dec_start, buff.length-dec_start, Encoding);
}catch(Exception e){
e.printStackTrace();
}
|
|
本文只以 Java 程序为例。当从程序中向 DB2 XML 列中存储数据时,有两种方式,这两种方式采用的编码处理方式有所不同。以字符数据类型对 XML 列赋值,比如setString(),这种情况下,传给 DBMS 的数据所使用的编码是由 Java 语言本身所决定的 (UTF-16),这种方式称之为外部编码。在这种情况下,XML 数据本身所包含的内部编码将被忽略。我们使用一些例子来验证一下,首先来看使用外部编码来存储两个使用不同编码的 XML 文件。
