📄 dbasefilereader.java
字号:
/*
* GeoTools - OpenSource mapping toolkit
* http://geotools.org
* (C) 2002-2006, Geotools Project Managment Committee (PMC)
* (C) 2002, Centre for Computational Geography
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* This file is based on an origional contained in the GISToolkit project:
* http://gistoolkit.sourceforge.net/
*/
package org.geotools.data.shapefile.dbf;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Calendar;
import org.geotools.data.shapefile.FileReader;
import org.geotools.data.shapefile.ShpFileType;
import org.geotools.data.shapefile.ShpFiles;
import org.geotools.data.shapefile.StreamLogging;
import org.geotools.resources.NIOUtilities;
/**
* A DbaseFileReader is used to read a dbase III format file. <br>
* The general use of this class is: <CODE><PRE>
*
* FileChannel in = new FileInputStream("thefile.dbf").getChannel();
* DbaseFileReader r = new DbaseFileReader( in ) Object[] fields = new
* Object[r.getHeader().getNumFields()]; while (r.hasNext()) {
* r.readEntry(fields); // do stuff } r.close();
*
* </PRE></CODE> For consumers who wish to be a bit more selective with their reading
* of rows, the Row object has been added. The semantics are the same as using
* the readEntry method, but remember that the Row object is always the same.
* The values are parsed as they are read, so it pays to copy them out (as each
* call to Row.read() will result in an expensive String parse). <br>
* <b>EACH CALL TO readEntry OR readRow ADVANCES THE FILE!</b><br>
* An example of using the Row method of reading: <CODE><PRE>
*
* FileChannel in = new FileInputStream("thefile.dbf").getChannel();
* DbaseFileReader r = new DbaseFileReader( in ) int fields =
* r.getHeader().getNumFields(); while (r.hasNext()) { DbaseFileReader.Row row =
* r.readRow(); for (int i = 0; i < fields; i++) { // do stuff Foo.bar(
* row.read(i) ); } } r.close();
*
* </PRE></CODE>
*
* @author Ian Schneider
* @source $URL:
* http://svn.geotools.org/geotools/trunk/gt/modules/plugin/shapefile/src/main/java/org/geotools/data/shapefile/dbf/DbaseFileReader.java $
*/
public class DbaseFileReader implements FileReader {
public final class Row {
public Object read(int column) throws IOException {
int offset = getOffset(column);
return readObject(offset, column);
}
public String toString() {
StringBuffer ret = new StringBuffer("DBF Row - ");
for (int i = 0; i < header.getNumFields(); i++) {
ret.append(header.getFieldName(i)).append(": \"");
try {
ret.append(this.read(i));
} catch (IOException ioe) {
ret.append(ioe.getMessage());
}
ret.append("\" ");
}
return ret.toString();
}
}
DbaseFileHeader header;
ByteBuffer buffer;
ReadableByteChannel channel;
CharBuffer charBuffer;
Charset charset;
CharsetDecoder decoder;
char[] fieldTypes;
int[] fieldLengths;
int cnt = 1;
Row row;
protected boolean useMemoryMappedBuffer;
protected boolean randomAccessEnabled;
protected int currentOffset = 0;
private StreamLogging streamLogger = new StreamLogging("Dbase File Reader");
private Charset stringCharset;
/**
* Creates a new instance of DBaseFileReader
*
* @param shapefileFiles.
* The readable channel to use.
* @throws IOException
* If an error occurs while initializing.
*/
public DbaseFileReader(ShpFiles shapefileFiles,
boolean useMemoryMappedBuffer, Charset charset) throws IOException {
ReadableByteChannel dbfChannel = shapefileFiles.getReadChannel(ShpFileType.DBF, this);
init(dbfChannel, useMemoryMappedBuffer, charset);
}
public DbaseFileReader(ReadableByteChannel readChannel, boolean useMemoryMappedBuffer, Charset charset) throws IOException {
init(readChannel, useMemoryMappedBuffer, charset);
}
private void init(ReadableByteChannel dbfChannel, boolean useMemoryMappedBuffer,
Charset charset) throws IOException {
this.channel = dbfChannel;
this.stringCharset = charset;
this.charset = Charset.forName("ISO-8859-1"); // charset;
this.useMemoryMappedBuffer = useMemoryMappedBuffer;
this.randomAccessEnabled = (channel instanceof FileChannel);
streamLogger.open();
header = new DbaseFileHeader();
header.readHeader(channel);
// create the ByteBuffer
// if we have a FileChannel, lets map it
if (channel instanceof FileChannel && this.useMemoryMappedBuffer) {
FileChannel fc = (FileChannel) channel;
buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
buffer.position((int) fc.position());
this.currentOffset = 0;
} else {
// Force useMemoryMappedBuffer to false
this.useMemoryMappedBuffer = false;
// Some other type of channel
// start with a 8K buffer, should be more than adequate
int size = 8 * 1024;
// if for some reason its not, resize it
size = header.getRecordLength() > size ? header.getRecordLength()
: size;
buffer = ByteBuffer.allocateDirect(size);
// fill it and reset
fill(buffer, channel);
buffer.flip();
this.currentOffset = header.getHeaderLength();
}
// The entire file is in little endian
buffer.order(ByteOrder.LITTLE_ENDIAN);
// Set up some buffers and lookups for efficiency
fieldTypes = new char[header.getNumFields()];
fieldLengths = new int[header.getNumFields()];
for (int i = 0, ii = header.getNumFields(); i < ii; i++) {
fieldTypes[i] = header.getFieldType(i);
fieldLengths[i] = header.getFieldLength(i);
}
charBuffer = CharBuffer.allocate(header.getRecordLength() - 1);
decoder = charset.newDecoder();
row = new Row();
}
protected int fill(ByteBuffer buffer, ReadableByteChannel channel)
throws IOException {
int r = buffer.remaining();
// channel reads return -1 when EOF or other error
// because they a non-blocking reads, 0 is a valid return value!!
while (buffer.remaining() > 0 && r != -1) {
r = channel.read(buffer);
}
if (r == -1) {
buffer.limit(buffer.position());
}
return r;
}
private void bufferCheck() throws IOException {
// remaining is less than record length
// compact the remaining data and read again
if (!buffer.isReadOnly()
&& buffer.remaining() < header.getRecordLength()) {
// if (!this.useMemoryMappedBuffer) {
this.currentOffset += buffer.position();
// }
buffer.compact();
fill(buffer, channel);
buffer.position(0);
}
}
private int getOffset(int column) {
int offset = 0;
for (int i = 0, ii = column; i < ii; i++) {
offset += fieldLengths[i];
}
return offset;
}
/**
* Get the header from this file. The header is read upon instantiation.
*
* @return The header associated with this file or null if an error
* occurred.
*/
public DbaseFileHeader getHeader() {
return header;
}
/**
* Clean up all resources associated with this reader.<B>Highly recomended.</B>
*
* @throws IOException
* If an error occurs.
*/
public void close() throws IOException {
if (channel.isOpen()) {
channel.close();
streamLogger.close();
}
if (buffer instanceof MappedByteBuffer) {
NIOUtilities.clean(buffer);
}
buffer = null;
channel = null;
charBuffer = null;
decoder = null;
header = null;
row = null;
}
/**
* Query the reader as to whether there is another record.
*
* @return True if more records exist, false otherwise.
*/
public boolean hasNext() {
return cnt < header.getNumRecords() + 1;
}
/**
* Get the next record (entry). Will return a new array of values.
*
* @throws IOException
* If an error occurs.
* @return A new array of values.
*/
public Object[] readEntry() throws IOException {
return readEntry(new Object[header.getNumFields()]);
}
public Row readRow() throws IOException {
read();
return row;
}
/**
* Skip the next record.
*
* @throws IOException
* If an error occurs.
*/
public void skip() throws IOException {
boolean foundRecord = false;
while (!foundRecord) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -