之前没整过读写DBF文件这类的东西,也不太明白这个到底是什么原因,跟了一下代码,发现写进去的数据没有乱啊,而且编码设置的GBK,写中文也不会乱码啊,都很正常。纠结了很长时间,从网上找了很多读写DBF的例子,跟我项目的也大相径庭,于是又将javadbf-0[1].4.0.jar这个里面的各个类反编译了,研究了一番,也没发现什么蛛丝蚂迹对我有帮助..............
然后我发现导出的DBF文件是从某一列以后开始产生数据错位的,该DBF总共有5列:目录号、文件号、题名、总页数、负责人。本该属于题名的列数据全都跑到总页数里面去了,后面的各列数据也都往后偏移一列。于是查看了导出的DBF文件的表结构,居然意外的发现题名那一列的宽度是0,检查代码,在代码里写进去的列宽度分别是20、30、512、30、20,因为考虑到题名字段的内容比较多,所以故意将其长度设的比较大,可是不明白为什么他会变成0呢,看了DBFField.java的setFieldLength方法,如果传进去的时候就是0的话,他应该会打印Field length should be a positive number这一句话的,
public void setFieldLength(int value)
{
if (value <= 0)
{
throw new IllegalArgumentException("Field length should be a positive number");
}
if (this.dataType == 68)
{
throw new UnsupportedOperationException("Cannot do this on a Date field");
}
this.fieldLength = value;
}
没有打印,而且我这里是setFieldLength(titleLength),titleLength的值是动态传入的,打断点看到的是512,可确实是到了DBF文件里,题名的列宽度就变成了0,为什么呢?
于是写了1024,导出的DBF题名一列还是0,写了2048,也变成了0,于是以为凡是这个长度能整除1024,或者1024能整除这个数,就会在DBF文件里变成0,又多试了几次,写了个513,导出的DBF里宽度是1,而且中文全是乱码,写514,导出的DBF里宽度是2,写256,导出的DBF里宽度是0,写257, 导出的DBF里宽度是1,后来反复研究发现,DBF文件里的宽度取的是setFieldLength(titleLength)里面传进去的titleLength%256的值,因为1024%256==0;2048%256==0;256%256==0;513%126==1;257%256==1;514%256==2,也就是说你在setFieldLength写256和512是相同的结果。
于是心想应该是javadbf本身的问题,想试图修复这个问题,但是在javadbf的各个类里没有找到任何可能导致此问题的痕迹,因为在各个类里根本没有找到255这个数字。最后想着只能是在自己的代码里去避免这个问题了,那可是如果我题名那一列确实数据比较多我就要给他写列宽为512咋办,心想可不可以手动的在VFP里建一个文件将其宽度设为512呢,于是一试才发现,通过VFP里面的工具向导---表向导建表的时候,通过它自己增加和减小按钮列宽度增大254,再点向上按钮,增大就没变化买反映了,手动的在宽度框里输了个255,右上角立刻提示范围:1-254。于是差不多清晰了,应该是DBF文件里宽度最大允许设置到254,但是我可以通过代码写入255,这应该是极限了,所以传过去的大于256的数字就解析成了对256取余的结果了。
于是我只能是去在往DBF文件写数据的代码里去避免了,我是这样处理的,将设置宽度的代码修改为:
if(titleLength>=256){
fields[i].setFieldLength(255);
}else{
fields[i].setFieldLength(titleLength);
}
将写值的地方修改为
if(ob.toString().length()>=256){
if(ob.toString().matches("^[\u4e00-\u9fa5]{0,}$")&&ob.toString().length()>=127){//如果是中文,就只要前127个字
rowData[j] = ob==null?"":ob.toString().substring(0,127);
}else{
rowData[j] = ob==null?"":ob.toString().substring(0,255);
}
}else{
rowData[j] = ob==null?"":ob.toString();
}
然后就OK了,算是解决了。
随手写了个test.java,如下:
package cn.welcome.test;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import com.linuxense.javadbf.DBFBase;
import com.linuxense.javadbf.DBFField;
import com.linuxense.javadbf.DBFReader;
import com.linuxense.javadbf.DBFWriter;
public class test {
private static FileInputStream inputStream;
private static DBFReader reader; //读取dbf文件对象
private static int numberOfFields;
private static int numberofRecord;
public static void main(String args[]) throws IOException{
DBFField fields[] = new DBFField[3];
fields[0] = new DBFField();
fields[0].setName("姓名");
fields[0].setDataType( DBFField.FIELD_TYPE_C);
fields[0].setFieldLength(10);
fields[1] = new DBFField();
fields[1].setName("年龄");
fields[1].setDataType( DBFField.FIELD_TYPE_N);
fields[1].setFieldLength(768);
// fields[2] = new DBFField();
// fields[2].setName("性别");
// fields[2].setDataType( DBFField.FIELD_TYPE_N);
// fields[2].setFieldLength(12);
// fields[2].setDecimalCount(2);
fields[2] = new DBFField();
fields[2].setName("性别");
fields[2].setDataType( DBFField.FIELD_TYPE_C);
fields[2].setFieldLength(12);
DBFWriter writer = new DBFWriter();
writer.setFields(fields);
Object rowData[] = new Object[3];
rowData[0] = "1000";
rowData[1] = "John";
rowData[2] = "111";
writer.addRecord(rowData);
rowData = new Object[3];
rowData[0] = "1001";
rowData[1] = "检查数据检查数据检查数据检查数据检查过后就会恢复的";
rowData[2] = "222";
writer.setCharactersetName("GBK");
writer.addRecord(rowData);
rowData = new Object[3];
rowData[0] = "1002";
rowData[1] = "Rohit";
rowData[2] = "333";
writer.addRecord(rowData);
writer.write(new FileOutputStream("D:\\creatDBF.dbf"));
inputStream = new FileInputStream("D:\\creatDBF.dbf");
reader = new DBFReader(inputStream);
reader.setCharactersetName("GBK");
numberOfFields = reader.getFieldCount();
numberofRecord = reader.getRecordCount();
for(int i = 0;i < reader.getFieldCount(); i++){
DBFField dbf = reader.getField(i);
// System.out.println(dbf.getName());
}
Object[] rowValue;
while((rowValue = reader.nextRecord())!=null){
for(int i = 0 ; i < rowValue.length ; i++){
// System.out.println(rowValue[i]);
}
}
}
}
如上fields[1].setFieldLength(768);打开D盘根目录下的creatDBF.dbf文件发现年龄一列宽度是0,
写成257,或者259,creatDBF.dbf里面的数据都出现乱码,写258,发现第二列数据出现错位,而且第三列数据丢失。写成255就好了,没有问题了。