Because of the working relationship, need to work, need to read the DBF file, find some dbf read open source software, either is too large, still tens of thousands of lines, or functional problems, coding, length, in short, there is no very cool to find. Under the helpless, my old man's anger from the heart, evil to the side of life, decided to write their own. Results only with less than 300 lines of code to take care of, of course, not the only goal, but also elegant and concise to do, the parents follow my footsteps together to feel the simple design and implementation of it.
Before starting coding, introduce DBF, this dbf is an old thing, in the DOS era has appeared, and coquettish for quite some time, later with the application of large database, it gradually decline, but because of its simple and easy to use features, or application in a large number of data exchange. However, its development process, also formed a number of versions, different versions of the structure is not the same, it also determines the resolution of the program is not the same.
Today I only realized the FOXBASE/DBASEIII, but also for the expansion of various other versions are ready.
Interface Design
083756_dzpl_1245989.jpg (37.32 KB, download number: 0)
Download attachments
2015-5-27 22:33 Upload
There are a total of two classes, an interface, field and header is two simple Pojo class, respectively, defines the file header and field-related information.
Reader interface is a DBF file read interface, the main definition is to get the file type, encoding, field and record movement related methods.
Code Implementation first implements the abstract class of reader
Public abstract class Dbfreader implements Reader {protected String encode = "GBK"; Private FileChannel FileChannel; protected header Header; protected list<field> fields; Private Boolean recordremoved; int position = 0; Static Map<integer, class> Readermap = new Hashmap<integer, class> (); static {Addreader (3, Foxprodbase3reader.class); } public static void Addreader (int type, Class clazz) {readermap.put (type, clazz); The public static void Addreader (int type, String className) throws ClassNotFoundException {Readermap.put (type, Class.forName (ClassName)); } public byte GetType () {return 3; } public String Getencode () {return encode; } Public Header GetHeader () {return header; } public list<field> GetFields () {return fields; } public boolean isrecordremoved () {return recordremoved; } public static ReAder Parse (string dbffile, String encode) throws IOException, Illegalaccessexception, instantiationexception {ret Urn Parse (new File (Dbffile), encode); public static Reader Parse (String dbffile) throws IOException, Illegalaccessexception, instantiationexception { Return Parse (new File (Dbffile), "GBK"); public static Reader Parse (File dbffile) throws IOException, Illegalaccessexception, instantiationexception { Return Parse (dbffile, "GBK"); public static Reader Parse (File dbffile, String encode) throws IOException, Illegalaccessexception, Instantiationexc eption {randomaccessfile afile = new Randomaccessfile (Dbffile, "R"); FileChannel FileChannel = Afile.getchannel (); Bytebuffer Bytebuffer = bytebuffer.allocate (1); Filechannel.read (Bytebuffer); BYTE type = Bytebuffer.array () [0]; class<reader> Readerclass = readermap.get ((int) type); if (Readerclass = = null) {fIlechannel.close (); throw new IOException ("Unsupported file type [" + Type + "]. "); } Dbfreader reader = (dbfreader) readerclass.newinstance (); Reader.setfilechannel (FileChannel); Reader.readheader (); Reader.readfields (); return reader; } public void Setfilechannel (FileChannel filechannel) {this.filechannel = FileChannel; } protected abstract void ReadFields () throws IOException; public void Movebeforefirst () throws IOException {position = 0; Filechannel.position (Header.getheaderlength ()); }/** * @param position starting from 1 * @throws java.io.IOException */public void Absolute (int position) th Rows IOException {checkposition (position); This.position = position; Filechannel.position (Header.getheaderlength () + (position-1) * header.getrecordlength ()); } private void checkposition (int position) throws IOException {if (position >= heAder.getrecordcount ()) {throw new IOException ("Expected record line number is" + (This.position + 1) + ", exceeding the actual record line count:" + Header.getreco Rdcount () + ". "); }} protected abstract Field Readfield () throws IOException; protected abstract void Readheader () throws IOException; private void Skipheaderterminator () throws IOException {Bytebuffer Bytebuffer = bytebuffer.allocate (1); Readbytebuffer (Bytebuffer); } public void Close () throws IOException {filechannel.close (); } public void Next () throws IOException {checkposition (position); Bytebuffer Bytebuffer = bytebuffer.allocate (1); Readbytebuffer (Bytebuffer); this.recordremoved = (Bytebuffer.array () [0] = = ' * '); for (field Field:fields) {read (field); } position++; } public boolean Hasnext () {return position < Header.getrecordcount (); private void Read (Field field) throws IOException {Bytebuffer buffer = bytebuffer.allocate (Field.getlength ()); Readbytebuffer (buffer); Field.setstringvalue (New String (Buffer.array (), encode). Trim ()); Field.setbuffer (buffer); } protected void Readbytebuffer (Bytebuffer bytebuffer) throws IOException {Filechannel.read (bytebuffer); } }
The
class is the largest class, and it is noteworthy that several static methods: addreader and Parse, Addreader is used to add new types of Reader,parse for parsing files.
Parse executes the process of reading the first byte first, judging whether there is a corresponding parsing implementation class, if there is a corresponding parsing implementation class to parse, if not, then throw the error declaration is not supported. The
Write implementation class below is simple, the following is the parser for FOXPRODBASE3:
public class Foxprodbase3reader extends Dbfreader {protected void ReadFields () throws IOException {fields = NE W arraylist<field> (); for (int i = 0; I < (Header.getheaderlength ()-32-1)/i++) {Fields.Add (Readfield ()); }} public byte GetType () {return 3; } protected field Readfield () throws ioexception {Field field = new Field (); Bytebuffer Bytebuffer = bytebuffer.allocate (32); Readbytebuffer (Bytebuffer); byte[] bytes = Bytebuffer.array (); Field.setname (New String (bytes, 0, one, encode). Trim (). Split ("+") [0]); Field.settype ((char) bytes[11]); Field.setdisplacement (Util.getunsignedint (Bytes, 12, 4)); Field.setlength (Util.getunsignedint (Bytes, 16, 1)); Field.setdecimal (Util.getunsignedint (Bytes, 17, 1)); Field.setflag (bytes[18]); return field; } protected void Readheader () throws IOException {Header = new header (); BytEbuffer Bytebuffer = bytebuffer.allocate (31); Readbytebuffer (Bytebuffer); byte[] bytes = Bytebuffer.array (); Header.setlastupdate ((util.getunsignedint (bytes, 0, 1) + 1900) * 10000 + util.getunsignedint (bytes, 1, 1) * + Util.get Unsignedint (Bytes, 2, 1)); Header.setrecordcount (Util.getunsignedint (Bytes, 3, 4)); Header.setheaderlength (Util.getunsignedint (bytes, 7, 2)); Header.setrecordlength (Util.getunsignedint (Bytes, 9, 2)); }}
Test Cases
public class Dbfreadertest {static string[] files = {"BESTIMATE20140401", "BHDQUOTE20140401"}; public static void Main (string[] args) throws IOException, Illegalaccessexception, instantiationexception {for (St Ring file:files) {printfile (file); }} public static void Printfile (String fileName) throws IOException, Instantiationexception, illegalaccessexception {Reader Dbfreader = dbfreader.parse ("e:\\20140401\\" + FileName + ".) DBF "); For (Field Field:dbfReader.getFields ()) {System.out.printf ("name:%s%s (%d,%d) \ n", Field.getname (), Field.get Type (), Field.getlength (), Field.getdecimal ()); } System.out.println (); for (int i = 0; i < Dbfreader.getheader (). GetRecordCount (); i++) {dbfreader.next (); For (Field Field:dbfReader.getFields ()) {System.out.printf ("%" + field.getlength () + "s", Field.getstrin Gvalue ()); } System.out.println (); } dbfreader.close (); }}
you can see that the final use is also very concise.
Code Statistics
085959_ezqv_1245989.jpg (15.06 KB, download number: 0)
Download attachments
2015-5-27 22:33 Upload
The total number of lines of code is 282 lines, minus import and interface declarations, and so on, the real code of work is about 200 lines:
The summary above not only shows how to achieve the resolution of DBF files, but also shows how to design a reasonably balanced approach to the needs and future expansions that are now facing.
For example: To implement another standard DBF file support, as long as the above Foxprodbase3reader class, simple implementation, then call Dbfparser.addreader (Xxxreader);
Good design needs to avoid over-design, too complex, but also to the future changes and expansion of the appropriate consideration, to avoid the need for new requirements here to move, where changes lead to structural adjustment and change, but also pay attention to abide by the dry principle, you can say that if the program is necessary to a large number of duplication, It shows that there must be problems in structural design.
Welcome attention: Http://web.j2ee.top. The Code and framework information in this example will be shared here. Also welcome to join QQ Group: 228977971, let us grow together!
Self-writing Framework 2: Using a 200-line DBF parser to demonstrate good architecture design