.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包。使用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驱动,实现用java的JDBC方式读写DBF文件。将DBF文件视作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)来存取数据库。
由于微软的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();
}
}
}