/*
* 文件名:LineIterator.java
* 描述:文件行迭代器
* 修改人: ManerFan
*
*/
package maner.fan;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.apache.log4j.Logger;
/**
* <p>在commons-io的基础上进行改进,支持从第pos字节处读取
*
* @author ManerFan
*/
public class LineIterator implements Iterator<String> {
/** The reader that is being read. */
private final BufferedRandomAccessFile randomReader;
/** The current line. */
private String cachedLine;
private long cachedOffset = 0;
/** 当前指针 */
private long currentOffset = 0;
/** A flag indicating if the iterator has been fully read. */
private boolean finished = false;
/** 默认编码 */
private String charSet = "UTF-8";
/** 起始字节数 */
private long offset;
/** log4j */
private static final Logger LOGGER = Logger.getLogger(LineIterator.class);
/**
* Constructs an iterator of the lines for a <code>Reader</code>.
*
* @param reader the <code>Reader</code> to read from, not null
* @throws IllegalArgumentException if the reader is null
*/
public LineIterator(final File reader, String charSet, final long offset)
throws IllegalArgumentException {
if (reader == null) {
throw new IllegalArgumentException("Reader must not be null");
}
if (!reader.exists() || !reader.isFile()) {
throw new IllegalArgumentException();
}
if (null != charSet && !charSet.trim().isEmpty()) {
this.charSet = charSet;
}
if (offset < 0) {
throw new IllegalArgumentException();
}
try {
randomReader = new BufferedRandomAccessFile(reader, "r");
this.offset = offset;
randomReader.seek(offset);
currentOffset = offset;
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
//-----------------------------------------------------------------------
/**
* Indicates whether the <code>Reader</code> has more lines.
* If there is an <code>IOException</code> then {@link #close()} will
* be called on this instance.
*
* @return {@code true} if the Reader has more lines
* @throws IllegalStateException if an IO exception occurs
*/
public boolean hasNext() {
try {
if (cachedLine != null) {
return true;
} else if (finished) {
return false;
} else if (randomReader.length() < offset) {
return false;
} else {
while (true) {
String line = randomReader.readLine();
if (line == null) {
finished = true;
return false;
} else {
String transLine = transcoding(line);
if (null != transLine) {
cachedLine = transLine;
cachedOffset = randomReader.getFilePointer();
return true;
}
}
}
}
} catch (IOException ioe) {
close();
throw new IllegalStateException(ioe);
}
}
/**
* Overridable method to validate each line that is returned.
* This implementation always returns true.
* @param line the line that is to be validated
* @return true if valid, false to remove from the iterator
*/
protected String transcoding(String line) {
String transString = null;
try {
if (null != line && !line.isEmpty()) {
transString = new String(line.getBytes("8859_1"), charSet); // 做一次转码
}
} catch (UnsupportedEncodingException e) {
return null;
}
return transString;
}
/**
* Returns the next line in the wrapped <code>Reader</code>.
*
* @return the next line from the input
* @throws NoSuchElementException if there is no line to return
*/
public String next() {
return nextLine();
}
/**
* Returns the next line in the wrapped <code>Reader</code>.
*
* @return the next line from the input
* @throws NoSuchElementException if there is no line to return
*/
private String nextLine() {
if (!hasNext()) {
throw new NoSuchElementException("No more lines");
}
String currentLine = cachedLine;
cachedLine = null;
currentOffset = cachedOffset;
return currentLine;
}
public long currentOffset() {
return currentOffset;
}
/**
* Closes the underlying <code>Reader</code> quietly.
* This method is useful if you only want to process the first few
* lines of a larger file. If you do not close the iterator
* then the <code>Reader</code> remains open.
* This method can safely be called multiple times.
* @throws IOException
*/
public void close() {
finished = true;
try {
randomReader.close();
} catch (IOException e) {
LOGGER.error("Cannot close randonReader.", e);
}
cachedLine = null;
}
/**
* Unsupported.
*
* @throws UnsupportedOperationException always
*/
public void remove() {
throw new UnsupportedOperationException("Remove unsupported on LineIterator");
}
//-----------------------------------------------------------------------
/**
* Closes the iterator, handling null and ignoring exceptions.
*
* @param iterator the iterator to close
*/
public static void closeQuietly(LineIterator iterator) {
if (iterator != null) {
iterator.close();
}
}
}