`

使用JAVA写出DBF出现乱码和数据错位

阅读更多

      最近在做项目的时候,发现有一个导出数据成DBF文件的功能的异常。导出的DBF文件用VFP或者打开,发现数据错位的现象,本该属于前一列的数据跑到后面那一列去了,这是问题一。还有一个问题,有的数据导出去以后出现了乱码,而且数据错位很严重,根本没有一条数据可以看的。而且tomcat是不报任何错误的。
      之前没整过读写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就好了,没有问题了。
 

分享到:
评论
2 楼 WH_LPWH 2015-09-30  
     哥呀 牛逼 大赞一个
1 楼 mengqingyu 2012-05-14  
遇到同样的问题,受教了!

相关推荐

Global site tag (gtag.js) - Google Analytics