当前位置: 首页 > 工具软件 > JavaDBF > 使用案例 >

DBF文件使用JAVA读写解决方案

张财
2023-12-01

DBF文件介绍

.dbf是一种特殊的文件格式,表示数据库文件,Foxbase,Dbase,Visual FoxPro等数据库处理系统所产生的数据库文件!

常见的数据库文件有:CSV 逗号分隔的值文件;DAT 数据文件;WrodPerfect合并数据文件;DB Borland的Paradox 7表;DBC Microsoft Visual FoxPro数据库容器文件。DBF dBASE文件,一种由Ashton-Tate创建的格式,可以被ACT!、Lipper、FoxPro、Arago、Wordtech、Xbase和类似数据库或与数据库有关产品识别,可用数据文件(能被Excel 97打开),Oracle 8.1.x表格空间文件;MDB是access文件;NSF Lotus Notes数据库;MDF和LDF是SQL SERVER文件;

DBF文件虽然比较古老,但目前仍在一些证券交易所用来进行文件交换,本文研究使用JAVA方式对DBF文件进行读写操作。

DBF文件具有固定的记录格式和数据类型,具体可百度。

本文提供两种方式:

1)使用javadbf开源组件

2)使用jdbc-odbc方式桥接微软foxpro驱动

使用javadbf开源组件

使用javadbf组件读写实现简单,只需依赖javadbf包。使用maven方式:

        <dependency>
            <groupId>com.github.albfernandez</groupId>
            <artifactId>javadbf</artifactId>
            <version>1.13.2</version>
        </dependency>

DbfWriterAndReadUtil实现:

import com.linuxense.javadbf.DBFDataType;
import com.linuxense.javadbf.DBFField;
import com.linuxense.javadbf.DBFReader;
import com.linuxense.javadbf.DBFWriter;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.File;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class DbfWriterAndReadUtil {

    /**
     * 创建dbf空文件,
     * @param path:文件路径
     * @param fieldList:dbf字段,需要设定name,type,length这些参数,可以参考从标准dbf文件中读取出来的样式。
     * @param charsetName 编码字符集
     * @throws IOException
     */
    public static void createDbf(String path, List<Map<String, String>> fieldList, String charsetName)
            throws IOException {
        DBFField[] fields = new DBFField[fieldList.size()];
        int index = 0;
        for (Map<String, String> fieldMap : fieldList) {
            DBFField field = new DBFField();
            field.setName(fieldMap.get("name"));//字段名称
            field.setType(DBFDataType.CHARACTER);//指定字段类型为字符串
            field.setLength(Integer.valueOf(fieldMap.get("length")));//指定长度
            fields[index] = field;
            index++;
        }
        //定义DBFWriter实例用来写DBF文件
        DBFWriter dbfWriter = new DBFWriter(new FileOutputStream(path), Charset.forName(charsetName));
        //设置字段
        dbfWriter.setFields(fields);
        //写入dbf文件并关闭
        dbfWriter.close();
    }


    /**
     * 获取字段名
     * @param path
     * @param charsetName
     * @return
     * @throws IOException
     */
    public static String[] getFieldName(String path, String charsetName) throws IOException {
//		InputStream fis = new FileInputStream(path);
        DBFReader dbfReader = new DBFReader(new FileInputStream(path), Charset.forName(charsetName));

        int fieldCount = dbfReader.getFieldCount();//获取字段数量
        String[] fieldName = new String[fieldCount];
        for (int i = 0; i < fieldCount; i++) {
            fieldName[i] = dbfReader.getField(i).getName();
        }
        dbfReader.close();
//		fis.close();
        return fieldName;
    }


    /**
     * 使用读取dbf文件作为模板,写dbf文件
     * @param pathRead:dbf文件头模板
     * @param pathWriter:dbf写文件路径
     * @param rowList:要写入的记录行
     * @param charsetName:字符集
     * @throws IOException
     */
    public static void writeDbf(String pathRead, String pathWriter, List<Map<String, Object>> rowList, String charsetName)
            throws IOException {

        DBFReader dbfReader = new DBFReader(new FileInputStream(pathRead), Charset.forName(charsetName));
        //获取字段数量
        int fieldCount = dbfReader.getFieldCount();

        DBFField[] fields = new DBFField[fieldCount];

        for (int i = 0; i < fieldCount; i++) {

            fields[i] = dbfReader.getField(i);
        }
        File fileWriter = new File(pathWriter);

        DBFWriter dbfWriter = new DBFWriter(fileWriter, Charset.forName(charsetName));
        //需要先设置好fileds,本方法fields与读取的dbf文件相同,所以直接从读取dbf文件拿。

        //如果文件不存在,需要设置dbf文件字段头
        if(!fileWriter.exists()){
            dbfWriter.setFields(fields);
        }

        //获取字段
        String[] fieldName = getFieldName(pathRead, "GBK");

        for (Map<String, Object> rowMap : rowList) {
            Object[] rowData = new Object[fieldName.length];
            for (int i = 0; i < rowData.length; i++) {
                //根据字段来排列指,不然可能出现错位情况
                rowData[i] = rowMap.get(fieldName[i]);
            }
//			rowMap.values().toArray(rowData);
            //添加记录(此时并没有写入文件)
            dbfWriter.addRecord(rowData);
        }

        //写入dbf文件并保存关闭
        dbfWriter.close();
    }

    /**
     * 读dbf记录
     * @param path
     * @return
     * @throws IOException
     */
    public static List<Map<String, Object>> readDbf(String path, String charsetName) throws IOException {
        List<Map<String, Object>> rowList = new ArrayList<>();
//		InputStream fis = new FileInputStream(path);
        DBFReader dbfReader = new DBFReader(new FileInputStream(path), Charset.forName(charsetName));
        Object[] rowValues;
        while ((rowValues = dbfReader.nextRecord()) != null) {
            Map<String, Object> rowMap = new HashMap<String, Object>();
            for (int i = 0; i < rowValues.length; i++) {
                rowMap.put(dbfReader.getField(i).getName(), rowValues[i]);
            }
//			System.out.println(rowMap);
            rowList.add(rowMap);
        }
        dbfReader.close();
//		fis.close();
        return rowList;
    }
}

测试类DbfTestMaven,调用javadbf组件

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.util.List;
import java.util.Map;
import java.util.Scanner;


@RunWith(BlockJUnit4ClassRunner.class)
public class DbfTestMaven {

    private static final String CHARSET = "GBK";
    @Test
    public void test() {
        try {
//            DbfWriterAndReadUtil.createDbf("b.dbf", fieldList, "GBK");
//            DbfWriterAndReadUtil.writeDbf("b.dbf", rowList, "GBK");
            //DBF文件路径
           //String pathDbf = "D:\\testdbf\\";
            //DBF文件前缀
            String dbfFileRead = "D:\\testdbf\\testdbf.dbf";
            String dbfFileReadTxt = "D:\\testdbf\\testdbf.txt";

            String dbfFileWrite = "D:\\testdbf\\testdbf-out.dbf";


            //输出读取dbf文件内容到txt
            BufferedWriter dbfTxt = new BufferedWriter(new FileWriter(dbfFileReadTxt));

            String[] fieldName = DbfWriterAndReadUtil.getFieldName(dbfFileRead, CHARSET);
            for (int i = 0; i < fieldName.length; i++) {
                System.out.println(fieldName[i]);
            }

            List<Map<String, Object>> getRowList = DbfWriterAndReadUtil.readDbf(dbfFileRead, CHARSET);
            for (Map<String, Object> entity : getRowList) {
                System.out.println(entity);

                dbfTxt.write(entity.toString() + "\n");

            }

            DbfWriterAndReadUtil.writeDbf(dbfFileRead,dbfFileWrite,getRowList,CHARSET);

            dbfTxt.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

        使用javadbf按照DBF组件的方法去处理DBF文件就可以了,查看该组件的实现方式为按照DBF文件的组织格式进行读写,即将DBF文件按输入字节流处理,再按字段的占位读取具体属性内容。需要注意的是,如果新建一个DBF文件,需要先设置字段头,使用dbfWriter.setFields(fields);否则报没有DBF文件头错误。如果DBF文件已有字段头,无需再次设置,否则也会报错。

        该方式优点是使用比较简单,无过多依赖的其他配置和环境,并且可以直接新建DBF文件,但缺点是使用字节流的方式读写dbf文件,可能会造成文件读写不准确,在商业应用中使用需要谨慎。

使用jdbc-odbc方式桥接微软foxpro驱动

即使用JDBC-ODBC桥接微软的foxpro驱动,实现用java的JDBC方式读写DBF文件。将DBF文件视作JDBC操作的一个表。

JDBC

JDBC, 全称为Java DataBase Connectivity standard, 它是一个面向对象的应用程序接口(API), 通过它可访问各类关系数据库。JDBC也是java核心类库的一部分。

JDBC的最大特点是它独立于具体的关系数据库。与ODBC (Open Database Connectivity)类似, JDBC API 中定义了一些Java类分别用来表示与数据库的连接(connections), SQL语句(SQL statements), 结果集(result sets)以及其它的数据库对象, 使得Java程序能方便地与数据库交互并处理所得的结果。使用JDBC, 所有Java程序(包括Java applications , applets和servlet)都能通过SQL语句或存储在数据库中的过程(stored procedures)来存取数据库。

JDBC-ODBC

由于微软的foxpro数据库不是用java编写的,如果需要java连接,需要使用JDBC-ODBC桥连接foxpro驱动。方式为:

try{
Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”);
}catch(ClassNotFoundException e){}

这里,Class是包java.lang中的一个类,该类通过调用静态方法forName加载sun.jdbc.odbc包中JdbcOdbcDriver类来建立JDBC-ODBC桥接器。如果连接成功,就可以使用标准的JDBC方式读写DBF文件了。需要注意的是,新的DBF文件需要foxpro生成结构,再使用insert语句插入数据。

需要注意的有三点:

1)jdbc-odbc从JAVA1.8开始就去掉了,如果使用。需要将java1.7的jdbc-odbc的jar包和dll文件拷贝到jdk1.8

2)安装foxpro的驱动是32位的,如果使用64位的jdk,则读取的为64位环境的驱动,会导致找不到32位的foxpro驱动,所以需要将jdk换成使用32位的。

3)新的DBF文件需要使用foxpro客户端新建,并定义字段名、字段类型、长度等信息。这样才可以使用jdbc方式对该文件进行操作

foxpro客户端资源:

VisualFoxPro2.5-6.0_foxpro2.5-其它工具类资源-CSDN下载

foxpro驱动:

VisualFoxProODBC驱动_VisualFoxProODBC-其它工具类资源-CSDN下载

//DBF文件url连接
String DB_URL = "jdbc:odbc:Driver={Microsoft Visual FoxPro Driver}; SourceType=DBF;Exclusive=No; " +
"SourceDB=XXX";//XXX为DBF的目录

//获取连接,连接获取到后,就和一般的JDBC操作一致了,DBF文件看做为一个表。
conn = DriverManager.getConnection(DB_URL);

//3. 从dbf文件读取数据
pstm = conn.prepareStatement("select * from " + dbfName); // dbfName 为DBF数据文件的名字

rs = pstm.executeQuery();

完整的TEST代码

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;

import java.io.File;
import java.io.FileOutputStream;
import java.sql.*;
import java.util.Properties;


@RunWith(BlockJUnit4ClassRunner.class)
public class DbfFoxPro {

    @Test
    public void test() {
        System.out.println("test");

        Connection conn = null;

        PreparedStatement pstm = null;

        ResultSet rs = null;

        Statement stmt = null;

//下面的代码其实都是基本的jdbc代码,所以编写上面基本没什么问题

        //dbf文件路径
        String path = DbfFoxPro.class.getClassLoader().getResource("dbfFile").getPath();


        String DB_URL = "jdbc:odbc:Driver={Microsoft Visual FoxPro Driver}; SourceType=DBF;Exclusive=No; " +
                "SourceDB=" + path.substring(1);

        /**
         * 注意使用jdbc:odbc方式的环境配置问题,否则可能会报获取不到JdbcOdbcDriver、
         * connection为null、jdbcodbc找不到url里的驱动问题,需要解决两个问题:
         * 1)由于java1.8是没有jdbcodbc的,所以需要添加,需要将jdk1.7的JdbcOdbc.dll文件拷贝到jdk1.8的jre/bin目录中,
         * 还需要将jdbcodbc的jar包拷贝到jdk1.8的jre/lib中,也可以使用本项目的方式,在pom中添加本地jdbcodbc的依赖包。(jdbcodbc的jar包获取可参考https://blog.csdn.net/vdora/article/details/119870738)
         * 2)如果项目运行环境是jdk64位版本,由于FoxPro驱动比较老,默认是安装在odbc驱动32位版本里的
         * (32位在C:\Windows\SysWOW64\odbcad32.exe,64位在C:\Windows\System32\中,不知道为何),所以即使确认驱动已经安装,
         * 也会报找不到url中驱动的问题,解决方案就是使用jdk32位版本。
         */
        try {



            //获取JdbcOdbcDriver
            Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

            String dbfName = "dbfFox";

            try {
//
//                File file = new File("D:\\testdbf\\" + dbfName +".DBF");
//                file.createNewFile();

                Properties properties = new Properties();
                properties.setProperty("charSet", "GBK");
                //获取连接
                conn = DriverManager.getConnection(DB_URL,properties);

                //1. 新建dbf文件,即建立一个以dbf文件为名的表

                /*
                String createTableSql = "create table " + dbfName +" (" +
                        "name char(50)" +
                        ")";

                stmt = conn.createStatement();
                stmt.execute(createTableSql);


                pstm = conn.prepareStatement(createTableSql); // 此处的testDbf 为DBF数据文件的名字
                pstm.execute();

                */

                //2. 向dbf文件中插入数据
                String insertSql = "insert into " + dbfName + " values('测试dbf3')";// 此处的testDbf为DBF数据文件的名字

                pstm = conn.prepareStatement(insertSql);
                pstm.execute();


                //3. 从dbf文件读取数据
                pstm = conn.prepareStatement("select * from " + dbfName); // 此处的XXB 为DBF数据文件的名字

                rs = pstm.executeQuery();

                ResultSetMetaData metaData = pstm.getMetaData();

//展示dbf元数据信息

                System.out.println("metaData.getColumnCount():"+metaData.getColumnCount());

                System.out.println("**************************");

                for(int i = 1 ; i <= metaData.getColumnCount() ; i++){

                    System.out.println("metaData.getCatalogName:"+metaData.getCatalogName(i));

                    System.out.println("metaData.getColumnClassName:"+metaData.getColumnClassName(i));

                    System.out.println("metaData.getColumnDisplaySize:"+metaData.getColumnDisplaySize(i));

                    System.out.println("metaData.getColumnLabel:"+metaData.getColumnLabel(i));

                    System.out.println("metaData.getColumnName:"+metaData.getColumnName(i));

                    System.out.println("metaData.getColumnType:"+metaData.getColumnType(i));

                    System.out.println("metaData.getColumnTypeName:"+metaData.getColumnTypeName(i));

                    System.out.println("metaData.getPrecision:"+metaData.getPrecision(i));

                    System.out.println("metaData.getScale:"+metaData.getScale(i));

                    System.out.println("metaData.getSchemaName:"+metaData.getSchemaName(i));

                    System.out.println("metaData.getTableName:"+metaData.getTableName(i));

                    System.out.println("**************************");

                }

//展示dbf中的行数据

                while(rs.next()){

                    for(int i = 1 ; i <= metaData.getColumnCount() ; i++){

                        System.out.println(rs.getString(i));

                    }

                    System.out.println("*******************************************");

                }

            } catch (SQLException e) {

// TODO Auto-generated catch block

                e.printStackTrace();

            }finally{

                if(rs != null){

                    rs.close();

                }

                if(pstm != null){

                    pstm.close();

                }

                if(conn != null){

                    conn.close();

                }

            }

        } catch (Exception e) {

// TODO Auto-generated catch block

            e.printStackTrace();

        }
    }
}

 类似资料: