您的位置:首页 > 其它

File I/O source code--读取文件 相关方法阅读

2014-12-09 19:07 393 查看
上一篇本来是要介绍读取文件的,后来写着写着,感觉我连如何创建文件都不知道,所以索性就标题改了,挪到这篇来讨论读取文件的方法

/* 读入TXT文件 */
String pathname = "D:\\input.txt"; //绝对路径或相对路径都可以,这里是绝对路径,写入文件时演示相对路径
File filename = new File(pathname); // 要读取以上路径的input.txt文件
InputStreamReader reader = new InputStreamReader(
new FileInputStream(filename)); // 建立一个输入流对象reader
BufferedReader br = new BufferedReader(reader); //建立一个对象,它把文件内容转成计算机能读懂的语言
String line = "";
line = br.readLine();
while (line != null) {
System.out.println(line);
line = br.readLine(); // 一次读入一行数据
}


我们从br.readLine()直接来跟踪代码

/**
* Reads a line of text.  A line is considered to be terminated by any one
* of a line feed ('\n'), a carriage return ('\r'), or a carriage return
* followed immediately by a linefeed.
*
* @return     A String containing the contents of the line, not including
*             any line-termination characters, or null if the end of the
*             stream has been reached
*
* @exception  IOException  If an I/O error occurs
*
* @see java.nio.file.Files#readAllLines
*/
public String readLine() throws IOException {
return readLine(false);
}
/**
* Reads a line of text.  A line is considered to be terminated by any one
* of a line feed ('\n'), a carriage return ('\r'), or a carriage return
* followed immediately by a linefeed.
*
* @param      ignoreLF  If true, the next '\n' will be skipped
*
* @return     A String containing the contents of the line, not including
*             any line-termination characters, or null if the end of the
*             stream has been reached
*
* @see        java.io.LineNumberReader#readLine()
*
* @exception  IOException  If an I/O error occurs
*/
String readLine(boolean ignoreLF) throws IOException {
StringBuffer s = null;
int startChar;

synchronized (lock) {
ensureOpen();
boolean omitLF = ignoreLF || skipLF;

bufferLoop:
for (;;) {

if (nextChar >= nChars)
fill();
if (nextChar >= nChars) { /* EOF */
if (s != null && s.length() > 0)
return s.toString();
else
return null;
}
boolean eol = false;
char c = 0;
int i;

/* Skip a leftover '\n', if necessary */
if (omitLF && (cb[nextChar] == '\n'))
nextChar++;
skipLF = false;
omitLF = false;

charLoop:
for (i = nextChar; i < nChars; i++) {
c = cb[i];
if ((c == '\n') || (c == '\r')) {
eol = true;
break charLoop;
}
}

startChar = nextChar;
nextChar = i;

if (eol) {
String str;
if (s == null) {
str = new String(cb, startChar, i - startChar);
} else {
s.append(cb, startChar, i - startChar);
str = s.toString();
}
nextChar++;
if (c == '\r') {
skipLF = true;
}
return str;
}

if (s == null)
s = new StringBuffer(defaultExpectedLineLength);
s.append(cb, startChar, i - startChar);
}
}
}
我跟踪代码发现会进入fill()方法:

/**
* Fills the input buffer, taking the mark into account if it is valid.
*/
private void fill() throws IOException {
int dst;
if (markedChar <= UNMARKED) {
/* No mark */
dst = 0;
} else {
/* Marked */
int delta = nextChar - markedChar;
if (delta >= readAheadLimit) {
/* Gone past read-ahead limit: Invalidate mark */
markedChar = INVALIDATED;
readAheadLimit = 0;
dst = 0;
} else {
if (readAheadLimit <= cb.length) {
/* Shuffle in the current buffer */
System.arraycopy(cb, markedChar, cb, 0, delta);
markedChar = 0;
dst = delta;
} else {
/* Reallocate buffer to accommodate read-ahead limit */
char ncb[] = new char[readAheadLimit];
System.arraycopy(cb, markedChar, ncb, 0, delta);
cb = ncb;
markedChar = 0;
dst = delta;
}
nextChar = nChars = delta;
}
}

int n;
do {
n = in.read(cb, dst, cb.length - dst);
} while (n == 0);
if (n > 0) {
nChars = dst + n;
nextChar = dst;
}
}


最终会执行到in.read(cb, dst, cb. length - dst)这步骤,当执行完这步之后文件中的数据将会存放到cb数组中来,拿in.read( .. )方法又做了什么呢

进去我们发现它是一个抽象方法:

/**
* Reads characters into a portion of an array.  This method will block
* until some input is available, an I/O error occurs, or the end of the
* stream is reached.
*
* @param      cbuf  Destination buffer
* @param      off   Offset at which to start storing characters
* @param      len   Maximum number of characters to read
*
* @return     The number of characters read, or -1 if the end of the
*             stream has been reached
*
* @exception  IOException  If an I/O error occurs
*/
abstract public int read(char cbuf[], int off, int len) throws IOException;


那java.io.Reader抽象类肯定有实现类来实现这个方法,经过查找发现在sun.nio.cs.StreamDecoder中:

/**
* Reads characters into a portion of an array.
*
* @param      cbuf     Destination buffer
* @param      offset   Offset at which to start storing characters
* @param      length   Maximum number of characters to read
*
* @return     The number of characters read, or -1 if the end of the
*             stream has been reached
*
* @exception  IOException  If an I/O error occurs
*/
public int read(char cbuf[], int offset, int length) throws IOException {
return sd.read(cbuf, offset, length);


public int read(char cbuf[], int offset, int length) throws IOException {
int off = offset;
int len = length;
synchronized (lock) {
ensureOpen();
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
}
if (len == 0)
return 0;

int n = 0;

if (haveLeftoverChar) {
// Copy the leftover char into the buffer
cbuf[off] = leftoverChar;
off++; len--;
haveLeftoverChar = false;
n = 1;
if ((len == 0) || !implReady())
// Return now if this is all we can produce w/o blocking
return n;
}

if (len == 1) {
// Treat single-character array reads just like read()
int c = read0();
if (c == -1)
return (n == 0) ? -1 : n;
cbuf[off] = (char)c;
return n + 1;
}

return n + implRead(cbuf, off, off + len);
}
}


int implRead(char[] cbuf, int off, int end) throws IOException {

// In order to handle surrogate pairs, this method requires that
// the invoker attempt to read at least two characters.  Saving the
// extra character, if any, at a higher level is easier than trying
// to deal with it here.
assert (end - off > 1);

CharBuffer cb = CharBuffer.wrap(cbuf, off, end - off);
if (cb.position() != 0)
// Ensure that cb[0] == cbuf[off]
cb = cb.slice();

boolean eof = false;
for (;;) {
CoderResult cr = decoder.decode(bb, cb, eof);
if (cr.isUnderflow()) {
if (eof)
break;
if (!cb.hasRemaining())
break;
if ((cb.position() > 0) && !inReady())
break;          // Block at most once
int n = readBytes();
if (n < 0) {
eof = true;
if ((cb.position() == 0) && (!bb.hasRemaining()))
break;
decoder.reset();
}
continue;
}
if (cr.isOverflow()) {
assert cb.position() > 0;
break;
}
cr.throwException();
}

if (eof) {
// ## Need to flush decoder
decoder.reset();
}

if (cb.position() == 0) {
if (eof)
return -1;
assert false;
}
return cb.position();
}


private int readBytes() throws IOException {
bb.compact();
try {
if (ch != null) {
// Read from the channel
int n = ch.read(bb);
if (n < 0)
return n;
} else {
// Read from the input stream, and then update the buffer
int lim = bb.limit();
int pos = bb.position();
assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0);
assert rem > 0;
int n = in.read(bb.array(), bb.arrayOffset() + pos, rem);
if (n < 0)
return n;
if (n == 0)
throw new IOException("Underlying input stream returned zero bytes");
assert (n <= rem) : "n = " + n + ", rem = " + rem;
bb.position(pos + n);
}
} finally {
// Flip even when an IOException is thrown,
// otherwise the stream will stutter
bb.flip();
}

int rem = bb.remaining();
assert (rem != 0) : rem;
return rem;
}


上面有一句in.read(bb.array(), bb.arrayOffset() + pos, rem); 这句是直接调用java.io.FileInputStream类型中的方法

/**
* Reads up to <code>len</code> bytes of data from this input stream
* into an array of bytes. If <code>len</code> is not zero, the method
* blocks until some input is available; otherwise, no
* bytes are read and <code>0</code> is returned.
*
* @param      b     the buffer into which the data is read.
* @param      off   the start offset in the destination array <code>b</code>
* @param      len   the maximum number of bytes read.
* @return     the total number of bytes read into the buffer, or
*             <code>-1</code> if there is no more data because the end of
*             the file has been reached.
* @exception  NullPointerException If <code>b</code> is <code>null</code>.
* @exception  IndexOutOfBoundsException If <code>off</code> is negative,
* <code>len</code> is negative, or <code>len</code> is greater than
* <code>b.length - off</code>
* @exception  IOException  if an I/O error occurs.
*/
public int read(byte b[], int off, int len) throws IOException {
return readBytes(b, off, len);
}


/**
* Reads a subarray as a sequence of bytes.
* @param b the data to be written
* @param off the start offset in the data
* @param len the number of bytes that are written
* @exception IOException If an I/O error has occurred.
*/
private native int readBytes(byte b[], int off, int len) throws IOException;


代码跟到这里我就跟不下去了,至于jvm是如何调用系统中的方法来读取磁盘文件的,我不知道,不过我在网上看到一篇这样的文章,对这个I/O有大概的一些了解,它的工作原理如下图:



我觉得这篇文章讲的很好,详细可参考:深入分析 Java I/O 的工作机制

还有一篇是讲解I/O底层的:Java I/O底层是如何工作的?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐