用户工具

站点工具


分享:技术:dbf:javadbf操作dbf文件

javadbf操作dbf

dbf扫盲

概念

  1. 是常用的桌面型数据库
  2. 是dBase,Foxbase和FoxPro所使用的数据库格式

打开方式

  1. DBF Viewer Pro是一个用于Windows下的DBF数据库文件管理器。
  2. 用FOXPRO打开
  3. microsoft access可以导入导出.dbf文件

javadbf操作dbf

JAVA操作dbf文件的读写,这里使用第三方API:javadbf

官网

项目引入javadbf方式

  1. 将jar加入classpath
  2. maven项目加入依赖
pom.xml
<!-- javadbf -->
<dependency>
	<groupId>com.github.albfernandez</groupId>
	<artifactId>javadbf</artifactId>
	<version>1.9.2</version>
</dependency>

同时支持读与写的类型

[XBase类型] [XBase符号] [JavaDBF中java类型]
Character C java.lang.String
Numeric N java.math.BigDecimal
Double F java.math.BigDecimal
Logical L java.lang.Boolean
Date D java.util.Date

支持读暂不支持写的类型

这算是javadbf的待完善之处,后续版本有可能会跟进优化

[FoxPro类型] [符号] [JavaDBF中java类型]
Currency Y java.math.BigDecimal
Long I java.lang.Integer
Date Type T java.util.Date
Timestamp @ java.util.Date
AutoIncrement + java.lang.Integer
Memo M java.lang.String or byte[]
Binary B byte[] or java.lang.Double
Blob W byte[]
General G byte[]
Picture P byte[]
VarBinary Q byte[]
Varchar V java.lang.String
Double O java.lang.Double

分析常见的场景

表:	对应一个 dbf文件,库与表一个概念
表关联:	表之间没有关联查询的功能
创建表:	创建文件,创建列,参考[testWrite1-见下方]
删除表:	删除文件即可
索引:	没有索引概念
增:
	第一次创建文件,或者,一次性将数据覆盖原有文件,参考[testWrite1-见下方]
	在文件末尾添加记录,参考[testWrite2-见下方]
删:
	在[遍历表]的基础上,将想删的数据过滤,再覆盖文件
改:
	在[遍历表]的基础上,将想改的数据修改,再覆盖文件
查:
	[遍历表]
		逐条记录读取,记录转Object数组,参考[testRead1-见下方]
		逐条记录读取,根据列名读取列,参考[testRead2-见下方]
	根据条件查
		在遍历表的基础上,读取列,判断条件

DEMO-testRead1

DbfTest.java
	/**
	 * dbf文件
	 */
	public static final String DBF_FILE = "/Users/guanxianghui/Desktop/user.dbf";
 
	/**
	 * 测试读取文件-逐条记录读取,记录转Object数组
	 */
	@Test
	public void testRead1() {
		System.out.println("[测试读取文件-逐条记录读取,记录转Object数组]");
		System.out.println("DBF文件路径:[" + DBF_FILE + "]");
		DBFReader reader = null;
		try {
			/**
			 * 读工具类,指定文件
			 */
			reader = new DBFReader(new FileInputStream(DBF_FILE), Charset.forName("GBK"));
			/**
			 * 读取表结构
			 */
			int numberOfFields = reader.getFieldCount();
			System.out.println("表结构共:[" + numberOfFields + "]列");
			System.out.println("=========");
			for (int i = 0; i < numberOfFields; i++) {
				DBFField field = reader.getField(i);
				System.out.println("列:" + field.getName() + "[" + field.getLength() + "]");
			}
			System.out.println("=========");
			/**
			 * 逐条记录读取,记录转Object数组
			 */
			System.out.println("总记录数[" + reader.getRecordCount() + "],逐条记录读取,记录转Object数组");
			System.out.println("=========");
			Object[] rowObjects;
			int index = 0;
			while (null != (rowObjects = reader.nextRecord())) {
				String content = "";
				for (int i = 0; i < rowObjects.length; i++) {
					if (StringUtils.isNotBlank(content)) {
						content += ",";
					}
					content += rowObjects[i] + "[" + rowObjects[i].getClass().getName() + "]";
				}
				System.out.println("记录[" + (++index) + "]:" + content);
			}
			System.out.println("=========");
		} catch (DBFException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			/**
			 * 关闭资源
			 */
			if (null != reader) {
				DBFUtils.close(reader);
			}
		}
	}

输出

log.txt
[测试读取文件-逐条记录读取,记录转Object数组]
DBF文件路径:[/Users/guanxianghui/Desktop/user.dbf]
表结构共:[3]列
=========
列:COUNTRY[10]
列:NAME[20]
列:SALARY[12]
=========
总记录数[3],逐条记录读取,记录转Object数组
=========
记录[1]:中国[java.lang.String],关向辉[java.lang.String],5000.01[java.math.BigDecimal]
记录[2]:英国[java.lang.String],关向新[java.lang.String],3400.20[java.math.BigDecimal]
记录[3]:澳门[java.lang.String],关向阳[java.lang.String],7300.03[java.math.BigDecimal]
=========

DEMO-testRead2

DbfTest.java
	/**
	 * dbf文件
	 */
	public static final String DBF_FILE = "/Users/guanxianghui/Desktop/user.dbf";
 
	/**
	 * 测试读取文件-逐条记录读取,根据列名读取列
	 */
	@Test
	public void testRead2() {
		System.out.println("[测试读取文件-逐条记录读取,根据列名读取列]");
		System.out.println("DBF文件路径:[" + DBF_FILE + "]");
		DBFReader reader = null;
		try {
			/**
			 * 读工具类,指定文件
			 */
			reader = new DBFReader(new FileInputStream(DBF_FILE), Charset.forName("GBK"));
			/**
			 * 读取表结构
			 */
			int numberOfFields = reader.getFieldCount();
			System.out.println("表结构共[:" + numberOfFields + "]列");
			System.out.println("=========");
			for (int i = 0; i < numberOfFields; i++) {
				DBFField field = reader.getField(i);
				System.out.println("列:" + field.getName() + "[" + field.getLength() + "]");
			}
			System.out.println("=========");
			System.out.println("总记录数[" + reader.getRecordCount() + "],逐条记录读取,根据列名读取列");
			System.out.println("=========");
			/**
			 * 逐条记录读取,根据列名读取列
			 */
			DBFRow row;
			int index = 0;
			while ((row = reader.nextRow()) != null) {
				System.out.println("第[" + (++index) + "]记录,COUNTRY:[" + row.getString("COUNTRY") + "],NAME:["
						+ row.getString("NAME") + "],SALARY:[" + row.getString("SALARY") + "]");
			}
			System.out.println("=========");
		} catch (DBFException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			/**
			 * 关闭资源
			 */
			if (null != reader) {
				DBFUtils.close(reader);
			}
		}
	}

输出

log.txt
[测试读取文件-逐条记录读取,根据列名读取列]
DBF文件路径:[/Users/guanxianghui/Desktop/user.dbf]
表结构共[:3]列
=========
列:COUNTRY[10]
列:NAME[20]
列:SALARY[12]
=========
总记录数[6],逐条记录读取,根据列名读取列
=========
第[1]记录,COUNTRY:[中国],NAME:[关向辉],SALARY:[5000.01]
第[2]记录,COUNTRY:[英国],NAME:[关向新],SALARY:[3400.20]
第[3]记录,COUNTRY:[澳门],NAME:[关向阳],SALARY:[7300.03]
第[4]记录,COUNTRY:[中国],NAME:[关向辉],SALARY:[5000.01]
第[5]记录,COUNTRY:[英国],NAME:[关向新],SALARY:[3400.20]
第[6]记录,COUNTRY:[澳门],NAME:[关向阳],SALARY:[7300.03]
=========

DEMO-testWrite1

这种方式是正常(Normal)模式(Mode),即非(Not)同步(Syn)方式(Mode),所有数据储存在内存中,在关闭(Close)资源时一口气输出到dfb文件,覆盖文件列+数据!好处:交互少,性能高,在数据量小的场景适用!隐患:数据量过大,有可能会导致内存溢出!

DbfTest.java
	/**
	 * dbf文件
	 */
	public static final String DBF_FILE = "/Users/guanxianghui/Desktop/user.dbf";
 
	/**
	 * 测试写文件-FileOutputStream-覆盖文件列+数据
	 */
	@Test
	public void testWrite1() {
		System.out.println("[测试写文件-FileOutputStream-覆盖文件列+数据]");
		System.out.println("DBF文件路径:[" + DBF_FILE + "]");
		/**
		 * 定义列
		 */
		DBFField[] fields = new DBFField[3];
 
		fields[0] = new DBFField();
		fields[0].setName("COUNTRY");
		fields[0].setType(DBFDataType.CHARACTER);
		fields[0].setLength(10);
 
		fields[1] = new DBFField();
		fields[1].setName("NAME");
		fields[1].setType(DBFDataType.CHARACTER);
		fields[1].setLength(20);
 
		fields[2] = new DBFField();
		fields[2].setName("SALARY");
		fields[2].setType(DBFDataType.NUMERIC);
		fields[2].setLength(12);
		fields[2].setDecimalCount(2);
 
		DBFWriter writer = null;
		System.out.println("设置列,并输出数据到文件");
		System.out.println("=========");
		try {
			/**
			 * 输出到文件
			 */
			writer = new DBFWriter(new FileOutputStream(DBF_FILE), Charset.forName("GBK"));//注意,是FileOutputStream
 
			/**
			 * 设置列
			 */
			for (DBFField field : fields) {
				System.out.println("列:" + field.getName() + "[" + field.getLength() + "]");
			}
			writer.setFields(fields);
			System.out.println("=========");
 
			/**
			 * 设置数据,并新增数据
			 */
			Object rowData[] = new Object[3];
			rowData[0] = "中国";
			rowData[1] = "关向辉";
			rowData[2] = new Double(5000.01);
			System.out.println("输出数据:[" + rowData[0] + "," + rowData[1] + "," + rowData[2] + "]");
			writer.addRecord(rowData);
 
			rowData = new Object[3];
			rowData[0] = "英国";
			rowData[1] = "关向新";
			rowData[2] = new Double(3400.20);
			System.out.println("输出数据:[" + rowData[0] + "," + rowData[1] + "," + rowData[2] + "]");
			writer.addRecord(rowData);
 
			rowData = new Object[3];
			rowData[0] = "澳门";
			rowData[1] = "关向阳";
			rowData[2] = new Double(7300.03);
			System.out.println("输出数据:[" + rowData[0] + "," + rowData[1] + "," + rowData[2] + "]");
			writer.addRecord(rowData);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} finally {
			/**
			 * 关闭资源
			 */
			if (null != writer) {
				DBFUtils.close(writer);
			}
		}
		System.out.println("=========");
		System.out.println("输出完成");
	}

输出

log.txt
[测试写文件-FileOutputStream-覆盖文件列+数据]
DBF文件路径:[/Users/guanxianghui/Desktop/user.dbf]
设置列,并输出数据到文件
=========
列:COUNTRY[10]
列:NAME[20]
列:SALARY[12]
=========
输出数据:[中国,关向辉,5000.01]
输出数据:[英国,关向新,3400.2]
输出数据:[澳门,关向阳,7300.03]
=========
输出完成

DEMO-testWrite2

这种方式是同步(Syn)方式(Mode),添加记录addRecord方法调用时,马上输出到dfb文件!好处:数据量大时候,不会内存溢出!隐患:交互多,数据量小的场景,性能低下!

同步(Syn)方式(Mode),如果dbf文件存在,而且已经有列定义,不能重复操作列定义(重复操作会报错:com.linuxense.javadbf.DBFException: Fields has already been set),如果dbf是新文件,则可以操作一次列定义。

DbfTest.java
	/**
	 * dbf文件
	 */
	public static final String DBF_FILE = "/Users/guanxianghui/Desktop/user.dbf";
 
	/**
	 * 测试写文件-File-文件列不变,append追加数据
	 */
	@Test
	public void testWrite2() {
		System.out.println("[测试写文件-File-文件列不变,append追加数据]");
		System.out.println("DBF文件路径:[" + DBF_FILE + "]");
		DBFWriter writer = null;
		System.out.println("=========");
		try {
			/**
			 * 输出到文件
			 */
			writer = new DBFWriter(new File(DBF_FILE), Charset.forName("GBK"));//注意,是File
 
			/**
			 * 设置数据,并新增数据
			 */
			Object rowData[] = new Object[3];
			rowData[0] = "中国";
			rowData[1] = "关向辉";
			rowData[2] = new Double(5000.01);
			System.out.println("追加数据:[" + rowData[0] + "," + rowData[1] + "," + rowData[2] + "]");
			writer.addRecord(rowData);
 
			rowData = new Object[3];
			rowData[0] = "英国";
			rowData[1] = "关向新";
			rowData[2] = new Double(3400.20);
			System.out.println("追加数据:[" + rowData[0] + "," + rowData[1] + "," + rowData[2] + "]");
			writer.addRecord(rowData);
 
			rowData = new Object[3];
			rowData[0] = "澳门";
			rowData[1] = "关向阳";
			rowData[2] = new Double(7300.03);
			System.out.println("追加数据:[" + rowData[0] + "," + rowData[1] + "," + rowData[2] + "]");
			writer.addRecord(rowData);
		} finally {
			/**
			 * 关闭资源
			 */
			if (null != writer) {
				DBFUtils.close(writer);
			}
		}
		System.out.println("=========");
		System.out.println("输出完成");
	}

输出

log.txt
[测试写文件-File-文件列不变,append追加数据]
DBF文件路径:[/Users/guanxianghui/Desktop/user.dbf]
=========
追加数据:[中国,关向辉,5000.01]
追加数据:[英国,关向新,3400.2]
追加数据:[澳门,关向阳,7300.03]
=========
输出完成

dbf示例文件

可以解压zip得到dbf示例文件 user.dbf.zip

分享/技术/dbf/javadbf操作dbf文件.txt · 最后更改: 2018/04/19 16:17 由 gxx