Tinydbf-uses a 200-line DBF parser to demonstrate good architecture design

Source: Internet
Author: User

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

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.