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
Uploaded 6 days ago
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21st 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
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); }
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 { Return 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, instantiationexception { 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) throws 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 number:" + header.getrecordcount () + ". "); } }
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); } } |
This class is the largest of a class, notably several static methods: Addreader and Parse, Addreader used to add a new type of reader,parse used to parse the file.
The parse execution process is to first read the first byte, determine whether there is a corresponding parsing implementation class, if there is, there is a corresponding parsing implementation class to parse, if not, then throw the error declaration is not supported.
The following is a simple implementation class, the following is the FOXPRODBASE3 parser:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21st 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
public class Foxprodbase3reader extends Dbfreader { protected void ReadFields () throws IOException { Fields = new 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21st 22 23 24 25 26 |
public class Dbfreadertest { Static string[] files = {"BESTIMATE20140401", "BHDQUOTE20140401"};
public static void Main (string[] args) throws IOException, Illegalaccessexception, instantiationexception { for (String 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.gettype (), 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.getstringvalue ()); } 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
Uploaded 6 days ago
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.
All of the code can be seen in the following connection:
Https://git.oschina.net/tinyframework/tiny/tree/master/framework/org.tinygroup.dbf/src/main/java/org/tinygroup/dbf
Tinydbf-uses a 200-line DBF parser to demonstrate good architecture design